summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/adi,max98388.yaml79
-rw-r--r--Documentation/devicetree/bindings/sound/adi,ssm2518.yaml47
-rw-r--r--Documentation/devicetree/bindings/sound/adi,ssm3515.yaml49
-rw-r--r--Documentation/devicetree/bindings/sound/audio-graph.yaml6
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml11
-rw-r--r--Documentation/devicetree/bindings/sound/da7219.txt112
-rw-r--r--Documentation/devicetree/bindings/sound/dialog,da7219.yaml237
-rw-r--r--Documentation/devicetree/bindings/sound/fsl-asoc-card.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/google,chv3-codec.yaml31
-rw-r--r--Documentation/devicetree/bindings/sound/google,chv3-i2s.yaml44
-rw-r--r--Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml3
-rw-r--r--Documentation/devicetree/bindings/sound/ingenic,aic.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml70
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml66
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml27
-rw-r--r--Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml8
-rw-r--r--Documentation/devicetree/bindings/sound/nau8315.txt24
-rw-r--r--Documentation/devicetree/bindings/sound/nau8540.txt16
-rw-r--r--Documentation/devicetree/bindings/sound/nau8810.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/nau8824.txt88
-rw-r--r--Documentation/devicetree/bindings/sound/nau8825.txt111
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8315.yaml44
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml45
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml182
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml239
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-clocks.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml66
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt1016.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/rt1016.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml3
-rw-r--r--Documentation/devicetree/bindings/sound/ssm2518.txt20
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml11
-rw-r--r--Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml98
-rw-r--r--Documentation/devicetree/bindings/sound/tas2562.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/tas2770.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/tas27xx.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas2781.yaml74
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320aic32x4.yaml101
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/tlv320aic32x4.txt42
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8903.yaml4
-rw-r--r--Documentation/sound/cards/audigy-mixer.rst38
-rw-r--r--Documentation/sound/cards/index.rst1
-rw-r--r--Documentation/sound/cards/pcmtest.rst120
-rw-r--r--Documentation/sound/cards/sb-live-mixer.rst2
-rw-r--r--Documentation/sound/designs/compress-offload.rst11
-rw-r--r--Documentation/sound/designs/index.rst1
-rw-r--r--Documentation/sound/designs/midi-2.0.rst378
-rw-r--r--MAINTAINERS15
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c8
-rw-r--r--include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h8
-rw-r--r--include/linux/pci_ids.h3
-rw-r--r--include/linux/usb/midi-v2.h94
-rw-r--r--include/sound/asequencer.h4
-rw-r--r--include/sound/core.h4
-rw-r--r--include/sound/cs35l56.h1
-rw-r--r--include/sound/da7219-aad.h6
-rw-r--r--include/sound/emu10k1.h289
-rw-r--r--include/sound/emux_synth.h2
-rw-r--r--include/sound/hdaudio.h2
-rw-r--r--include/sound/rawmidi.h15
-rw-r--r--include/sound/seq_device.h1
-rw-r--r--include/sound/seq_kernel.h10
-rw-r--r--include/sound/simple_card_utils.h7
-rw-r--r--include/sound/soc-acpi-intel-match.h2
-rw-r--r--include/sound/soc-component.h15
-rw-r--r--include/sound/soc.h40
-rw-r--r--include/sound/tas2781-dsp.h183
-rw-r--r--include/sound/tas2781-tlv.h21
-rw-r--r--include/sound/tas2781.h164
-rw-r--r--include/sound/ump.h268
-rw-r--r--include/sound/ump_convert.h46
-rw-r--r--include/sound/ump_msg.h765
-rw-r--r--include/uapi/sound/asequencer.h86
-rw-r--r--include/uapi/sound/asound.h81
-rw-r--r--include/uapi/sound/emu10k1.h8
-rw-r--r--sound/aoa/codecs/onyx.c2
-rw-r--r--sound/aoa/codecs/tas.c2
-rw-r--r--sound/core/Kconfig13
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/compress_offload.c5
-rw-r--r--sound/core/control.c12
-rw-r--r--sound/core/control_compat.c14
-rw-r--r--sound/core/control_led.c2
-rw-r--r--sound/core/init.c4
-rw-r--r--sound/core/pcm_drm_eld.c73
-rw-r--r--sound/core/pcm_native.c4
-rw-r--r--sound/core/rawmidi.c248
-rw-r--r--sound/core/rawmidi_compat.c4
-rw-r--r--sound/core/seq/Kconfig14
-rw-r--r--sound/core/seq/Makefile3
-rw-r--r--sound/core/seq/seq_clientmgr.c557
-rw-r--r--sound/core/seq/seq_clientmgr.h27
-rw-r--r--sound/core/seq/seq_compat.c3
-rw-r--r--sound/core/seq/seq_dummy.c9
-rw-r--r--sound/core/seq/seq_memory.c98
-rw-r--r--sound/core/seq/seq_memory.h19
-rw-r--r--sound/core/seq/seq_midi.c12
-rw-r--r--sound/core/seq/seq_ports.c47
-rw-r--r--sound/core/seq/seq_ports.h23
-rw-r--r--sound/core/seq/seq_system.c1
-rw-r--r--sound/core/seq/seq_ump_client.c541
-rw-r--r--sound/core/seq/seq_ump_convert.c1206
-rw-r--r--sound/core/seq/seq_ump_convert.h22
-rw-r--r--sound/core/seq/seq_virmidi.c1
-rw-r--r--sound/core/timer.c18
-rw-r--r--sound/core/ump.c1164
-rw-r--r--sound/core/ump_convert.c505
-rw-r--r--sound/drivers/Kconfig19
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/pcmtest.c727
-rw-r--r--sound/firewire/bebob/bebob.c2
-rw-r--r--sound/firewire/dice/dice.c2
-rw-r--r--sound/firewire/digi00x/digi00x.c2
-rw-r--r--sound/firewire/fireface/ff.c2
-rw-r--r--sound/firewire/fireworks/fireworks.c2
-rw-r--r--sound/firewire/isight.c2
-rw-r--r--sound/firewire/lib.c2
-rw-r--r--sound/firewire/motu/motu.c2
-rw-r--r--sound/firewire/oxfw/oxfw.c2
-rw-r--r--sound/firewire/tascam/tascam.c2
-rw-r--r--sound/hda/hdac_controller.c5
-rw-r--r--sound/hda/hdac_device.c1
-rw-r--r--sound/hda/hdac_regmap.c3
-rw-r--r--sound/hda/hdac_stream.c6
-rw-r--r--sound/isa/Kconfig1
-rw-r--r--sound/pci/Kconfig45
-rw-r--r--sound/pci/ac97/ac97_codec.c4
-rw-r--r--sound/pci/emu10k1/emu10k1.c12
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c252
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c461
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c1
-rw-r--r--sound/pci/emu10k1/emufx.c845
-rw-r--r--sound/pci/emu10k1/emumixer.c1594
-rw-r--r--sound/pci/emu10k1/emupcm.c1034
-rw-r--r--sound/pci/emu10k1/emuproc.c514
-rw-r--r--sound/pci/emu10k1/io.c354
-rw-r--r--sound/pci/emu10k1/irq.c36
-rw-r--r--sound/pci/emu10k1/memory.c4
-rw-r--r--sound/pci/emu10k1/timer.c26
-rw-r--r--sound/pci/emu10k1/voice.c136
-rw-r--r--sound/pci/hda/cs35l41_hda.c32
-rw-r--r--sound/pci/hda/cs35l41_hda_i2c.c2
-rw-r--r--sound/pci/hda/hda_bind.c1
-rw-r--r--sound/pci/hda/hda_intel.c20
-rw-r--r--sound/pci/hda/patch_hdmi.c1
-rw-r--r--sound/pci/hda/patch_realtek.c55
-rw-r--r--sound/pci/mixart/mixart.c8
-rw-r--r--sound/pci/mixart/mixart_core.h7
-rw-r--r--sound/pcmcia/Kconfig1
-rw-r--r--sound/ppc/keywest.c2
-rw-r--r--sound/soc/Kconfig3
-rw-r--r--sound/soc/Makefile3
-rw-r--r--sound/soc/amd/Kconfig5
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c20
-rw-r--r--sound/soc/amd/acp-es8336.c2
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c43
-rw-r--r--sound/soc/amd/acp/acp-pci.c1
-rw-r--r--sound/soc/amd/acp/acp-pdm.c2
-rw-r--r--sound/soc/amd/acp/acp-platform.c1
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c25
-rw-r--r--sound/soc/amd/acp/acp-renoir.c17
-rw-r--r--sound/soc/amd/ps/Makefile2
-rw-r--r--sound/soc/amd/ps/acp63.h183
-rw-r--r--sound/soc/amd/ps/pci-ps.c484
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c66
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c555
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c3
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c3
-rw-r--r--sound/soc/amd/vangogh/acp5x-pcm-dma.c4
-rw-r--r--sound/soc/amd/vangogh/acp5x.h2
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c7
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c7
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c3
-rw-r--r--sound/soc/atmel/atmel-classd.c8
-rw-r--r--sound/soc/atmel/atmel-pdmic.c8
-rw-r--r--sound/soc/atmel/mchp-pdmc.c2
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c1
-rw-r--r--sound/soc/codecs/Kconfig67
-rw-r--r--sound/soc/codecs/Makefile16
-rw-r--r--sound/soc/codecs/ad193x-i2c.c2
-rw-r--r--sound/soc/codecs/adau1372-i2c.c2
-rw-r--r--sound/soc/codecs/adau1373.c2
-rw-r--r--sound/soc/codecs/adau1701.c2
-rw-r--r--sound/soc/codecs/adau1761-i2c.c2
-rw-r--r--sound/soc/codecs/adau1781-i2c.c2
-rw-r--r--sound/soc/codecs/adau17x1.c13
-rw-r--r--sound/soc/codecs/adau1977-i2c.c2
-rw-r--r--sound/soc/codecs/adau7118-i2c.c2
-rw-r--r--sound/soc/codecs/adav803.c2
-rw-r--r--sound/soc/codecs/ak4118.c13
-rw-r--r--sound/soc/codecs/ak4375.c2
-rw-r--r--sound/soc/codecs/ak4458.c2
-rw-r--r--sound/soc/codecs/ak4535.c2
-rw-r--r--sound/soc/codecs/ak4613.c2
-rw-r--r--sound/soc/codecs/ak4641.c2
-rw-r--r--sound/soc/codecs/ak4642.c2
-rw-r--r--sound/soc/codecs/ak4671.c2
-rw-r--r--sound/soc/codecs/ak5558.c2
-rw-r--r--sound/soc/codecs/alc5623.c2
-rw-r--r--sound/soc/codecs/alc5632.c2
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c2
-rw-r--r--sound/soc/codecs/chv3-codec.c41
-rw-r--r--sound/soc/codecs/cs35l32.c4
-rw-r--r--sound/soc/codecs/cs35l33.c4
-rw-r--r--sound/soc/codecs/cs35l34.c4
-rw-r--r--sound/soc/codecs/cs35l35.c4
-rw-r--r--sound/soc/codecs/cs35l36.c2
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c2
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c6
-rw-r--r--sound/soc/codecs/cs35l45-spi.c4
-rw-r--r--sound/soc/codecs/cs35l45-tables.c2
-rw-r--r--sound/soc/codecs/cs35l45.c4
-rw-r--r--sound/soc/codecs/cs35l45.h2
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c2
-rw-r--r--sound/soc/codecs/cs35l56.c65
-rw-r--r--sound/soc/codecs/cs4234.c4
-rw-r--r--sound/soc/codecs/cs4265.c2
-rw-r--r--sound/soc/codecs/cs4270.c2
-rw-r--r--sound/soc/codecs/cs4271-i2c.c2
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c2
-rw-r--r--sound/soc/codecs/cs42l42.c9
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c2
-rw-r--r--sound/soc/codecs/cs42l51.c9
-rw-r--r--sound/soc/codecs/cs42l52.c2
-rw-r--r--sound/soc/codecs/cs42l56.c2
-rw-r--r--sound/soc/codecs/cs42l73.c4
-rw-r--r--sound/soc/codecs/cs42l83-i2c.c4
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c2
-rw-r--r--sound/soc/codecs/cs43130.c4
-rw-r--r--sound/soc/codecs/cs4341.c2
-rw-r--r--sound/soc/codecs/cs4349.c2
-rw-r--r--sound/soc/codecs/cs53l30.c14
-rw-r--r--sound/soc/codecs/cx2072x.c2
-rw-r--r--sound/soc/codecs/da7210.c2
-rw-r--r--sound/soc/codecs/da7213.c2
-rw-r--r--sound/soc/codecs/da7218.c2
-rw-r--r--sound/soc/codecs/da7219-aad.c42
-rw-r--r--sound/soc/codecs/da7219.c2
-rw-r--r--sound/soc/codecs/da732x.c2
-rw-r--r--sound/soc/codecs/da9055.c2
-rw-r--r--sound/soc/codecs/es8316.c27
-rw-r--r--sound/soc/codecs/es8326.c2
-rw-r--r--sound/soc/codecs/es8328-i2c.c2
-rw-r--r--sound/soc/codecs/es8328.c2
-rw-r--r--sound/soc/codecs/hdmi-codec.c36
-rw-r--r--sound/soc/codecs/isabelle.c2
-rw-r--r--sound/soc/codecs/lm4857.c2
-rw-r--r--sound/soc/codecs/lm49453.c2
-rw-r--r--sound/soc/codecs/max9768.c2
-rw-r--r--sound/soc/codecs/max98088.c24
-rw-r--r--sound/soc/codecs/max98090.c56
-rw-r--r--sound/soc/codecs/max98090.h3
-rw-r--r--sound/soc/codecs/max98095.c2
-rw-r--r--sound/soc/codecs/max98363.c6
-rw-r--r--sound/soc/codecs/max98371.c2
-rw-r--r--sound/soc/codecs/max98373-i2c.c4
-rw-r--r--sound/soc/codecs/max98388.c1013
-rw-r--r--sound/soc/codecs/max98388.h234
-rw-r--r--sound/soc/codecs/max98390.c2
-rw-r--r--sound/soc/codecs/max98396.c2
-rw-r--r--sound/soc/codecs/max9850.c2
-rw-r--r--sound/soc/codecs/max98504.c2
-rw-r--r--sound/soc/codecs/max98520.c2
-rw-r--r--sound/soc/codecs/max9860.c2
-rw-r--r--sound/soc/codecs/max9867.c2
-rw-r--r--sound/soc/codecs/max9877.c2
-rw-r--r--sound/soc/codecs/max98925.c2
-rw-r--r--sound/soc/codecs/max98926.c2
-rw-r--r--sound/soc/codecs/max98927.c2
-rw-r--r--sound/soc/codecs/ml26124.c2
-rw-r--r--sound/soc/codecs/mt6359.c152
-rw-r--r--sound/soc/codecs/mt6660.c2
-rw-r--r--sound/soc/codecs/nau8540.c2
-rw-r--r--sound/soc/codecs/nau8810.c2
-rw-r--r--sound/soc/codecs/nau8821.c2
-rw-r--r--sound/soc/codecs/nau8822.c2
-rw-r--r--sound/soc/codecs/nau8824.c2
-rw-r--r--sound/soc/codecs/nau8825.c150
-rw-r--r--sound/soc/codecs/nau8825.h11
-rw-r--r--sound/soc/codecs/pcm1681.c2
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c2
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c2
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c2
-rw-r--r--sound/soc/codecs/pcm186x.c1
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c2
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c2
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c2
-rw-r--r--sound/soc/codecs/rk3328_codec.c1
-rw-r--r--sound/soc/codecs/rt1011.c4
-rw-r--r--sound/soc/codecs/rt1015.c2
-rw-r--r--sound/soc/codecs/rt1016.c2
-rw-r--r--sound/soc/codecs/rt1019.c4
-rw-r--r--sound/soc/codecs/rt1305.c4
-rw-r--r--sound/soc/codecs/rt1308-sdw.c7
-rw-r--r--sound/soc/codecs/rt1308-sdw.h1
-rw-r--r--sound/soc/codecs/rt1308.c4
-rw-r--r--sound/soc/codecs/rt1316-sdw.c7
-rw-r--r--sound/soc/codecs/rt1316-sdw.h1
-rw-r--r--sound/soc/codecs/rt1318-sdw.c7
-rw-r--r--sound/soc/codecs/rt1318-sdw.h1
-rw-r--r--sound/soc/codecs/rt274.c2
-rw-r--r--sound/soc/codecs/rt286.c2
-rw-r--r--sound/soc/codecs/rt298.c2
-rw-r--r--sound/soc/codecs/rt5514.c4
-rw-r--r--sound/soc/codecs/rt5616.c4
-rw-r--r--sound/soc/codecs/rt5631.c4
-rw-r--r--sound/soc/codecs/rt5640.c4
-rw-r--r--sound/soc/codecs/rt5645.c6
-rw-r--r--sound/soc/codecs/rt5651.c4
-rw-r--r--sound/soc/codecs/rt5659.c12
-rw-r--r--sound/soc/codecs/rt5660.c4
-rw-r--r--sound/soc/codecs/rt5663.c6
-rw-r--r--sound/soc/codecs/rt5665.c4
-rw-r--r--sound/soc/codecs/rt5668.c4
-rw-r--r--sound/soc/codecs/rt5670.c4
-rw-r--r--sound/soc/codecs/rt5677.c4
-rw-r--r--sound/soc/codecs/rt5682-i2c.c5
-rw-r--r--sound/soc/codecs/rt5682-sdw.c7
-rw-r--r--sound/soc/codecs/rt5682.h1
-rw-r--r--sound/soc/codecs/rt5682s.c16
-rw-r--r--sound/soc/codecs/rt700-sdw.c7
-rw-r--r--sound/soc/codecs/rt700.h1
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c9
-rw-r--r--sound/soc/codecs/rt711-sdca.h1
-rw-r--r--sound/soc/codecs/rt711-sdw.c7
-rw-r--r--sound/soc/codecs/rt711.h1
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c9
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h1
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c9
-rw-r--r--sound/soc/codecs/rt712-sdca.h1
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c9
-rw-r--r--sound/soc/codecs/rt715-sdca.h1
-rw-r--r--sound/soc/codecs/rt715-sdw.c6
-rw-r--r--sound/soc/codecs/rt715.h1
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c507
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h124
-rw-r--r--sound/soc/codecs/rt722-sdca.c1555
-rw-r--r--sound/soc/codecs/rt722-sdca.h237
-rw-r--r--sound/soc/codecs/rt9120.c2
-rw-r--r--sound/soc/codecs/sgtl5000.c2
-rw-r--r--sound/soc/codecs/sma1303.c2
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c2
-rw-r--r--sound/soc/codecs/ssm2518.c2
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c2
-rw-r--r--sound/soc/codecs/ssm3515.c448
-rw-r--r--sound/soc/codecs/ssm4567.c2
-rw-r--r--sound/soc/codecs/sta32x.c2
-rw-r--r--sound/soc/codecs/sta350.c2
-rw-r--r--sound/soc/codecs/sta529.c2
-rw-r--r--sound/soc/codecs/tas2552.c2
-rw-r--r--sound/soc/codecs/tas2562.c3
-rw-r--r--sound/soc/codecs/tas2764.c2
-rw-r--r--sound/soc/codecs/tas2770.c2
-rw-r--r--sound/soc/codecs/tas2780.c2
-rw-r--r--sound/soc/codecs/tas2781-comlib.c534
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c2428
-rw-r--r--sound/soc/codecs/tas2781-i2c.c763
-rw-r--r--sound/soc/codecs/tas5086.c2
-rw-r--r--sound/soc/codecs/tas571x.c2
-rw-r--r--sound/soc/codecs/tas5720.c3
-rw-r--r--sound/soc/codecs/tas5805m.c2
-rw-r--r--sound/soc/codecs/tas6424.c3
-rw-r--r--sound/soc/codecs/tda7419.c2
-rw-r--r--sound/soc/codecs/tfa9879.c2
-rw-r--r--sound/soc/codecs/tfa989x.c2
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c2
-rw-r--r--sound/soc/codecs/tlv320adcx140.c2
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c2
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c2
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c2
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c2
-rw-r--r--sound/soc/codecs/tlv320dac33.c2
-rw-r--r--sound/soc/codecs/tpa6130a2.c2
-rw-r--r--sound/soc/codecs/ts3a227e.c2
-rw-r--r--sound/soc/codecs/tscs42xx.c2
-rw-r--r--sound/soc/codecs/tscs454.c2
-rw-r--r--sound/soc/codecs/uda1380.c2
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c2
-rw-r--r--sound/soc/codecs/wm0010.c3
-rw-r--r--sound/soc/codecs/wm1250-ev1.c2
-rw-r--r--sound/soc/codecs/wm2000.c2
-rw-r--r--sound/soc/codecs/wm2200.c2
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8510.c2
-rw-r--r--sound/soc/codecs/wm8523.c2
-rw-r--r--sound/soc/codecs/wm8580.c2
-rw-r--r--sound/soc/codecs/wm8711.c2
-rw-r--r--sound/soc/codecs/wm8728.c2
-rw-r--r--sound/soc/codecs/wm8731-i2c.c2
-rw-r--r--sound/soc/codecs/wm8737.c2
-rw-r--r--sound/soc/codecs/wm8741.c2
-rw-r--r--sound/soc/codecs/wm8750.c2
-rw-r--r--sound/soc/codecs/wm8753.c2
-rw-r--r--sound/soc/codecs/wm8776.c2
-rw-r--r--sound/soc/codecs/wm8804-i2c.c2
-rw-r--r--sound/soc/codecs/wm8900.c2
-rw-r--r--sound/soc/codecs/wm8903.c2
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8940.c2
-rw-r--r--sound/soc/codecs/wm8955.c2
-rw-r--r--sound/soc/codecs/wm8960.c2
-rw-r--r--sound/soc/codecs/wm8961.c2
-rw-r--r--sound/soc/codecs/wm8962.c2
-rw-r--r--sound/soc/codecs/wm8971.c2
-rw-r--r--sound/soc/codecs/wm8974.c2
-rw-r--r--sound/soc/codecs/wm8978.c2
-rw-r--r--sound/soc/codecs/wm8983.c2
-rw-r--r--sound/soc/codecs/wm8985.c2
-rw-r--r--sound/soc/codecs/wm8988.c2
-rw-r--r--sound/soc/codecs/wm8990.c2
-rw-r--r--sound/soc/codecs/wm8991.c2
-rw-r--r--sound/soc/codecs/wm8993.c2
-rw-r--r--sound/soc/codecs/wm8995.c2
-rw-r--r--sound/soc/codecs/wm8996.c2
-rw-r--r--sound/soc/codecs/wm9081.c2
-rw-r--r--sound/soc/codecs/wm9090.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c21
-rw-r--r--sound/soc/codecs/wsa883x.c3
-rw-r--r--sound/soc/codecs/wsa884x.c1936
-rw-r--r--sound/soc/dwc/dwc-i2s.c78
-rw-r--r--sound/soc/dwc/local.h7
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c22
-rw-r--r--sound/soc/fsl/fsl_sai.c24
-rw-r--r--sound/soc/fsl/fsl_sai.h2
-rw-r--r--sound/soc/fsl/imx-audmix.c24
-rw-r--r--sound/soc/fsl/imx-card.c25
-rw-r--r--sound/soc/fsl/imx-rpmsg.c6
-rw-r--r--sound/soc/fsl/imx-spdif.c8
-rw-r--r--sound/soc/generic/audio-graph-card.c107
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.dtsi165
-rw-r--r--sound/soc/generic/audio-graph-card2.c107
-rw-r--r--sound/soc/generic/simple-card-utils.c118
-rw-r--r--sound/soc/generic/simple-card.c4
-rw-r--r--sound/soc/google/Kconfig6
-rw-r--r--sound/soc/google/Makefile2
-rw-r--r--sound/soc/google/chv3-i2s.c338
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c1
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c1
-rw-r--r--sound/soc/intel/atom/sst/sst_loader.c1
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c1
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c1
-rw-r--r--sound/soc/intel/avs/boards/da7219.c45
-rw-r--r--sound/soc/intel/avs/boards/dmic.c2
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c65
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c6
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c39
-rw-r--r--sound/soc/intel/avs/boards/max98373.c45
-rw-r--r--sound/soc/intel/avs/boards/max98927.c45
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c45
-rw-r--r--sound/soc/intel/avs/boards/rt274.c45
-rw-r--r--sound/soc/intel/avs/boards/rt286.c45
-rw-r--r--sound/soc/intel/avs/boards/rt298.c45
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c45
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c57
-rw-r--r--sound/soc/intel/boards/Kconfig4
-rw-r--r--sound/soc/intel/boards/Makefile10
-rw-r--r--sound/soc/intel/boards/ehl_rt5660.c8
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c8
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c11
-rw-r--r--sound/soc/intel/boards/sof_es8336.c11
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c21
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c3
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c94
-rw-r--r--sound/soc/intel/boards/sof_sdw.c669
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h86
-rw-r--r--sound/soc/intel/boards/sof_sdw_cs42l42.c131
-rw-r--r--sound/soc/intel/boards/sof_sdw_maxim.c (renamed from sound/soc/intel/boards/sof_sdw_max98373.c)59
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt711.c4
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt712_sdca.c102
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c (renamed from sound/soc/intel/boards/sof_sdw_rt711_sdca.c)73
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c29
-rw-r--r--sound/soc/intel/common/Makefile1
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c46
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c72
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c167
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c54
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c53
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c9
-rw-r--r--sound/soc/loongson/Kconfig27
-rw-r--r--sound/soc/loongson/Makefile8
-rw-r--r--sound/soc/loongson/loongson_card.c218
-rw-r--r--sound/soc/loongson/loongson_dma.c350
-rw-r--r--sound/soc/loongson/loongson_dma.h16
-rw-r--r--sound/soc/loongson/loongson_i2s.c269
-rw-r--r--sound/soc/loongson/loongson_i2s.h71
-rw-r--r--sound/soc/loongson/loongson_i2s_pci.c171
-rw-r--r--sound/soc/mediatek/Kconfig5
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c53
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c13
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c2
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-control.c1
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c1
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c1
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c142
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.h15
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-common.h3
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c113
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c110
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c922
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c453
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-reg.h2
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-control.c2
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c99
-rw-r--r--sound/soc/meson/axg-card.c11
-rw-r--r--sound/soc/meson/gx-card.c3
-rw-r--r--sound/soc/meson/meson-card-utils.c26
-rw-r--r--sound/soc/meson/meson-card.h3
-rw-r--r--sound/soc/qcom/common.c34
-rw-r--r--sound/soc/qcom/lpass-sc7180.c2
-rw-r--r--sound/soc/qcom/lpass-sc7280.c2
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c325
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h63
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c34
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c445
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c39
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c68
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h6
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.c35
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.h1
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c43
-rw-r--r--sound/soc/qcom/sc7280.c23
-rw-r--r--sound/soc/samsung/odroid.c16
-rw-r--r--sound/soc/sh/siu_dai.c2
-rw-r--r--sound/soc/soc-component.c22
-rw-r--r--sound/soc/soc-compress.c1
-rw-r--r--sound/soc/soc-core.c112
-rw-r--r--sound/soc/soc-dapm.c75
-rw-r--r--sound/soc/soc-pcm.c209
-rw-r--r--sound/soc/soc-topology.c91
-rw-r--r--sound/soc/soc-utils.c7
-rw-r--r--sound/soc/sof/Kconfig11
-rw-r--r--sound/soc/sof/Makefile2
-rw-r--r--sound/soc/sof/amd/acp-ipc.c7
-rw-r--r--sound/soc/sof/amd/acp.h3
-rw-r--r--sound/soc/sof/amd/pci-rmb.c3
-rw-r--r--sound/soc/sof/amd/pci-rn.c3
-rw-r--r--sound/soc/sof/core.c4
-rw-r--r--sound/soc/sof/intel/Kconfig3
-rw-r--r--sound/soc/sof/intel/hda-bus.c11
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c137
-rw-r--r--sound/soc/sof/intel/hda-dai.c168
-rw-r--r--sound/soc/sof/intel/hda-loader-skl.c1
-rw-r--r--sound/soc/sof/intel/hda-pcm.c2
-rw-r--r--sound/soc/sof/intel/hda-stream.c1
-rw-r--r--sound/soc/sof/intel/hda.c43
-rw-r--r--sound/soc/sof/intel/hda.h25
-rw-r--r--sound/soc/sof/intel/mtl.c64
-rw-r--r--sound/soc/sof/intel/mtl.h7
-rw-r--r--sound/soc/sof/intel/skl.c1
-rw-r--r--sound/soc/sof/intel/tgl.c10
-rw-r--r--sound/soc/sof/ipc3-control.c54
-rw-r--r--sound/soc/sof/ipc3-priv.h2
-rw-r--r--sound/soc/sof/ipc3.c102
-rw-r--r--sound/soc/sof/ipc4-control.c39
-rw-r--r--sound/soc/sof/ipc4-loader.c72
-rw-r--r--sound/soc/sof/ipc4-pcm.c4
-rw-r--r--sound/soc/sof/ipc4-priv.h10
-rw-r--r--sound/soc/sof/ipc4-topology.c459
-rw-r--r--sound/soc/sof/ipc4-topology.h74
-rw-r--r--sound/soc/sof/ipc4.c44
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.c1
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c119
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h5
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.c1
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c36
-rw-r--r--sound/soc/sof/nocodec.c8
-rw-r--r--sound/soc/sof/pcm.c2
-rw-r--r--sound/soc/sof/pm.c12
-rw-r--r--sound/soc/sof/sof-audio.c47
-rw-r--r--sound/soc/sof/sof-audio.h1
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c162
-rw-r--r--sound/soc/sof/sof-client.c52
-rw-r--r--sound/soc/sof/sof-client.h1
-rw-r--r--sound/soc/sof/sof-priv.h3
-rw-r--r--sound/soc/sof/topology.c2
-rw-r--r--sound/soc/starfive/Kconfig15
-rw-r--r--sound/soc/starfive/Makefile2
-rw-r--r--sound/soc/starfive/jh7110_tdm.c670
-rw-r--r--sound/soc/stm/stm32_sai_sub.c9
-rw-r--r--sound/soc/tegra/tegra186_asrc.c4
-rw-r--r--sound/soc/tegra/tegra20_ac97.c1
-rw-r--r--sound/soc/tegra/tegra20_i2s.c9
-rw-r--r--sound/soc/tegra/tegra20_spdif.c9
-rw-r--r--sound/soc/tegra/tegra210_adx.c2
-rw-r--r--sound/soc/tegra/tegra210_ahub.c10
-rw-r--r--sound/soc/ti/davinci-mcasp.c27
-rw-r--r--sound/soc/ti/omap-hdmi.c8
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c1
-rw-r--r--sound/sound_core.c23
-rw-r--r--sound/synth/emux/emux_synth.c3
-rw-r--r--sound/usb/Kconfig11
-rw-r--r--sound/usb/Makefile1
-rw-r--r--sound/usb/card.c12
-rw-r--r--sound/usb/midi.c7
-rw-r--r--sound/usb/midi.h5
-rw-r--r--sound/usb/midi2.c1230
-rw-r--r--sound/usb/midi2.h33
-rw-r--r--sound/usb/quirks.c8
-rw-r--r--sound/usb/usbaudio.h2
-rw-r--r--tools/testing/selftests/alsa/Makefile2
-rw-r--r--tools/testing/selftests/alsa/test-pcmtest-driver.c333
607 files changed, 33532 insertions, 7069 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,max98388.yaml b/Documentation/devicetree/bindings/sound/adi,max98388.yaml
new file mode 100644
index 000000000000..93ccd5905736
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,max98388.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,max98388.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX98388 Speaker Amplifier
+
+maintainers:
+ - Ryan Lee <ryans.lee@analog.com>
+
+description:
+ The MAX98388 is a mono Class-D speaker amplifier with I/V feedback.
+ The device provides a PCM interface for audio data and a standard
+ I2C interface for control data communication.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - adi,max98388
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ adi,vmon-slot-no:
+ description: slot number of the voltage feedback monitor
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+ default: 0
+
+ adi,imon-slot-no:
+ description: slot number of the current feedback monitor
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 15
+ default: 1
+
+ adi,interleave-mode:
+ description:
+ For cases where a single combined channel for the I/V feedback data
+ is not sufficient, the device can also be configured to share
+ a single data output channel on alternating frames.
+ In this configuration, the current and voltage data will be frame
+ interleaved on a single output channel.
+ type: boolean
+
+ reset-gpios:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ max98388: amplifier@39 {
+ compatible = "adi,max98388";
+ reg = <0x39>;
+ #sound-dai-cells = <0>;
+ adi,vmon-slot-no = <0>;
+ adi,imon-slot-no = <1>;
+ adi,interleave-mode;
+ reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/adi,ssm2518.yaml b/Documentation/devicetree/bindings/sound/adi,ssm2518.yaml
new file mode 100644
index 000000000000..f3f32540779c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,ssm2518.yaml
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,ssm2518.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices SSM2518 audio amplifier
+
+maintainers:
+ - Lars-Peter Clausen <lars@metafoo.de>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: adi,ssm2518
+
+ reg:
+ maxItems: 1
+ description: |
+ I2C address of the device. This will either be 0x34 (ADDR pin low)
+ or 0x35 (ADDR pin high)
+
+ gpios:
+ maxItems: 1
+ description: |
+ GPIO connected to the nSD pin. If the property is not present
+ it is assumed that the nSD pin is hardwired to always on.
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@34 {
+ compatible = "adi,ssm2518";
+ reg = <0x34>;
+ gpios = <&gpio 5 0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/adi,ssm3515.yaml b/Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
new file mode 100644
index 000000000000..144450df5869
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,ssm3515.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices SSM3515 Audio Amplifier
+
+maintainers:
+ - Martin Povišer <povik+lin@cutebit.org>
+
+description: |
+ SSM3515 is a mono Class-D audio amplifier with digital input.
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/SSM3515.pdf
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - adi,ssm3515
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@14 {
+ compatible = "adi,ssm3515";
+ reg = <0x14>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Tweeter";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/audio-graph.yaml b/Documentation/devicetree/bindings/sound/audio-graph.yaml
index c87eb91de159..ed31e04ff6a6 100644
--- a/Documentation/devicetree/bindings/sound/audio-graph.yaml
+++ b/Documentation/devicetree/bindings/sound/audio-graph.yaml
@@ -24,7 +24,11 @@ properties:
connection's sink, the second being the connection's source.
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
widgets:
- description: User specified audio sound widgets.
+ description: |
+ User specified audio sound widgets.
+ Each entry is a pair of strings, the first being the type of
+ widget ("Microphone", "Line", "Headphone", "Speaker"), the
+ second being the machine specific name for the widget.
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
convert-rate:
$ref: /schemas/sound/dai-params.yaml#/$defs/dai-sample-rate
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
index 2ab74f995685..4c9acb8d4c4c 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
@@ -62,7 +62,7 @@ patternProperties:
GPIO pin direction. Valid only when 'gpio-ctrl' is 1
0 = Output
1 = Input
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 1
default: 1
@@ -71,7 +71,7 @@ patternProperties:
GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0
0 = Low
1 = High
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 1
default: 0
@@ -80,7 +80,7 @@ patternProperties:
GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0
0 = CMOS
1 = Open Drain
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 1
default: 0
@@ -90,7 +90,7 @@ patternProperties:
and 'gpio-dir' is 0
0 = Non-inverted, Active High
1 = Inverted, Active Low
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 1
default: 0
@@ -114,7 +114,7 @@ patternProperties:
0 = High impedance input
1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
2-7 = Reserved
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 7
default: 0
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml
index 670b67ec0b61..f7bafbd4f1c2 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l51.yaml
@@ -44,6 +44,10 @@ properties:
VAHP-supply:
description: phandle to voltage regulator of headphone
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
required:
- compatible
- reg
@@ -69,6 +73,13 @@ examples:
VA-supply = <&reg_audio>;
VAHP-supply = <&reg_audio>;
reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>;
+
+ /* assume audio-graph */
+ port {
+ cpu_endpoint: endpoint {
+ remote-endpoint = <&cpu_endpoint>;
+ };
+ };
};
};
...
diff --git a/Documentation/devicetree/bindings/sound/da7219.txt b/Documentation/devicetree/bindings/sound/da7219.txt
deleted file mode 100644
index add1caf26ac2..000000000000
--- a/Documentation/devicetree/bindings/sound/da7219.txt
+++ /dev/null
@@ -1,112 +0,0 @@
-Dialog Semiconductor DA7219 Audio Codec bindings
-
-DA7219 is an audio codec with advanced accessory detect features.
-
-======
-
-Required properties:
-- compatible : Should be "dlg,da7219"
-- reg: Specifies the I2C slave address
-
-- interrupts : IRQ line info for DA7219.
- (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
- further information relating to interrupt properties)
-
-- VDD-supply: VDD power supply for the device
-- VDDMIC-supply: VDDMIC power supply for the device
-- VDDIO-supply: VDDIO power supply for the device
- (See Documentation/devicetree/bindings/regulator/regulator.txt for further
- information relating to regulators)
-
-Optional properties:
-- interrupt-names : Name associated with interrupt line. Should be "wakeup" if
- interrupt is to be used to wake system, otherwise "irq" should be used.
-- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
-
-- #clock-cells : Should be set to '<1>', two clock sources provided;
-- clock-output-names : Names given for DAI clock outputs (WCLK & BCLK);
-
-- clocks : phandle and clock specifier for codec MCLK.
-- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
-
-- dlg,micbias-lvl : Voltage (mV) for Mic Bias
- [<1600>, <1800>, <2000>, <2200>, <2400>, <2600>]
-- dlg,mic-amp-in-sel : Mic input source type
- ["diff", "se_p", "se_n"]
-
-Deprecated properties:
-- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine
- (LDO unavailable in production HW so property no longer required).
-
-======
-
-Child node - 'da7219_aad':
-
-Optional properties:
-- dlg,micbias-pulse-lvl : Mic bias higher voltage pulse level (mV).
- [<2800>, <2900>]
-- dlg,micbias-pulse-time : Mic bias higher voltage pulse duration (ms)
-- dlg,btn-cfg : Periodic button press measurements for 4-pole jack (ms)
- [<2>, <5>, <10>, <50>, <100>, <200>, <500>]
-- dlg,mic-det-thr : Impedance threshold for mic detection measurement (Ohms)
- [<200>, <500>, <750>, <1000>]
-- dlg,jack-ins-deb : Debounce time for jack insertion (ms)
- [<5>, <10>, <20>, <50>, <100>, <200>, <500>, <1000>]
-- dlg,jack-det-rate: Jack type detection latency (3/4 pole)
- ["32ms_64ms", "64ms_128ms", "128ms_256ms", "256ms_512ms"]
-- dlg,jack-rem-deb : Debounce time for jack removal (ms)
- [<1>, <5>, <10>, <20>]
-- dlg,a-d-btn-thr : Impedance threshold between buttons A and D
- [0x0 - 0xFF]
-- dlg,d-b-btn-thr : Impedance threshold between buttons D and B
- [0x0 - 0xFF]
-- dlg,b-c-btn-thr : Impedance threshold between buttons B and C
- [0x0 - 0xFF]
-- dlg,c-mic-btn-thr : Impedance threshold between button C and Mic
- [0x0 - 0xFF]
-- dlg,btn-avg : Number of 8-bit readings for averaged button measurement
- [<1>, <2>, <4>, <8>]
-- dlg,adc-1bit-rpt : Repeat count for 1-bit button measurement
- [<1>, <2>, <4>, <8>]
-
-======
-
-Example:
-
- codec: da7219@1a {
- compatible = "dlg,da7219";
- reg = <0x1a>;
-
- interrupt-parent = <&gpio6>;
- interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
-
- VDD-supply = <&reg_audio>;
- VDDMIC-supply = <&reg_audio>;
- VDDIO-supply = <&reg_audio>;
-
- #clock-cells = <1>;
- clock-output-names = "dai-wclk", "dai-bclk";
-
- clocks = <&clks 201>;
- clock-names = "mclk";
-
- dlg,ldo-lvl = <1200>;
- dlg,micbias-lvl = <2600>;
- dlg,mic-amp-in-sel = "diff";
-
- da7219_aad {
- dlg,btn-cfg = <50>;
- dlg,mic-det-thr = <500>;
- dlg,jack-ins-deb = <20>;
- dlg,jack-det-rate = "32ms_64ms";
- dlg,jack-rem-deb = <1>;
-
- dlg,a-d-btn-thr = <0xa>;
- dlg,d-b-btn-thr = <0x16>;
- dlg,b-c-btn-thr = <0x21>;
- dlg,c-mic-btn-thr = <0x3E>;
-
- dlg,btn-avg = <4>;
- dlg,adc-1bit-rpt = <1>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/dialog,da7219.yaml b/Documentation/devicetree/bindings/sound/dialog,da7219.yaml
new file mode 100644
index 000000000000..bb5af48ab1e1
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/dialog,da7219.yaml
@@ -0,0 +1,237 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/dialog,da7219.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Dialog Semiconductor DA7219 Audio Codec
+
+maintainers:
+ - David Rau <David.Rau.opensource@dm.renesas.com>
+
+description:
+ The DA7219 is an ultra low-power audio codec with
+ in-built advanced accessory detection (AAD) for mobile
+ computing and accessory applications, which supports
+ sample rates up to 96 kHz at 24-bit resolution.
+
+properties:
+ compatible:
+ const: dlg,da7219
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ VDD-supply:
+ description:
+ VDD power supply for the device.
+
+ VDDMIC-supply:
+ description:
+ VDDMIC power supply for the device.
+
+ VDDIO-supply:
+ description:
+ VDDIO power supply for the device.
+
+ interrupt-names:
+ description:
+ Should be "wakeup" if interrupt is to be used to wake system,
+ otherwise "irq" should be used.
+ enum:
+ - wakeup
+ - irq
+
+ wakeup-source:
+ type: boolean
+ description:
+ Flag to indicate this device can wake system (suspend/resume).
+
+ "#clock-cells":
+ const: 1
+
+ clock-output-names:
+ minItems: 2
+ maxItems: 2
+ description:
+ Name given for DAI WCLK and BCLK outputs.
+
+ clocks:
+ maxItems: 1
+ description:
+ phandle and clock specifier for codec MCLK.
+
+ clock-names:
+ const: mclk
+
+ dlg,micbias-lvl:
+ enum: [1600, 1800, 2000, 2200, 2400, 2600]
+ description:
+ Voltage (mV) for Mic Bias.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,mic-amp-in-sel:
+ enum: ["diff", "se_p", "se_n"]
+ description:
+ Mic input source type.
+
+ diff - Differential.
+
+ se_p - MIC_P.
+ Positive differential analog microphone input.
+
+ se_n - MIC_N.
+ Negative differential analog microphone input.
+ $ref: /schemas/types.yaml#/definitions/string
+
+ da7219_aad:
+ type: object
+ description:
+ Configuration of advanced accessory detection.
+ properties:
+ dlg,micbias-pulse-lvl:
+ enum: [2800, 2900]
+ description:
+ Mic bias higher voltage pulse level (mV).
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,micbias-pulse-time:
+ description:
+ Mic bias higher voltage pulse duration (ms).
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+
+ dlg,btn-cfg:
+ enum: [2, 5, 10, 50, 100, 200, 500]
+ description:
+ Periodic button press measurements for 4-pole jack (ms).
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,mic-det-thr:
+ enum: [200, 500, 750, 1000]
+ description:
+ Impedance threshold for mic detection measurement (Ohms).
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,jack-ins-deb:
+ enum: [5, 10, 20, 50, 100, 200, 500, 1000]
+ description:
+ Debounce time for jack insertion (ms).
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,jack-ins-det-pty:
+ enum: ["low", "high"]
+ description:
+ Polarity for jack insertion detection.
+ $ref: /schemas/types.yaml#/definitions/string
+
+ dlg,jack-det-rate:
+ enum: ["32_64", "64_128", "128_256", "256_512"]
+ description:
+ Jack type (3/4 pole) detection latency (ms).
+ $ref: /schemas/types.yaml#/definitions/string
+
+ dlg,jack-rem-deb:
+ enum: [1, 5, 10, 20]
+ description:
+ Debounce time for jack removal (ms).
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,a-d-btn-thr:
+ description:
+ Impedance threshold between buttons A and D.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+
+ dlg,d-b-btn-thr:
+ description:
+ Impedance threshold between buttons D and B.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+
+ dlg,b-c-btn-thr:
+ description:
+ Impedance threshold between buttons B and C.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+
+ dlg,c-mic-btn-thr:
+ description:
+ Impedance threshold between button C and Mic.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 255
+
+ dlg,btn-avg:
+ enum: [1, 2, 4, 8]
+ description:
+ Number of 8-bit readings for averaged button measurement.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dlg,adc-1bit-rpt:
+ enum: [1, 2, 4, 8]
+ description:
+ Repeat count for 1-bit button measurement.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - VDD-supply
+ - VDDMIC-supply
+ - VDDIO-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec: da7219@1a {
+ compatible = "dlg,da7219";
+ reg = <0x1a>;
+
+ interrupt-parent = <&gpio6>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+
+ VDD-supply = <&vdd_reg>;
+ VDDMIC-supply = <&vddmic_reg>;
+ VDDIO-supply = <&vddio_reg>;
+
+ #clock-cells = <1>;
+ clock-output-names = "da7219-dai-wclk", "da7219-dai-bclk";
+
+ clocks = <&clks 201>;
+ clock-names = "mclk";
+
+ dlg,micbias-lvl = <2600>;
+ dlg,mic-amp-in-sel = "diff";
+
+ da7219_aad {
+ dlg,btn-cfg = <50>;
+ dlg,mic-det-thr = <500>;
+ dlg,jack-ins-deb = <20>;
+ dlg,jack-ins-det-pty = "low";
+ dlg,jack-det-rate = "32_64";
+ dlg,jack-rem-deb = <1>;
+
+ dlg,a-d-btn-thr = <0xa>;
+ dlg,d-b-btn-thr = <0x16>;
+ dlg,b-c-btn-thr = <0x21>;
+ dlg,c-mic-btn-thr = <0x3E>;
+
+ dlg,btn-avg = <4>;
+ dlg,adc-1bit-rpt = <1>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
index 8b4f4015cfe4..4e8dbc5abfd1 100644
--- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
+++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
@@ -46,6 +46,8 @@ The compatible list for this generic sound card currently:
"fsl,imx-audio-wm8958"
+ "fsl,imx-audio-nau8822"
+
Required properties:
- compatible : Contains one of entries in the compatible list.
diff --git a/Documentation/devicetree/bindings/sound/google,chv3-codec.yaml b/Documentation/devicetree/bindings/sound/google,chv3-codec.yaml
new file mode 100644
index 000000000000..5329dc140b1c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/google,chv3-codec.yaml
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/google,chv3-codec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Google Chameleon v3 audio codec
+
+maintainers:
+ - Paweł Anikiel <pan@semihalf.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: google,chv3-codec
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ audio-codec {
+ compatible = "google,chv3-codec";
+ };
diff --git a/Documentation/devicetree/bindings/sound/google,chv3-i2s.yaml b/Documentation/devicetree/bindings/sound/google,chv3-i2s.yaml
new file mode 100644
index 000000000000..3ce910f44d39
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/google,chv3-i2s.yaml
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/google,chv3-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Google Chameleon v3 I2S device
+
+maintainers:
+ - Paweł Anikiel <pan@semihalf.com>
+
+description: |
+ I2S device for the Google Chameleon v3. The device handles both RX
+ and TX using a producer/consumer ring buffer design.
+
+properties:
+ compatible:
+ const: google,chv3-i2s
+
+ reg:
+ items:
+ - description: core registers
+ - description: irq registers
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ i2s@c0060300 {
+ compatible = "google,chv3-i2s";
+ reg = <0xc0060300 0x100>,
+ <0xc0060f00 0x10>;
+ interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml b/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml
index 67ccddd44489..666a95ac22c8 100644
--- a/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml
+++ b/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml
@@ -74,7 +74,8 @@ patternProperties:
properties:
sound-dai:
- maxItems: 1
+ minItems: 1
+ maxItems: 4
required:
- link-name
diff --git a/Documentation/devicetree/bindings/sound/ingenic,aic.yaml b/Documentation/devicetree/bindings/sound/ingenic,aic.yaml
index c59a7cd9eaa9..d15c000f14e1 100644
--- a/Documentation/devicetree/bindings/sound/ingenic,aic.yaml
+++ b/Documentation/devicetree/bindings/sound/ingenic,aic.yaml
@@ -23,6 +23,7 @@ properties:
- ingenic,jz4760-i2s
- ingenic,jz4770-i2s
- ingenic,jz4780-i2s
+ - ingenic,x1000-i2s
- items:
- const: ingenic,jz4725b-i2s
- const: ingenic,jz4740-i2s
diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml
new file mode 100644
index 000000000000..61e8babed402
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/loongson,ls-audio-card.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson 7axxx/2kxxx ASoC audio sound card driver
+
+maintainers:
+ - Yingkun Meng <mengyingkun@loongson.cn>
+
+description:
+ The binding describes the sound card present in loongson
+ 7axxx/2kxxx platform. The sound card is an ASoC component
+ which uses Loongson I2S controller to transfer the audio data.
+
+properties:
+ compatible:
+ const: loongson,ls-audio-card
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: User specified audio sound card name
+
+ mclk-fs:
+ $ref: simple-card.yaml#/definitions/mclk-fs
+
+ cpu:
+ description: Holds subnode which indicates cpu dai.
+ type: object
+ additionalProperties: false
+ properties:
+ sound-dai:
+ maxItems: 1
+ required:
+ - sound-dai
+
+ codec:
+ description: Holds subnode which indicates codec dai.
+ type: object
+ additionalProperties: false
+ properties:
+ sound-dai:
+ maxItems: 1
+ required:
+ - sound-dai
+
+required:
+ - compatible
+ - model
+ - mclk-fs
+ - cpu
+ - codec
+
+additionalProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "loongson,ls-audio-card";
+ model = "loongson-audio";
+ mclk-fs = <512>;
+
+ cpu {
+ sound-dai = <&i2s>;
+ };
+ codec {
+ sound-dai = <&es8323>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml
index 82ccb32f08f2..e6cb711ece77 100644
--- a/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml
@@ -29,6 +29,10 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle
description: The phandle of the mediatek topckgen controller
+ mediatek,infracfg:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of the mediatek infracfg controller
+
power-domains:
maxItems: 1
@@ -52,6 +56,11 @@ properties:
- description: mux for i2si1_mck
- description: mux for i2si2_mck
- description: audio 26m clock
+ - description: audio pll1 divide 4
+ - description: audio pll2 divide 4
+ - description: clock divider for iec
+ - description: mux for a2sys clock
+ - description: mux for aud_iec
clock-names:
items:
@@ -63,16 +72,21 @@ properties:
- const: apll12_div2
- const: apll12_div3
- const: apll12_div9
- - const: a1sys_hp_sel
- - const: aud_intbus_sel
- - const: audio_h_sel
- - const: audio_local_bus_sel
- - const: dptx_m_sel
- - const: i2so1_m_sel
- - const: i2so2_m_sel
- - const: i2si1_m_sel
- - const: i2si2_m_sel
+ - const: top_a1sys_hp
+ - const: top_aud_intbus
+ - const: top_audio_h
+ - const: top_audio_local_bus
+ - const: top_dptx
+ - const: top_i2so1
+ - const: top_i2so2
+ - const: top_i2si1
+ - const: top_i2si2
- const: adsp_audio_26m
+ - const: apll1_d4
+ - const: apll2_d4
+ - const: apll12_div4
+ - const: top_a2sys
+ - const: top_aud_iec
mediatek,etdm-in1-cowork-source:
$ref: /schemas/types.yaml#/definitions/uint32
@@ -144,6 +158,7 @@ required:
- resets
- reset-names
- mediatek,topckgen
+ - mediatek,infracfg
- power-domains
- clocks
- clock-names
@@ -162,6 +177,7 @@ examples:
resets = <&watchdog 14>;
reset-names = "audiosys";
mediatek,topckgen = <&topckgen>;
+ mediatek,infracfg = <&infracfg_ao>;
power-domains = <&spm 13>; //MT8188_POWER_DOMAIN_AUDIO
mediatek,etdm-in2-cowork-source = <2>;
mediatek,etdm-out2-cowork-source = <0>;
@@ -184,7 +200,12 @@ examples:
<&topckgen 78>, //CLK_TOP_I2SO2
<&topckgen 79>, //CLK_TOP_I2SI1
<&topckgen 80>, //CLK_TOP_I2SI2
- <&adsp_audio26m 0>; //CLK_AUDIODSP_AUDIO26M
+ <&adsp_audio26m 0>, //CLK_AUDIODSP_AUDIO26M
+ <&topckgen 132>, //CLK_TOP_APLL1_D4
+ <&topckgen 133>, //CLK_TOP_APLL2_D4
+ <&topckgen 183>, //CLK_TOP_APLL12_CK_DIV4
+ <&topckgen 84>, //CLK_TOP_A2SYS
+ <&topckgen 82>; //CLK_TOP_AUD_IEC>;
clock-names = "clk26m",
"apll1",
"apll2",
@@ -193,16 +214,21 @@ examples:
"apll12_div2",
"apll12_div3",
"apll12_div9",
- "a1sys_hp_sel",
- "aud_intbus_sel",
- "audio_h_sel",
- "audio_local_bus_sel",
- "dptx_m_sel",
- "i2so1_m_sel",
- "i2so2_m_sel",
- "i2si1_m_sel",
- "i2si2_m_sel",
- "adsp_audio_26m";
+ "top_a1sys_hp",
+ "top_aud_intbus",
+ "top_audio_h",
+ "top_audio_local_bus",
+ "top_dptx",
+ "top_i2so1",
+ "top_i2so2",
+ "top_i2si1",
+ "top_i2si2",
+ "adsp_audio_26m",
+ "apll1_d4",
+ "apll2_d4",
+ "apll12_div4",
+ "top_a2sys",
+ "top_aud_iec";
};
...
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml
index 6640272b3f4f..05e532b5d50a 100644
--- a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml
@@ -11,7 +11,9 @@ maintainers:
properties:
compatible:
- const: mediatek,mt8188-mt6359-evb
+ enum:
+ - mediatek,mt8188-mt6359-evb
+ - mediatek,mt8188-nau8825
model:
$ref: /schemas/types.yaml#/definitions/string
@@ -42,7 +44,6 @@ patternProperties:
we are going to update parameters in this node.
items:
enum:
- - ADDA_BE
- DPTX_BE
- ETDM1_IN_BE
- ETDM2_IN_BE
@@ -62,11 +63,28 @@ patternProperties:
required:
- sound-dai
+ dai-format:
+ description: audio format.
+ items:
+ enum:
+ - i2s
+ - right_j
+ - left_j
+ - dsp_a
+ - dsp_b
+
+ mediatek,clk-provider:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: Indicates dai-link clock master.
+ items:
+ enum:
+ - cpu
+ - codec
+
additionalProperties: false
required:
- link-name
- - codec
additionalProperties: false
@@ -87,7 +105,8 @@ examples:
"AIN1", "Headset Mic";
dai-link-0 {
link-name = "ETDM3_OUT_BE";
-
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
codec {
sound-dai = <&hdmi0>;
};
diff --git a/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml
index 9b40268537cb..9aa65c975c4e 100644
--- a/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml
+++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml
@@ -56,13 +56,9 @@ properties:
items:
items:
- description: value for DS line
+ enum: [0, 1]
- description: value for sampling edge
- anyOf:
- - enum:
- - [0, 0]
- - [0, 1]
- - [1, 0]
- - [1, 1]
+ enum: [0, 1]
minItems: 1
maxItems: 4
uniqueItems: true
diff --git a/Documentation/devicetree/bindings/sound/nau8315.txt b/Documentation/devicetree/bindings/sound/nau8315.txt
deleted file mode 100644
index 1cd94517d45e..000000000000
--- a/Documentation/devicetree/bindings/sound/nau8315.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Nuvoton NAU8315 Mono Class-D Amplifier
-
-Required properties:
-- compatible : "nuvoton,nau8315"
- "nuvoton,nau8318"
-
-Optional properties:
-- enable-gpios : GPIO specifier for the chip's device enable input(EN) pin.
- If this option is not specified then driver does not manage
- the pin state (e.g. chip is always on).
-
-Example:
-
-#include <dt-bindings/gpio/gpio.h>
-
-nau8315 {
- compatible = "nuvoton,nau8315";
- enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
-};
-
-nau8318 {
- compatible = "nuvoton,nau8318";
- enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
-};
diff --git a/Documentation/devicetree/bindings/sound/nau8540.txt b/Documentation/devicetree/bindings/sound/nau8540.txt
deleted file mode 100644
index 307a76528320..000000000000
--- a/Documentation/devicetree/bindings/sound/nau8540.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-NAU85L40 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
- - compatible : "nuvoton,nau8540"
-
- - reg : the I2C address of the device.
-
-Example:
-
-codec: nau8540@1c {
- compatible = "nuvoton,nau8540";
- reg = <0x1c>;
-};
diff --git a/Documentation/devicetree/bindings/sound/nau8810.txt b/Documentation/devicetree/bindings/sound/nau8810.txt
deleted file mode 100644
index 7deaa452b200..000000000000
--- a/Documentation/devicetree/bindings/sound/nau8810.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-NAU8810/NAU8812/NAU8814 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
- - compatible : One of "nuvoton,nau8810" or "nuvoton,nau8812" or
- "nuvoton,nau8814"
-
- - reg : the I2C address of the device.
-
-Example:
-
-codec: nau8810@1a {
- compatible = "nuvoton,nau8810";
- reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/nau8824.txt b/Documentation/devicetree/bindings/sound/nau8824.txt
deleted file mode 100644
index e0058b97e49a..000000000000
--- a/Documentation/devicetree/bindings/sound/nau8824.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-Nuvoton NAU8824 audio codec
-
-This device supports I2C only.
-
-Required properties:
- - compatible : Must be "nuvoton,nau8824"
-
- - reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1).
-
-Optional properties:
- - nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low.
-
- - nuvoton,vref-impedance: VREF Impedance selection
- 0 - Open
- 1 - 25 kOhm
- 2 - 125 kOhm
- 3 - 2.5 kOhm
-
- - nuvoton,micbias-voltage: Micbias voltage level.
- 0 - VDDA
- 1 - VDDA
- 2 - VDDA * 1.1
- 3 - VDDA * 1.2
- 4 - VDDA * 1.3
- 5 - VDDA * 1.4
- 6 - VDDA * 1.53
- 7 - VDDA * 1.53
-
- - nuvoton,sar-threshold-num: Number of buttons supported
- - nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as
- SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R)
- where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance.
- Refer datasheet section 10.2 for more information about threshold calculation.
-
- - nuvoton,sar-hysteresis: Button impedance measurement hysteresis.
-
- - nuvoton,sar-voltage: Reference voltage for button impedance measurement.
- 0 - VDDA
- 1 - VDDA
- 2 - VDDA * 1.1
- 3 - VDDA * 1.2
- 4 - VDDA * 1.3
- 5 - VDDA * 1.4
- 6 - VDDA * 1.53
- 7 - VDDA * 1.53
-
- - nuvoton,sar-compare-time: SAR compare time
- 0 - 500 ns
- 1 - 1 us
- 2 - 2 us
- 3 - 4 us
-
- - nuvoton,sar-sampling-time: SAR sampling time
- 0 - 2 us
- 1 - 4 us
- 2 - 8 us
- 3 - 16 us
-
- - nuvoton,short-key-debounce: Button short key press debounce time.
- 0 - 30 ms
- 1 - 50 ms
- 2 - 100 ms
-
- - nuvoton,jack-eject-debounce: Jack ejection debounce time.
- 0 - 0 ms
- 1 - 1 ms
- 2 - 10 ms
-
-
-Example:
-
- headset: nau8824@1a {
- compatible = "nuvoton,nau8824";
- reg = <0x1a>;
- interrupt-parent = <&gpio>;
- interrupts = <TEGRA_GPIO(E, 6) IRQ_TYPE_LEVEL_LOW>;
- nuvoton,vref-impedance = <2>;
- nuvoton,micbias-voltage = <6>;
- // Setup 4 buttons impedance according to Android specification
- nuvoton,sar-threshold-num = <4>;
- nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>;
- nuvoton,sar-hysteresis = <0>;
- nuvoton,sar-voltage = <6>;
- nuvoton,sar-compare-time = <1>;
- nuvoton,sar-sampling-time = <1>;
- nuvoton,short-key-debounce = <0>;
- nuvoton,jack-eject-debounce = <1>;
- };
diff --git a/Documentation/devicetree/bindings/sound/nau8825.txt b/Documentation/devicetree/bindings/sound/nau8825.txt
deleted file mode 100644
index a9c34526f4cb..000000000000
--- a/Documentation/devicetree/bindings/sound/nau8825.txt
+++ /dev/null
@@ -1,111 +0,0 @@
-Nuvoton NAU8825 audio codec
-
-This device supports I2C only.
-
-Required properties:
- - compatible : Must be "nuvoton,nau8825"
-
- - reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1).
-
-Optional properties:
- - nuvoton,jkdet-enable: Enable jack detection via JKDET pin.
- - nuvoton,jkdet-pull-enable: Enable JKDET pin pull. If set - pin pull enabled,
- otherwise pin in high impedance state.
- - nuvoton,jkdet-pull-up: Pull-up JKDET pin. If set then JKDET pin is pull up, otherwise pull down.
- - nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low.
-
- - nuvoton,vref-impedance: VREF Impedance selection
- 0 - Open
- 1 - 25 kOhm
- 2 - 125 kOhm
- 3 - 2.5 kOhm
-
- - nuvoton,micbias-voltage: Micbias voltage level.
- 0 - VDDA
- 1 - VDDA
- 2 - VDDA * 1.1
- 3 - VDDA * 1.2
- 4 - VDDA * 1.3
- 5 - VDDA * 1.4
- 6 - VDDA * 1.53
- 7 - VDDA * 1.53
-
- - nuvoton,sar-threshold-num: Number of buttons supported
- - nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as
- SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R)
- where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance.
- Refer datasheet section 10.2 for more information about threshold calculation.
-
- - nuvoton,sar-hysteresis: Button impedance measurement hysteresis.
-
- - nuvoton,sar-voltage: Reference voltage for button impedance measurement.
- 0 - VDDA
- 1 - VDDA
- 2 - VDDA * 1.1
- 3 - VDDA * 1.2
- 4 - VDDA * 1.3
- 5 - VDDA * 1.4
- 6 - VDDA * 1.53
- 7 - VDDA * 1.53
-
- - nuvoton,sar-compare-time: SAR compare time
- 0 - 500 ns
- 1 - 1 us
- 2 - 2 us
- 3 - 4 us
-
- - nuvoton,sar-sampling-time: SAR sampling time
- 0 - 2 us
- 1 - 4 us
- 2 - 8 us
- 3 - 16 us
-
- - nuvoton,short-key-debounce: Button short key press debounce time.
- 0 - 30 ms
- 1 - 50 ms
- 2 - 100 ms
- 3 - 30 ms
-
- - nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
- - nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
-
- - nuvoton,crosstalk-enable: make crosstalk function enable if set.
-
- - nuvoton,adcout-drive-strong: make the drive strength of ADCOUT IO PIN strong if set.
- Otherwise, the drive keeps normal strength.
-
- - nuvoton,adc-delay-ms: Delay (in ms) to make input path stable and avoid pop noise. The
- default value is 125 and range between 125 to 500 ms.
-
- - clocks: list of phandle and clock specifier pairs according to common clock bindings for the
- clocks described in clock-names
- - clock-names: should include "mclk" for the MCLK master clock
-
-Example:
-
- headset: nau8825@1a {
- compatible = "nuvoton,nau8825";
- reg = <0x1a>;
- interrupt-parent = <&gpio>;
- interrupts = <TEGRA_GPIO(E, 6) IRQ_TYPE_LEVEL_LOW>;
- nuvoton,jkdet-enable;
- nuvoton,jkdet-pull-enable;
- nuvoton,jkdet-pull-up;
- nuvoton,jkdet-polarity = <GPIO_ACTIVE_LOW>;
- nuvoton,vref-impedance = <2>;
- nuvoton,micbias-voltage = <6>;
- // Setup 4 buttons impedance according to Android specification
- nuvoton,sar-threshold-num = <4>;
- nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>;
- nuvoton,sar-hysteresis = <1>;
- nuvoton,sar-voltage = <0>;
- nuvoton,sar-compare-time = <0>;
- nuvoton,sar-sampling-time = <0>;
- nuvoton,short-key-debounce = <2>;
- nuvoton,jack-insert-debounce = <7>;
- nuvoton,jack-eject-debounce = <7>;
- nuvoton,crosstalk-enable;
-
- clock-names = "mclk";
- clocks = <&tegra_pmc TEGRA_PMC_CLK_OUT_2>;
- };
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8315.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8315.yaml
new file mode 100644
index 000000000000..24006e9dc501
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8315.yaml
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8315.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NAU8315/NAU8318 Mono Class-D Amplifier
+
+maintainers:
+ - David Lin <CTLIN0@nuvoton.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nau8315
+ - nuvoton,nau8318
+
+ '#sound-dai-cells':
+ const: 0
+
+ enable-gpios:
+ maxItems: 1
+ description:
+ GPIO specifier for the chip's device enable input(EN) pin.
+ If this option is not specified then driver does not manage
+ the pin state (e.g. chip is always on).
+
+required:
+ - compatible
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ codec {
+ compatible = "nuvoton,nau8315";
+ #sound-dai-cells = <0>;
+ enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml
new file mode 100644
index 000000000000..7ccfbb8d8b04
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8540.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton Technology Corporation NAU85L40 Audio CODEC
+
+maintainers:
+ - John Hsu <KCHSU0@nuvoton.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: nuvoton,nau8540
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1c {
+ compatible = "nuvoton,nau8540";
+ reg = <0x1c>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml
new file mode 100644
index 000000000000..d9696f6c75ed
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8810.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NAU8810/NAU8812/NAU8814 audio CODEC
+
+maintainers:
+ - David Lin <CTLIN0@nuvoton.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nau8810
+ - nuvoton,nau8812
+ - nuvoton,nau8814
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@1a {
+ #sound-dai-cells = <0>;
+ compatible = "nuvoton,nau8810";
+ reg = <0x1a>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml
new file mode 100644
index 000000000000..3dbf438c3841
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml
@@ -0,0 +1,182 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8824.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NAU8824 audio CODEC
+
+maintainers:
+ - John Hsu <KCHSU0@nuvoton.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nau8824
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ interrupts:
+ maxItems: 1
+
+ nuvoton,jkdet-polarity:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ JKDET pin polarity.
+ enum:
+ - 0 # active high
+ - 1 # active low
+ default: 1
+
+ nuvoton,vref-impedance:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ VREF Impedance selection.
+ enum:
+ - 0 # Open
+ - 1 # 25 kOhm
+ - 2 # 125 kOhm
+ - 3 # 2.5 kOhm
+ default: 2
+
+ nuvoton,micbias-voltage:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Micbias voltage level.
+ enum:
+ - 0 # VDDA
+ - 1 # VDDA
+ - 2 # VDDA * 1.1
+ - 3 # VDDA * 1.2
+ - 4 # VDDA * 1.3
+ - 5 # VDDA * 1.4
+ - 6 # VDDA * 1.53
+ - 7 # VDDA * 1.53
+ default: 6
+
+ nuvoton,sar-threshold-num:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Number of buttons supported.
+ minimum: 1
+ maximum: 8
+ default: 4
+
+ nuvoton,sar-threshold:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description:
+ Impedance threshold for each button. Array that contains up to 8 buttons
+ configuration. SAR value is calculated as
+ SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R) where MICBIAS is
+ configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by
+ 'nuvoton,sar-voltage', R - button impedance.
+ Refer datasheet section 10.2 for more information about threshold
+ calculation.
+ minItems: 1
+ maxItems: 8
+ items:
+ minimum: 0
+ maximum: 255
+
+ nuvoton,sar-hysteresis:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Button impedance measurement hysteresis.
+ default: 0
+
+ nuvoton,sar-voltage:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Reference voltage for button impedance measurement.
+ enum:
+ - 0 # VDDA
+ - 1 # VDDA
+ - 2 # VDDA * 1.1
+ - 3 # VDDA * 1.2
+ - 4 # VDDA * 1.3
+ - 5 # VDDA * 1.4
+ - 6 # VDDA * 1.53
+ - 7 # VDDA * 1.53
+ default: 6
+
+ nuvoton,sar-compare-time:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ SAR compare time.
+ enum:
+ - 0 # 500ns
+ - 1 # 1us
+ - 2 # 2us
+ - 3 # 4us
+ default: 1
+
+ nuvoton,sar-sampling-time:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ SAR sampling time.
+ enum:
+ - 0 # 2us
+ - 1 # 4us
+ - 2 # 8us
+ - 3 # 16us
+ default: 1
+
+ nuvoton,short-key-debounce:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Button short key press debounce time.
+ enum:
+ - 0 # 30 ms
+ - 1 # 50 ms
+ - 2 # 100 ms
+ default: 0
+
+ nuvoton,jack-eject-debounce:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Jack ejection debounce time.
+ enum:
+ - 0 # 0 ms
+ - 1 # 1 ms
+ - 2 # 10 ms
+ default: 1
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1a {
+ #sound-dai-cells = <0>;
+ compatible = "nuvoton,nau8824";
+ reg = <0x1a>;
+ interrupt-parent = <&gpio>;
+ interrupts = <38 IRQ_TYPE_LEVEL_LOW>;
+ nuvoton,vref-impedance = <2>;
+ nuvoton,micbias-voltage = <6>;
+ nuvoton,sar-threshold-num = <4>;
+ // Setup 4 buttons impedance according to Android specification
+ nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>;
+ nuvoton,sar-hysteresis = <0>;
+ nuvoton,sar-voltage = <6>;
+ nuvoton,sar-compare-time = <1>;
+ nuvoton,sar-sampling-time = <1>;
+ nuvoton,short-key-debounce = <0>;
+ nuvoton,jack-eject-debounce = <1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
new file mode 100644
index 000000000000..a54f194a0b49
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
@@ -0,0 +1,239 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8825.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NAU8825 audio CODEC
+
+maintainers:
+ - John Hsu <KCHSU0@nuvoton.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nau8825
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ nuvoton,jkdet-enable:
+ description:
+ Enable jack detection via JKDET pin.
+ type: boolean
+
+ nuvoton,jkdet-pull-enable:
+ description:
+ Enable JKDET pin pull.
+ If set - pin pull enabled, otherwise pin in high impedance state.
+ type: boolean
+
+ nuvoton,jkdet-pull-up:
+ description:
+ Pull-up JKDET pin.
+ If set then JKDET pin is pull up, otherwise pull down.
+ type: boolean
+
+ nuvoton,jkdet-polarity:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ JKDET pin polarity.
+ enum:
+ - 0 # active high
+ - 1 # active low
+ default: 1
+
+ nuvoton,vref-impedance:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ VREF Impedance selection.
+ enum:
+ - 0 # Open
+ - 1 # 25 kOhm
+ - 2 # 125 kOhm
+ - 3 # 2.5 kOhm
+ default: 2
+
+ nuvoton,micbias-voltage:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Micbias voltage level.
+ enum:
+ - 0 # VDDA
+ - 1 # VDDA
+ - 2 # VDDA * 1.1
+ - 3 # VDDA * 1.2
+ - 4 # VDDA * 1.3
+ - 5 # VDDA * 1.4
+ - 6 # VDDA * 1.53
+ - 7 # VDDA * 1.53
+ default: 6
+
+ nuvoton,sar-threshold-num:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Number of buttons supported.
+ minimum: 1
+ maximum: 4
+ default: 4
+
+ nuvoton,sar-threshold:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description:
+ Impedance threshold for each button. Array that contains up to 8 buttons
+ configuration. SAR value is calculated as
+ SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R) where MICBIAS is
+ configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by
+ 'nuvoton,sar-voltage', R - button impedance.
+ Refer datasheet section 10.2 for more information about threshold
+ calculation.
+ minItems: 1
+ maxItems: 4
+ items:
+ minimum: 0
+ maximum: 255
+
+ nuvoton,sar-hysteresis:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Button impedance measurement hysteresis.
+ default: 0
+
+ nuvoton,sar-voltage:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Reference voltage for button impedance measurement.
+ enum:
+ - 0 # VDDA
+ - 1 # VDDA
+ - 2 # VDDA * 1.1
+ - 3 # VDDA * 1.2
+ - 4 # VDDA * 1.3
+ - 5 # VDDA * 1.4
+ - 6 # VDDA * 1.53
+ - 7 # VDDA * 1.53
+ default: 6
+
+ nuvoton,sar-compare-time:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ SAR compare time.
+ enum:
+ - 0 # 500 ns
+ - 1 # 1 us
+ - 2 # 2 us
+ - 3 # 4 us
+ default: 1
+
+ nuvoton,sar-sampling-time:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ SAR sampling time.
+ enum:
+ - 0 # 2 us
+ - 1 # 4 us
+ - 2 # 8 us
+ - 3 # 16 us
+ default: 1
+
+ nuvoton,short-key-debounce:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Button short key press debounce time.
+ enum:
+ - 0 # 30 ms
+ - 1 # 50 ms
+ - 2 # 100 ms
+ - 3 # 30 ms
+ default: 3
+
+ nuvoton,jack-insert-debounce:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ number from 0 to 7 that sets debounce time to 2^(n+2) ms.
+ maximum: 7
+ default: 7
+
+ nuvoton,jack-eject-debounce:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ number from 0 to 7 that sets debounce time to 2^(n+2) ms
+ maximum: 7
+ default: 0
+
+ nuvoton,crosstalk-enable:
+ description:
+ make crosstalk function enable if set.
+ type: boolean
+
+ nuvoton,adcout-drive-strong:
+ description:
+ make the drive strength of ADCOUT IO PIN strong if set.
+ Otherwise, the drive keeps normal strength.
+ type: boolean
+
+ nuvoton,adc-delay-ms:
+ description:
+ Delay (in ms) to make input path stable and avoid pop noise.
+ The default value is 125 and range between 125 to 500 ms.
+ minimum: 125
+ maximum: 500
+ default: 125
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: mclk
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1a {
+ #sound-dai-cells = <0>;
+ compatible = "nuvoton,nau8825";
+ reg = <0x1a>;
+ interrupt-parent = <&gpio>;
+ interrupts = <38 IRQ_TYPE_LEVEL_LOW>;
+ nuvoton,jkdet-enable;
+ nuvoton,jkdet-pull-enable;
+ nuvoton,jkdet-pull-up;
+ nuvoton,jkdet-polarity = <GPIO_ACTIVE_LOW>;
+ nuvoton,vref-impedance = <2>;
+ nuvoton,micbias-voltage = <6>;
+ // Setup 4 buttons impedance according to Android specification
+ nuvoton,sar-threshold-num = <4>;
+ nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>;
+ nuvoton,sar-hysteresis = <1>;
+ nuvoton,sar-voltage = <0>;
+ nuvoton,sar-compare-time = <0>;
+ nuvoton,sar-sampling-time = <0>;
+ nuvoton,short-key-debounce = <2>;
+ nuvoton,jack-insert-debounce = <7>;
+ nuvoton,jack-eject-debounce = <7>;
+ nuvoton,crosstalk-enable;
+
+ clock-names = "mclk";
+ clocks = <&tegra_pmc 1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml
index 7c1e9895ce85..2588589ad62d 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-common.yaml
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/nvidia,tegra-audio-common.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-common.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Common properties for NVIDIA Tegra audio complexes
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
index cdbb4096fa44..9e5b30d9c6e6 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/qcom,q6apm-dai.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/qcom,q6apm-dai.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Process Manager Digital Audio Interfaces
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-clocks.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-clocks.yaml
index 1168410f6fbd..3552c44137ed 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-clocks.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-clocks.yaml
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/qcom,q6dsp-lpass-clocks.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/qcom,q6dsp-lpass-clocks.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm DSP LPASS Clock Controller
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
index 044e77718a1b..08c618e7e428 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/qcom,q6dsp-lpass-ports.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/qcom,q6dsp-lpass-ports.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm DSP LPASS(Low Power Audio SubSystem) Audio Ports
diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
new file mode 100644
index 000000000000..e6723c9e312a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,wsa8840.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm WSA8840/WSA8845/WSA8845H smart speaker amplifier
+
+maintainers:
+ - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description:
+ WSA884X is a family of Qualcomm Aqstic smart speaker amplifiers using
+ SoundWire digital audio interface.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: sdw20217020400
+
+ reg:
+ maxItems: 1
+
+ powerdown-gpios:
+ description: Powerdown/Shutdown line to use (pin SD_N)
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ vdd-1p8-supply: true
+ vdd-io-supply: true
+
+required:
+ - compatible
+ - reg
+ - powerdown-gpios
+ - '#sound-dai-cells'
+ - vdd-1p8-supply
+ - vdd-io-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ soundwire-controller {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ speaker@0,1 {
+ compatible = "sdw20217020400";
+ reg = <0 1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spkr_2_sd_n_active>;
+ powerdown-gpios = <&lpass_tlmm 18 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "SpkrRight";
+ vdd-1p8-supply = <&vreg_l15b_1p8>;
+ vdd-io-supply = <&vreg_l3g_1p2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1016.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1016.yaml
new file mode 100644
index 000000000000..5287e9c9197e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,rt1016.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,rt1016.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Reaktek RT1016 Stereo Class D Audio Amplifier
+
+maintainers:
+ - oder_chiou@realtek.com
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: realtek,rt1016
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1a {
+ compatible = "realtek,rt1016";
+ reg = <0x1a>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/rt1016.txt b/Documentation/devicetree/bindings/sound/rt1016.txt
deleted file mode 100644
index 2310f8ff259b..000000000000
--- a/Documentation/devicetree/bindings/sound/rt1016.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-RT1016 Stereo Class D Audio Amplifier
-
-This device supports I2C only.
-
-Required properties:
-
-- compatible : "realtek,rt1016".
-
-- reg : The I2C address of the device.
-
-
-Example:
-
-rt1016: codec@1a {
- compatible = "realtek,rt1016";
- reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml b/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml
index 56e623d4e168..a970fd264b21 100644
--- a/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml
@@ -36,7 +36,8 @@ properties:
const: i2sclk
resets:
- maxItems: 1
+ items:
+ - description: Optional controller resets
dmas:
items:
diff --git a/Documentation/devicetree/bindings/sound/ssm2518.txt b/Documentation/devicetree/bindings/sound/ssm2518.txt
deleted file mode 100644
index 59381a778c79..000000000000
--- a/Documentation/devicetree/bindings/sound/ssm2518.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-SSM2518 audio amplifier
-
-This device supports I2C only.
-
-Required properties:
- - compatible : Must be "adi,ssm2518"
- - reg : the I2C address of the device. This will either be 0x34 (ADDR pin low)
- or 0x35 (ADDR pin high)
-
-Optional properties:
- - gpios : GPIO connected to the nSD pin. If the property is not present it is
- assumed that the nSD pin is hardwired to always on.
-
-Example:
-
- ssm2518: ssm2518@34 {
- compatible = "adi,ssm2518";
- reg = <0x34>;
- gpios = <&gpio 5 0>;
- };
diff --git a/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml b/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml
index a040d4d31412..b9111d375b93 100644
--- a/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml
@@ -61,6 +61,10 @@ properties:
description: Configure the I2S device as MCLK clock provider.
const: 0
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
required:
- compatible
- "#sound-dai-cells"
@@ -89,6 +93,13 @@ examples:
dma-names = "rx", "tx";
pinctrl-names = "default";
pinctrl-0 = <&i2s2_pins_a>;
+
+ /* assume audio-graph */
+ port {
+ codec_endpoint: endpoint {
+ remote-endpoint = <&codec_endpoint>;
+ };
+ };
};
...
diff --git a/Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml b/Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml
new file mode 100644
index 000000000000..abb373fbfa26
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/starfive,jh7110-tdm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive JH7110 TDM Controller
+
+description: |
+ The TDM Controller is a Time Division Multiplexed audio interface
+ integrated in StarFive JH7110 SoC, allowing up to 8 channels of
+ audio over a serial interface. The TDM controller can operate both
+ in master and slave mode.
+
+maintainers:
+ - Walker Chen <walker.chen@starfivetech.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - starfive,jh7110-tdm
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: TDM AHB Clock
+ - description: TDM APB Clock
+ - description: TDM Internal Clock
+ - description: TDM Clock
+ - description: Inner MCLK
+ - description: TDM External Clock
+
+ clock-names:
+ items:
+ - const: tdm_ahb
+ - const: tdm_apb
+ - const: tdm_internal
+ - const: tdm
+ - const: mclk_inner
+ - const: tdm_ext
+
+ resets:
+ items:
+ - description: tdm ahb reset line
+ - description: tdm apb reset line
+ - description: tdm core reset line
+
+ dmas:
+ items:
+ - description: RX DMA Channel
+ - description: TX DMA Channel
+
+ dma-names:
+ items:
+ - const: rx
+ - const: tx
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - resets
+ - dmas
+ - dma-names
+ - "#sound-dai-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ tdm@10090000 {
+ compatible = "starfive,jh7110-tdm";
+ reg = <0x10090000 0x1000>;
+ clocks = <&syscrg 184>,
+ <&syscrg 185>,
+ <&syscrg 186>,
+ <&syscrg 187>,
+ <&syscrg 17>,
+ <&tdm_ext>;
+ clock-names = "tdm_ahb", "tdm_apb",
+ "tdm_internal", "tdm",
+ "mclk_inner", "tdm_ext";
+ resets = <&syscrg 105>,
+ <&syscrg 107>,
+ <&syscrg 106>;
+ dmas = <&dma 20>, <&dma 21>;
+ dma-names = "rx","tx";
+ #sound-dai-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/tas2562.yaml
index 31a3024ea789..f01c0dde0cf7 100644
--- a/Documentation/devicetree/bindings/sound/tas2562.yaml
+++ b/Documentation/devicetree/bindings/sound/tas2562.yaml
@@ -2,8 +2,8 @@
# Copyright (C) 2019 Texas Instruments Incorporated
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/tas2562.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/tas2562.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TAS2562 Smart PA
diff --git a/Documentation/devicetree/bindings/sound/tas2770.yaml b/Documentation/devicetree/bindings/sound/tas2770.yaml
index 8908bf1122e9..be2536e8c440 100644
--- a/Documentation/devicetree/bindings/sound/tas2770.yaml
+++ b/Documentation/devicetree/bindings/sound/tas2770.yaml
@@ -2,8 +2,8 @@
# Copyright (C) 2019-20 Texas Instruments Incorporated
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/tas2770.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/tas2770.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TAS2770 Smart PA
diff --git a/Documentation/devicetree/bindings/sound/tas27xx.yaml b/Documentation/devicetree/bindings/sound/tas27xx.yaml
index a876545ec87d..f2d878f6f495 100644
--- a/Documentation/devicetree/bindings/sound/tas27xx.yaml
+++ b/Documentation/devicetree/bindings/sound/tas27xx.yaml
@@ -2,8 +2,8 @@
# Copyright (C) 2020-2022 Texas Instruments Incorporated
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/tas27xx.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/tas27xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TAS2764/TAS2780 Smart PA
diff --git a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
new file mode 100644
index 000000000000..8d60e4e236d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tas2781.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TAS2781 SmartAMP
+
+maintainers:
+ - Shenghao Ding <shenghao-ding@ti.com>
+
+description:
+ The TAS2781 is a mono, digital input Class-D audio amplifier
+ optimized for efficiently driving high peak power into small
+ loudspeakers. An integrated on-chip DSP supports Texas Instruments
+ Smart Amp speaker protection algorithm. The integrated speaker
+ voltage and current sense provides for real time
+ monitoring of loudspeaker behavior.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ti,tas2781
+
+ reg:
+ description:
+ I2C address, in multiple tas2781s case, all the i2c address
+ aggreate as one Audio Device to support multiple audio slots.
+ maxItems: 8
+ minItems: 1
+ items:
+ minimum: 0x38
+ maximum: 0x3f
+
+ reset-gpios:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ /* example with quad tas2781s, such as tablet or pad device */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ quad_tas2781: tas2781@38 {
+ compatible = "ti,tas2781";
+ reg = <0x38>, /* Audio slot 0 */
+ <0x3a>, /* Audio slot 1 */
+ <0x39>, /* Audio slot 2 */
+ <0x3b>; /* Audio slot 3 */
+
+ #sound-dai-cells = <0>;
+ reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <15>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320aic32x4.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320aic32x4.yaml
new file mode 100644
index 000000000000..a7cc9aa34468
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320aic32x4.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2019 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tlv320aic32x4.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TLV320AIC32x4 Stereo Audio codec
+
+maintainers:
+ - Alexander Stein <alexander.stein@ew.tq-group.com>
+
+description: |
+ The TLV320AIC32x4 audio codec can be accessed using I2C or SPI
+
+properties:
+ compatible:
+ enum:
+ - ti,tas2505
+ - ti,tlv320aic32x4
+ - ti,tlv320aic32x6
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Master clock
+
+ clock-names:
+ items:
+ - const: mclk
+
+ av-supply:
+ description: Analog core power supply
+
+ dv-supply:
+ description: Digital core power supply
+
+ iov-supply:
+ description: Digital IO power supply
+
+ ldoin-supply:
+ description: LDO power supply
+
+ reset-gpios:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ aic32x4-gpio-func:
+ description: |
+ GPIO function configuration for pins MFP1-MFP5.
+ Types are defined in include/sound/tlv320aic32x4.h
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 5
+ maxItems: 5
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - iov-supply
+
+allOf:
+ - $ref: dai-common.yaml#
+ - if:
+ not:
+ required:
+ - ldoin-supply
+ then:
+ required:
+ - av-supply
+ - dv-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ audio-codec@18 {
+ compatible = "ti,tlv320aic32x4";
+ reg = <0x18>;
+ iov-supply = <&reg_3v3>;
+ ldoin-supply = <&reg_3v3>;
+ clocks = <&clks 201>;
+ clock-names = "mclk";
+ aic32x4-gpio-func= <
+ 0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
+ 0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
+ 0x04 /* MFP3 AIC32X4_MFP3_GPIO_ENABLED */
+ 0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
+ 0x08 /* MFP5 AIC32X4_MFP5_GPIO_INPUT */
+ >;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml
index e8ca9f3369f8..206f6d61e362 100644
--- a/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml
@@ -61,6 +61,7 @@ properties:
GPIO specification for the active low RESET input.
gpio-reset:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
maxItems: 1
description:
Deprecated, please use reset-gpios instead.
diff --git a/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt b/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt
deleted file mode 100644
index 0b4e21bde5bc..000000000000
--- a/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-Texas Instruments - tlv320aic32x4 Codec module
-
-The tlv320aic32x4 serial control bus communicates through I2C protocols
-
-Required properties:
- - compatible - "string" - One of:
- "ti,tlv320aic32x4" TLV320AIC3204
- "ti,tlv320aic32x6" TLV320AIC3206, TLV320AIC3256
- "ti,tas2505" TAS2505, TAS2521
- - reg: I2C slave address
- - *-supply: Required supply regulators are:
- "iov" - digital IO power supply
- "ldoin" - LDO power supply
- "dv" - Digital core power supply
- "av" - Analog core power supply
- If you supply ldoin, dv and av are optional. Otherwise they are required
- See regulator/regulator.txt for more information about the detailed binding
- format.
-
-Optional properties:
- - reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt
- - clocks/clock-names: Clock named 'mclk' for the master clock of the codec.
- See clock/clock-bindings.txt for information about the detailed format.
- - aic32x4-gpio-func - <array of 5 int>
- - Types are defined in include/sound/tlv320aic32x4.h
-
-
-Example:
-
-codec: tlv320aic32x4@18 {
- compatible = "ti,tlv320aic32x4";
- reg = <0x18>;
- clocks = <&clks 201>;
- clock-names = "mclk";
- aic32x4-gpio-func= <
- 0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
- 0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
- 0x04 /* MFP3 AIC32X4_MFP3_GPIO_ENABLED */
- 0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
- 0x08 /* MFP5 AIC32X4_MFP5_GPIO_INPUT */
- >;
-};
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml
index 7105ed5fd6c7..4cfa66f62681 100644
--- a/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml
+++ b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/sound/wlf,wm8903.yaml#"
-$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+$id: http://devicetree.org/schemas/sound/wlf,wm8903.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8903 audio codec
diff --git a/Documentation/sound/cards/audigy-mixer.rst b/Documentation/sound/cards/audigy-mixer.rst
index aa176451d5b5..ea66b50a2b03 100644
--- a/Documentation/sound/cards/audigy-mixer.rst
+++ b/Documentation/sound/cards/audigy-mixer.rst
@@ -227,7 +227,7 @@ PCM stream related controls
name='EMU10K1 PCM Volume',index 0-31
------------------------------------
-Channel volume attenuation in range 0-0xffff. The maximum value (no
+Channel volume attenuation in range 0-0x1fffd. The middle value (no
attenuation) is default. The channel mapping for three values is
as follows:
@@ -240,30 +240,30 @@ name='EMU10K1 PCM Send Routing',index 0-31
This control specifies the destination - FX-bus accumulators. There are 24
values in this mapping:
-* 0 - mono, A destination (FX-bus 0-63), default 0
-* 1 - mono, B destination (FX-bus 0-63), default 1
-* 2 - mono, C destination (FX-bus 0-63), default 2
-* 3 - mono, D destination (FX-bus 0-63), default 3
-* 4 - mono, E destination (FX-bus 0-63), default 0
-* 5 - mono, F destination (FX-bus 0-63), default 0
-* 6 - mono, G destination (FX-bus 0-63), default 0
-* 7 - mono, H destination (FX-bus 0-63), default 0
-* 8 - left, A destination (FX-bus 0-63), default 0
-* 9 - left, B destination (FX-bus 0-63), default 1
+* 0 - mono, A destination (FX-bus 0-63), default 0
+* 1 - mono, B destination (FX-bus 0-63), default 1
+* 2 - mono, C destination (FX-bus 0-63), default 2
+* 3 - mono, D destination (FX-bus 0-63), default 3
+* 4 - mono, E destination (FX-bus 0-63), default 4
+* 5 - mono, F destination (FX-bus 0-63), default 5
+* 6 - mono, G destination (FX-bus 0-63), default 6
+* 7 - mono, H destination (FX-bus 0-63), default 7
+* 8 - left, A destination (FX-bus 0-63), default 0
+* 9 - left, B destination (FX-bus 0-63), default 1
* 10 - left, C destination (FX-bus 0-63), default 2
* 11 - left, D destination (FX-bus 0-63), default 3
-* 12 - left, E destination (FX-bus 0-63), default 0
-* 13 - left, F destination (FX-bus 0-63), default 0
-* 14 - left, G destination (FX-bus 0-63), default 0
-* 15 - left, H destination (FX-bus 0-63), default 0
+* 12 - left, E destination (FX-bus 0-63), default 4
+* 13 - left, F destination (FX-bus 0-63), default 5
+* 14 - left, G destination (FX-bus 0-63), default 6
+* 15 - left, H destination (FX-bus 0-63), default 7
* 16 - right, A destination (FX-bus 0-63), default 0
* 17 - right, B destination (FX-bus 0-63), default 1
* 18 - right, C destination (FX-bus 0-63), default 2
* 19 - right, D destination (FX-bus 0-63), default 3
-* 20 - right, E destination (FX-bus 0-63), default 0
-* 21 - right, F destination (FX-bus 0-63), default 0
-* 22 - right, G destination (FX-bus 0-63), default 0
-* 23 - right, H destination (FX-bus 0-63), default 0
+* 20 - right, E destination (FX-bus 0-63), default 4
+* 21 - right, F destination (FX-bus 0-63), default 5
+* 22 - right, G destination (FX-bus 0-63), default 6
+* 23 - right, H destination (FX-bus 0-63), default 7
Don't forget that it's illegal to assign a channel to the same FX-bus accumulator
more than once (it means 0=0 && 1=0 is an invalid combination).
diff --git a/Documentation/sound/cards/index.rst b/Documentation/sound/cards/index.rst
index c016f8c3b88b..49c1f2f688f8 100644
--- a/Documentation/sound/cards/index.rst
+++ b/Documentation/sound/cards/index.rst
@@ -17,3 +17,4 @@ Card-Specific Information
hdspm
serial-u16550
img-spdif-in
+ pcmtest
diff --git a/Documentation/sound/cards/pcmtest.rst b/Documentation/sound/cards/pcmtest.rst
new file mode 100644
index 000000000000..e163522f3205
--- /dev/null
+++ b/Documentation/sound/cards/pcmtest.rst
@@ -0,0 +1,120 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+The Virtual PCM Test Driver
+===========================
+
+The Virtual PCM Test Driver emulates a generic PCM device, and can be used for
+testing/fuzzing of the userspace ALSA applications, as well as for testing/fuzzing of
+the PCM middle layer. Additionally, it can be used for simulating hard to reproduce
+problems with PCM devices.
+
+What can this driver do?
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+At this moment the driver can do the following things:
+ * Simulate both capture and playback processes
+ * Generate random or pattern-based capturing data
+ * Inject delays into the playback and capturing processes
+ * Inject errors during the PCM callbacks
+
+It supports up to 8 substreams and 4 channels. Also it supports both interleaved and
+non-interleaved access modes.
+
+Also, this driver can check the playback stream for containing the predefined pattern,
+which is used in the corresponding selftest (alsa/pcmtest-test.sh) to check the PCM middle
+layer data transferring functionality. Additionally, this driver redefines the default
+RESET ioctl, and the selftest covers this PCM API functionality as well.
+
+Configuration
+-------------
+
+The driver has several parameters besides the common ALSA module parameters:
+
+ * fill_mode (bool) - Buffer fill mode (see below)
+ * inject_delay (int)
+ * inject_hwpars_err (bool)
+ * inject_prepare_err (bool)
+ * inject_trigger_err (bool)
+
+
+Capture Data Generation
+-----------------------
+
+The driver has two modes of data generation: the first (0 in the fill_mode parameter)
+means random data generation, the second (1 in the fill_mode) - pattern-based
+data generation. Let's look at the second mode.
+
+First of all, you may want to specify the pattern for data generation. You can do it
+by writing the pattern to the debugfs file. There are pattern buffer debugfs entries
+for each channel, as well as entries which contain the pattern buffer length.
+
+ * /sys/kernel/debug/pcmtest/fill_pattern[0-3]
+ * /sys/kernel/debug/pcmtest/fill_pattern[0-3]_len
+
+To set the pattern for the channel 0 you can execute the following command:
+
+.. code-block:: bash
+
+ echo -n mycoolpattern > /sys/kernel/debug/pcmtest/fill_pattern0
+
+Then, after every capture action performed on the 'pcmtest' device the buffer for the
+channel 0 will contain 'mycoolpatternmycoolpatternmycoolpatternmy...'.
+
+The pattern itself can be up to 4096 bytes long.
+
+Delay injection
+---------------
+
+The driver has 'inject_delay' parameter, which has very self-descriptive name and
+can be used for time delay/speedup simulations. The parameter has integer type, and
+it means the delay added between module's internal timer ticks.
+
+If the 'inject_delay' value is positive, the buffer will be filled slower, if it is
+negative - faster. You can try it yourself by starting a recording in any
+audiorecording application (like Audacity) and selecting the 'pcmtest' device as a
+source.
+
+This parameter can be also used for generating a huge amount of sound data in a very
+short period of time (with the negative 'inject_delay' value).
+
+Errors injection
+----------------
+
+This module can be used for injecting errors into the PCM communication process. This
+action can help you to figure out how the userspace ALSA program behaves under unusual
+circumstances.
+
+For example, you can make all 'hw_params' PCM callback calls return EBUSY error by
+writing '1' to the 'inject_hwpars_err' module parameter:
+
+.. code-block:: bash
+
+ echo 1 > /sys/module/snd_pcmtest/parameters/inject_hwpars_err
+
+Errors can be injected into the following PCM callbacks:
+
+ * hw_params (EBUSY)
+ * prepare (EINVAL)
+ * trigger (EINVAL)
+
+Playback test
+-------------
+
+This driver can be also used for the playback functionality testing - every time you
+write the playback data to the 'pcmtest' PCM device and close it, the driver checks the
+buffer for containing the looped pattern (which is specified in the fill_pattern
+debugfs file for each channel). If the playback buffer content represents the looped
+pattern, 'pc_test' debugfs entry is set into '1'. Otherwise, the driver sets it to '0'.
+
+ioctl redefinition test
+-----------------------
+
+The driver redefines the 'reset' ioctl, which is default for all PCM devices. To test
+this functionality, we can trigger the reset ioctl and check the 'ioctl_test' debugfs
+entry:
+
+.. code-block:: bash
+
+ cat /sys/kernel/debug/pcmtest/ioctl_test
+
+If the ioctl is triggered successfully, this file will contain '1', and '0' otherwise.
diff --git a/Documentation/sound/cards/sb-live-mixer.rst b/Documentation/sound/cards/sb-live-mixer.rst
index 819886634400..4dd9bfe01bd8 100644
--- a/Documentation/sound/cards/sb-live-mixer.rst
+++ b/Documentation/sound/cards/sb-live-mixer.rst
@@ -258,7 +258,7 @@ PCM stream related controls
``name='EMU10K1 PCM Volume',index 0-31``
----------------------------------------
-Channel volume attenuation in range 0-0xffff. The maximum value (no
+Channel volume attenuation in range 0-0x1fffd. The middle value (no
attenuation) is default. The channel mapping for three values is
as follows:
diff --git a/Documentation/sound/designs/compress-offload.rst b/Documentation/sound/designs/compress-offload.rst
index 935f325dbc77..655624f77092 100644
--- a/Documentation/sound/designs/compress-offload.rst
+++ b/Documentation/sound/designs/compress-offload.rst
@@ -268,11 +268,12 @@ with setting of meta_data and signalling for next track ::
| |
| V
| +----------+
- | | |
- | |NEXT_TRACK|
- | | |
- | +----------+
- | |
+ | compr_set_params() | |
+ | +-----------|NEXT_TRACK|
+ | | | |
+ | | +--+-------+
+ | | | |
+ | +--------------+ |
| |
| | compr_partial_drain()
| |
diff --git a/Documentation/sound/designs/index.rst b/Documentation/sound/designs/index.rst
index 1eb08e7bae52..b79db9ad8732 100644
--- a/Documentation/sound/designs/index.rst
+++ b/Documentation/sound/designs/index.rst
@@ -15,3 +15,4 @@ Designs and Implementations
oss-emulation
seq-oss
jack-injection
+ midi-2.0
diff --git a/Documentation/sound/designs/midi-2.0.rst b/Documentation/sound/designs/midi-2.0.rst
new file mode 100644
index 000000000000..27d0d3dea1b0
--- /dev/null
+++ b/Documentation/sound/designs/midi-2.0.rst
@@ -0,0 +1,378 @@
+=================
+MIDI 2.0 on Linux
+=================
+
+General
+=======
+
+MIDI 2.0 is an extended protocol for providing higher resolutions and
+more fine controls over the legacy MIDI 1.0. The fundamental changes
+introduced for supporting MIDI 2.0 are:
+
+- Support of Universal MIDI Packet (UMP)
+- Support of MIDI 2.0 protocol messages
+- Transparent conversions between UMP and legacy MIDI 1.0 byte stream
+- MIDI-CI for property and profile configurations
+
+UMP is a new container format to hold all MIDI protocol 1.0 and MIDI
+2.0 protocol messages. Unlike the former byte stream, it's 32bit
+aligned, and each message can be put in a single packet. UMP can send
+the events up to 16 "UMP Groups", where each UMP Group contain up to
+16 MIDI channels.
+
+MIDI 2.0 protocol is an extended protocol to achieve the higher
+resolution and more controls over the old MIDI 1.0 protocol.
+
+MIDI-CI is a high-level protocol that can talk with the MIDI device
+for the flexible profiles and configurations. It's represented in the
+form of special SysEx.
+
+For Linux implementations, the kernel supports the UMP transport and
+the encoding/decoding of MIDI protocols on UMP, while MIDI-CI is
+supported in user-space over the standard SysEx.
+
+As of this writing, only USB MIDI device supports the UMP and Linux
+2.0 natively. The UMP support itself is pretty generic, hence it
+could be used by other transport layers, although it could be
+implemented differently (e.g. as a ALSA sequencer client), too.
+
+The access to UMP devices are provided in two ways: the access via
+rawmidi device and the access via ALSA sequencer API.
+
+ALSA sequencer API was extended to allow the payload of UMP packets.
+It's allowed to connect freely between MIDI 1.0 and MIDI 2.0 sequencer
+clients, and the events are converted transparently.
+
+
+Kernel Configuration
+====================
+
+The following new configs are added for supporting MIDI 2.0:
+`CONFIG_SND_UMP`, `CONFIG_SND_UMP_LEGACY_RAWMIDI`,
+`CONFIG_SND_SEQ_UMP`, `CONFIG_SND_SEQ_UMP_CLIENT`, and
+`CONFIG_SND_USB_AUDIO_MIDI_V2`. The first visible one is
+`CONFIG_SND_USB_AUDIO_MIDI_V2`, and when you choose it (to set `=y`),
+the core support for UMP (`CONFIG_SND_UMP`) and the sequencer binding
+(`CONFIG_SND_SEQ_UMP_CLIENT`) will be automatically selected.
+
+Additionally, `CONFIG_SND_UMP_LEGACY_RAWMIDI=y` will enable the
+support for the legacy raw MIDI device for UMP Endpoints.
+
+
+Rawmidi Device with USB MIDI 2.0
+================================
+
+When a device supports MIDI 2.0, the USB-audio driver probes and uses
+the MIDI 2.0 interface (that is found always at the altset 1) as
+default instead of the MIDI 1.0 interface (at altset 0). You can
+switch back to the binding with the old MIDI 1.0 interface by passing
+`midi2_enable=0` option to snd-usb-audio driver module, too.
+
+The USB audio driver tries to query the UMP Endpoint and UMP Function
+Block information that are provided since UMP v1.1, and builds up the
+topology based on those information. When the device is older and
+doesn't respond to the new UMP inquiries, the driver falls back and
+builds the topology based on Group Terminal Block (GTB) information
+from the USB descriptor. Some device might be screwed up by the
+unexpected UMP command; in such a case, pass `midi2_probe=0` option to
+snd-usb-audio driver for skipping the UMP v1.1 inquiries.
+
+When the MIDI 2.0 device is probed, the kernel creates a rawmidi
+device for each UMP Endpoint of the device. Its device name is
+`/dev/snd/umpC*D*` and different from the standard rawmidi device name
+`/dev/snd/midiC*D*` for MIDI 1.0, in order to avoid confusing the
+legacy applications accessing mistakenly to UMP devices.
+
+You can read and write UMP packet data directly from/to this UMP
+rawmidi device. For example, reading via `hexdump` like below will
+show the incoming UMP packets of the card 0 device 0 in the hex
+format::
+
+ % hexdump -C /dev/snd/umpC0D0
+ 00000000 01 07 b0 20 00 07 b0 20 64 3c 90 20 64 3c 80 20 |... ... d<. d<. |
+
+Unlike the MIDI 1.0 byte stream, UMP is a 32bit packet, and the size
+for reading or writing the device is also aligned to 32bit (which is 4
+bytes).
+
+The 32-bit words in the UMP packet payload are always in CPU native
+endianness. Transport drivers are responsible to convert UMP words
+from / to system endianness to required transport endianness / byte
+order.
+
+When `CONFIG_SND_UMP_LEGACY_RAWMIDI` is set, the driver creates
+another standard raw MIDI device additionally as `/dev/snd/midiC*D*`.
+This contains 16 substreams, and each substream corresponds to a
+(0-based) UMP Group. Legacy applications can access to the specified
+group via each substream in MIDI 1.0 byte stream format. With the
+ALSA rawmidi API, you can open the arbitrary substream, while just
+opening `/dev/snd/midiC*D*` will end up with opening the first
+substream.
+
+Each UMP Endpoint can provide the additional information, constructed
+from the information inquired via UMP 1.1 Stream messages or USB MIDI
+2.0 descriptors. And a UMP Endpoint may contain one or more UMP
+Blocks, where UMP Block is an abstraction introduced in the ALSA UMP
+implementations to represent the associations among UMP Groups. UMP
+Block corresponds to Function Block in UMP 1.1 specification. When
+UMP 1.1 Function Block information isn't available, it's filled
+partially from Group Terminal Block (GTB) as defined in USB MIDI 2.0
+specifications.
+
+The information of UMP Endpoints and UMP Blocks are found in the proc
+file `/proc/asound/card*/midi*`. For example::
+
+ % cat /proc/asound/card1/midi0
+ ProtoZOA MIDI
+
+ Type: UMP
+ EP Name: ProtoZOA
+ EP Product ID: ABCD12345678
+ UMP Version: 0x0000
+ Protocol Caps: 0x00000100
+ Protocol: 0x00000100
+ Num Blocks: 3
+
+ Block 0 (ProtoZOA Main)
+ Direction: bidirection
+ Active: Yes
+ Groups: 1-1
+ Is MIDI1: No
+
+ Block 1 (ProtoZOA Ext IN)
+ Direction: output
+ Active: Yes
+ Groups: 2-2
+ Is MIDI1: Yes (Low Speed)
+ ....
+
+Note that `Groups` field shown in the proc file above indicates the
+1-based UMP Group numbers (from-to).
+
+Those additional UMP Endpoint and UMP Block information can be
+obtained via the new ioctls `SNDRV_UMP_IOCTL_ENDPOINT_INFO` and
+`SNDRV_UMP_IOCTL_BLOCK_INFO`, respectively.
+
+The rawmidi name and the UMP Endpoint name are usually identical, and
+in the case of USB MIDI, it's taken from `iInterface` of the
+corresponding USB MIDI interface descriptor. If it's not provided,
+it's copied from `iProduct` of the USB device descriptor as a
+fallback.
+
+The Endpoint Product ID is a string field and supposed to be unique.
+It's copied from `iSerialNumber` of the device for USB MIDI.
+
+The protocol capabilities and the actual protocol bits are defined in
+`asound.h`.
+
+
+ALSA Sequencer with USB MIDI 2.0
+================================
+
+In addition to the rawmidi interfaces, ALSA sequencer interface
+supports the new UMP MIDI 2.0 device, too. Now, each ALSA sequencer
+client may set its MIDI version (0, 1 or 2) to declare itself being
+either the legacy, UMP MIDI 1.0 or UMP MIDI 2.0 device, respectively.
+The first, legacy client is the one that sends/receives the old
+sequencer event as was. Meanwhile, UMP MIDI 1.0 and 2.0 clients send
+and receive in the extended event record for UMP. The MIDI version is
+seen in the new `midi_version` field of `snd_seq_client_info`.
+
+A UMP packet can be sent/received in a sequencer event embedded by
+specifying the new event flag bit `SNDRV_SEQ_EVENT_UMP`. When this
+flag is set, the event has 16 byte (128 bit) data payload for holding
+the UMP packet. Without the `SNDRV_SEQ_EVENT_UMP` bit flag, the event
+is treated as a legacy event as it was (with max 12 byte data
+payload).
+
+With `SNDRV_SEQ_EVENT_UMP` flag set, the type field of a UMP sequencer
+event is ignored (but it should be set to 0 as default).
+
+The type of each client can be seen in `/proc/asound/seq/clients`.
+For example::
+
+ % cat /proc/asound/seq/clients
+ Client info
+ cur clients : 3
+ ....
+ Client 14 : "Midi Through" [Kernel Legacy]
+ Port 0 : "Midi Through Port-0" (RWe-)
+ Client 20 : "ProtoZOA" [Kernel UMP MIDI1]
+ UMP Endpoint: ProtoZOA
+ UMP Block 0: ProtoZOA Main [Active]
+ Groups: 1-1
+ UMP Block 1: ProtoZOA Ext IN [Active]
+ Groups: 2-2
+ UMP Block 2: ProtoZOA Ext OUT [Active]
+ Groups: 3-3
+ Port 0 : "MIDI 2.0" (RWeX) [In/Out]
+ Port 1 : "ProtoZOA Main" (RWeX) [In/Out]
+ Port 2 : "ProtoZOA Ext IN" (-We-) [Out]
+ Port 3 : "ProtoZOA Ext OUT" (R-e-) [In]
+
+Here you can find two types of kernel clients, "Legacy" for client 14,
+and "UMP MIDI1" for client 20, which is a USB MIDI 2.0 device.
+A USB MIDI 2.0 client gives always the port 0 as "MIDI 2.0" and the
+rest ports from 1 for each UMP Group (e.g. port 1 for Group 1).
+In this example, the device has three active groups (Main, Ext IN and
+Ext OUT), and those are exposed as sequencer ports from 1 to 3.
+The "MIDI 2.0" port is for a UMP Endpoint, and its difference from
+other UMP Group ports is that UMP Endpoint port sends the events from
+the all ports on the device ("catch-all"), while each UMP Group port
+sends only the events from the given UMP Group.
+Also, UMP groupless messages (such as the UMP message type 0x0f) are
+sent only to the UMP Endpoint port.
+
+Note that, although each UMP sequencer client usually creates 16
+ports, those ports that don't belong to any UMP Blocks (or belonging
+to inactive UMP Blocks) are marked as inactive, and they don't appear
+in the proc outputs. In the example above, the sequencer ports from 4
+to 16 are present but not shown there.
+
+The proc file above shows the UMP Block information, too. The same
+entry (but with more detailed information) is found in the rawmidi
+proc output.
+
+When clients are connected between different MIDI versions, the events
+are translated automatically depending on the client's version, not
+only between the legacy and the UMP MIDI 1.0/2.0 types, but also
+between UMP MIDI 1.0 and 2.0 types, too. For example, running
+`aseqdump` program on the ProtoZOA Main port in the legacy mode will
+give you the output like::
+
+ % aseqdump -p 20:1
+ Waiting for data. Press Ctrl+C to end.
+ Source Event Ch Data
+ 20:1 Note on 0, note 60, velocity 100
+ 20:1 Note off 0, note 60, velocity 100
+ 20:1 Control change 0, controller 11, value 4
+
+When you run `aseqdump` in MIDI 2.0 mode, it'll receive the high
+precision data like::
+
+ % aseqdump -u 2 -p 20:1
+ Waiting for data. Press Ctrl+C to end.
+ Source Event Ch Data
+ 20:1 Note on 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
+ 20:1 Note off 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
+ 20:1 Control change 0, controller 11, value 0x2000000
+
+while the data is automatically converted by ALSA sequencer core.
+
+
+Rawmidi API Extensions
+======================
+
+* The additional UMP Endpoint information can be obtained via the new
+ ioctl `SNDRV_UMP_IOCTL_ENDPOINT_INFO`. It contains the associated
+ card and device numbers, the bit flags, the protocols, the number of
+ UMP Blocks, the name string of the endpoint, etc.
+
+ The protocols are specified in two field, the protocol capabilities
+ and the current protocol. Both contain the bit flags specifying the
+ MIDI protocol version (`SNDRV_UMP_EP_INFO_PROTO_MIDI1` or
+ `SNDRV_UMP_EP_INFO_PROTO_MIDI2`) in the upper byte and the jitter
+ reduction timestamp (`SNDRV_UMP_EP_INFO_PROTO_JRTS_TX` and
+ `SNDRV_UMP_EP_INFO_PROTO_JRTS_RX`) in the lower byte.
+
+ A UMP Endpoint may contain up to 32 UMP Blocks, and the number of
+ the currently assigned blocks are shown in the Endpoint information.
+
+* Each UMP Block information can be obtained via another new ioctl
+ `SNDRV_UMP_IOCTL_BLOCK_INFO`. The block ID number (0-based) has to
+ be passed for the block to query. The received data contains the
+ associated the direction of the block, the first associated group ID
+ (0-based) and the number of groups, the name string of the block,
+ etc.
+
+ The direction is either `SNDRV_UMP_DIR_INPUT`,
+ `SNDRV_UMP_DIR_OUTPUT` or `SNDRV_UMP_DIR_BIDIRECTION`.
+
+* For the device supports UMP v1.1, the UMP MIDI protocol can be
+ switched via "Stream Configuration Request" message (UMP type 0x0f,
+ status 0x05). When UMP core receives such a message, it updates the
+ UMP EP info and the corresponding sequencer clients as well.
+
+
+Control API Extensions
+======================
+
+* The new ioctl `SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE` is introduced for
+ querying the next UMP rawmidi device, while the existing ioctl
+ `SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE` queries only the legacy
+ rawmidi devices.
+
+ For setting the subdevice (substream number) to be opened, use the
+ ioctl `SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE` like the normal
+ rawmidi.
+
+* Two new ioctls `SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO` and
+ `SNDRV_CTL_IOCTL_UMP_BLOCK_INFO` provide the UMP Endpoint and UMP
+ Block information of the specified UMP device via ALSA control API
+ without opening the actual (UMP) rawmidi device.
+ The `card` field is ignored upon inquiry, always tied with the card
+ of the control interface.
+
+
+Sequencer API Extensions
+========================
+
+* `midi_version` field is added to `snd_seq_client_info` to indicate
+ the current MIDI version (either 0, 1 or 2) of each client.
+ When `midi_version` is 1 or 2, the alignment of read from a UMP
+ sequencer client is also changed from the former 28 bytes to 32
+ bytes for the extended payload. The alignment size for the write
+ isn't changed, but each event size may differ depending on the new
+ bit flag below.
+
+* `SNDRV_SEQ_EVENT_UMP` flag bit is added for each sequencer event
+ flags. When this bit flag is set, the sequencer event is extended
+ to have a larger payload of 16 bytes instead of the legacy 12
+ bytes, and the event contains the UMP packet in the payload.
+
+* The new sequencer port type bit (`SNDRV_SEQ_PORT_TYPE_MIDI_UMP`)
+ indicates the port being UMP-capable.
+
+* The sequencer ports have new capability bits to indicate the
+ inactive ports (`SNDRV_SEQ_PORT_CAP_INACTIVE`) and the UMP Endpoint
+ port (`SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT`).
+
+* The event conversion of ALSA sequencer clients can be suppressed the
+ new filter bit `SNDRV_SEQ_FILTER_NO_CONVERT` set to the client info.
+ For example, the kernel pass-through client (`snd-seq-dummy`) sets
+ this flag internally.
+
+* The port information gained the new field `direction` to indicate
+ the direction of the port (either `SNDRV_SEQ_PORT_DIR_INPUT`,
+ `SNDRV_SEQ_PORT_DIR_OUTPUT` or `SNDRV_SEQ_PORT_DIR_BIDIRECTION`).
+
+* Another additional field for the port information is `ump_group`
+ which specifies the associated UMP Group Number (1-based).
+ When it's non-zero, the UMP group field in the UMP packet updated
+ upon delivery to the specified group (corrected to be 0-based).
+ Each sequencer port is supposed to set this field if it's a port to
+ specific to a certain UMP group.
+
+* Each client may set the additional event filter for UMP Groups in
+ `group_filter` bitmap. The filter consists of bitmap from 1-based
+ Group numbers. For example, when the bit 1 is set, messages from
+ Group 1 (i.e. the very first group) are filtered and not delivered.
+ The bit 0 is used for filtering UMP groupless messages.
+
+* Two new ioctls are added for UMP-capable clients:
+ `SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO` and
+ `SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO`. They are used to get and set
+ either `snd_ump_endpoint_info` or `snd_ump_block_info` data
+ associated with the sequencer client. The USB MIDI driver provides
+ those information from the underlying UMP rawmidi, while a
+ user-space client may provide its own data via `*_SET` ioctl.
+ For an Endpoint data, pass 0 to the `type` field, while for a Block
+ data, pass the block number + 1 to the `type` field.
+ Setting the data for a kernel client shall result in an error.
+
+* With UMP 1.1, Function Block information may be changed
+ dynamically. When the update of Function Block is received from the
+ device, ALSA sequencer core changes the corresponding sequencer port
+ name and attributes accordingly, and notifies the changes via the
+ announcement to the ALSA sequencer system port, similarly like the
+ normal port change notification.
diff --git a/MAINTAINERS b/MAINTAINERS
index 59e4f383db63..8fd401194e09 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17267,6 +17267,7 @@ F: sound/soc/codecs/wcd9335.*
F: sound/soc/codecs/wcd934x.c
F: sound/soc/codecs/wsa881x.c
F: sound/soc/codecs/wsa883x.c
+F: sound/soc/codecs/wsa884x.c
F: sound/soc/qcom/
QCOM EMBEDDED USB DEBUGGER (EUD)
@@ -20244,6 +20245,12 @@ F: Documentation/devicetree/bindings/power/starfive*
F: drivers/soc/starfive/jh71xx_pmu.c
F: include/dt-bindings/power/starfive,jh7110-pmu.h
+STARFIVE JH7110 TDM DRIVER
+M: Walker Chen <walker.chen@starfivetech.com>
+S: Maintained
+F: Documentation/devicetree/bindings/sound/starfive,jh7110-tdm.yaml
+F: sound/soc/starfive/jh7110_tdm.c
+
STARFIVE SOC DRIVERS
M: Conor Dooley <conor@kernel.org>
S: Maintained
@@ -22535,6 +22542,14 @@ L: linux-fsdevel@vger.kernel.org
S: Maintained
F: fs/vboxsf/*
+VIRTUAL PCM TEST DRIVER
+M: Ivan Orlov <ivan.orlov0322@gmail.com>
+L: alsa-devel@alsa-project.org
+S: Maintained
+F: Documentation/sound/cards/pcmtest.rst
+F: sound/drivers/pcmtest.c
+F: tools/testing/selftests/alsa/test-pcmtest-driver.c
+
VIRTUAL SERIO DEVICE DRIVER
M: Stephen Chandler Paul <thatslyude@gmail.com>
S: Maintained
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c
index ec056f6f40ce..6a9aa97373d3 100644
--- a/drivers/firmware/cirrus/cs_dsp.c
+++ b/drivers/firmware/cirrus/cs_dsp.c
@@ -2059,10 +2059,10 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
goto out_fw;
}
- cs_dsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
- (le32_to_cpu(hdr->ver) >> 16) & 0xff,
- (le32_to_cpu(hdr->ver) >> 8) & 0xff,
- le32_to_cpu(hdr->ver) & 0xff);
+ cs_dsp_info(dsp, "%s: v%d.%d.%d\n", file,
+ (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+ (le32_to_cpu(hdr->ver) >> 8) & 0xff,
+ le32_to_cpu(hdr->ver) & 0xff);
pos = le32_to_cpu(hdr->len);
diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
index 9f7c5103bc82..39f203256c4f 100644
--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
@@ -131,6 +131,14 @@
#define RX_CODEC_DMA_RX_7 126
#define QUINARY_MI2S_RX 127
#define QUINARY_MI2S_TX 128
+#define DISPLAY_PORT_RX_0 DISPLAY_PORT_RX
+#define DISPLAY_PORT_RX_1 129
+#define DISPLAY_PORT_RX_2 130
+#define DISPLAY_PORT_RX_3 131
+#define DISPLAY_PORT_RX_4 132
+#define DISPLAY_PORT_RX_5 133
+#define DISPLAY_PORT_RX_6 134
+#define DISPLAY_PORT_RX_7 135
#define LPASS_CLK_ID_PRI_MI2S_IBIT 1
#define LPASS_CLK_ID_PRI_MI2S_EBIT 2
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index a99b1fcfc617..7173d6d401eb 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -158,6 +158,9 @@
#define PCI_VENDOR_ID_LOONGSON 0x0014
+#define PCI_DEVICE_ID_LOONGSON_HDA 0x7a07
+#define PCI_DEVICE_ID_LOONGSON_HDMI 0x7a37
+
#define PCI_VENDOR_ID_TTTECH 0x0357
#define PCI_DEVICE_ID_TTTECH_MC322 0x000a
diff --git a/include/linux/usb/midi-v2.h b/include/linux/usb/midi-v2.h
new file mode 100644
index 000000000000..16f09d959a2d
--- /dev/null
+++ b/include/linux/usb/midi-v2.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * <linux/usb/midi-v2.h> -- USB MIDI 2.0 definitions.
+ */
+
+#ifndef __LINUX_USB_MIDI_V2_H
+#define __LINUX_USB_MIDI_V2_H
+
+#include <linux/types.h>
+#include <linux/usb/midi.h>
+
+/* A.1 MS Class-Specific Interface Descriptor Types */
+#define USB_DT_CS_GR_TRM_BLOCK 0x26
+
+/* A.1 MS Class-Specific Interface Descriptor Subtypes */
+/* same as MIDI 1.0 */
+
+/* A.2 MS Class-Specific Endpoint Descriptor Subtypes */
+#define USB_MS_GENERAL_2_0 0x02
+
+/* A.3 MS Class-Specific Group Terminal Block Descriptor Subtypes */
+#define USB_MS_GR_TRM_BLOCK_UNDEFINED 0x00
+#define USB_MS_GR_TRM_BLOCK_HEADER 0x01
+#define USB_MS_GR_TRM_BLOCK 0x02
+
+/* A.4 MS Interface Header MIDIStreaming Class Revision */
+#define USB_MS_REV_MIDI_1_0 0x0100
+#define USB_MS_REV_MIDI_2_0 0x0200
+
+/* A.5 MS MIDI IN and OUT Jack Types */
+/* same as MIDI 1.0 */
+
+/* A.6 Group Terminal Block Types */
+#define USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL 0x00
+#define USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY 0x01
+#define USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY 0x02
+
+/* A.7 Group Terminal Default MIDI Protocol */
+#define USB_MS_MIDI_PROTO_UNKNOWN 0x00 /* Unknown (Use MIDI-CI) */
+#define USB_MS_MIDI_PROTO_1_0_64 0x01 /* MIDI 1.0, UMP up to 64bits */
+#define USB_MS_MIDI_PROTO_1_0_64_JRTS 0x02 /* MIDI 1.0, UMP up to 64bits, Jitter Reduction Timestamps */
+#define USB_MS_MIDI_PROTO_1_0_128 0x03 /* MIDI 1.0, UMP up to 128bits */
+#define USB_MS_MIDI_PROTO_1_0_128_JRTS 0x04 /* MIDI 1.0, UMP up to 128bits, Jitter Reduction Timestamps */
+#define USB_MS_MIDI_PROTO_2_0 0x11 /* MIDI 2.0 */
+#define USB_MS_MIDI_PROTO_2_0_JRTS 0x12 /* MIDI 2.0, Jitter Reduction Timestamps */
+
+/* 5.2.2.1 Class-Specific MS Interface Header Descriptor */
+/* Same as MIDI 1.0, use struct usb_ms_header_descriptor */
+
+/* 5.3.2 Class-Specific MIDI Streaming Data Endpoint Descriptor */
+struct usb_ms20_endpoint_descriptor {
+ __u8 bLength; /* 4+n */
+ __u8 bDescriptorType; /* USB_DT_CS_ENDPOINT */
+ __u8 bDescriptorSubtype; /* USB_MS_GENERAL_2_0 */
+ __u8 bNumGrpTrmBlock; /* Number of Group Terminal Blocks: n */
+ __u8 baAssoGrpTrmBlkID[]; /* ID of the Group Terminal Blocks [n] */
+} __packed;
+
+#define USB_DT_MS20_ENDPOINT_SIZE(n) (4 + (n))
+
+/* As above, but more useful for defining your own descriptors: */
+#define DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(n) \
+struct usb_ms20_endpoint_descriptor_##n { \
+ __u8 bLength; \
+ __u8 bDescriptorType; \
+ __u8 bDescriptorSubtype; \
+ __u8 bNumGrpTrmBlock; \
+ __u8 baAssoGrpTrmBlkID[n]; \
+} __packed
+
+/* 5.4.1 Class-Specific Group Terminal Block Header Descriptor */
+struct usb_ms20_gr_trm_block_header_descriptor {
+ __u8 bLength; /* 5 */
+ __u8 bDescriptorType; /* USB_DT_CS_GR_TRM_BLOCK */
+ __u8 bDescriptorSubtype; /* USB_MS_GR_TRM_BLOCK_HEADER */
+ __le16 wTotalLength; /* Total number of bytes */
+} __packed;
+
+/* 5.4.2.1 Group Terminal Block Descriptor */
+struct usb_ms20_gr_trm_block_descriptor {
+ __u8 bLength; /* 13 */
+ __u8 bDescriptorType; /* USB_DT_CS_GR_TRM_BLOCK */
+ __u8 bDescriptorSubtype; /* USB_MS_GR_TRM_BLOCK */
+ __u8 bGrpTrmBlkID; /* ID of this Group Terminal Block */
+ __u8 bGrpTrmBlkType; /* Group Terminal Block Type */
+ __u8 nGroupTrm; /* The first member Group Terminal in this block */
+ __u8 nNumGroupTrm; /* Number of member Group Terminals spanned */
+ __u8 iBlockItem; /* String ID of Block item */
+ __u8 bMIDIProtocol; /* Default MIDI protocol */
+ __le16 wMaxInputBandwidth; /* Max input bandwidth capability in 4kB/s */
+ __le16 wMaxOutputBandwidth; /* Max output bandwidth capability in 4kB/s */
+} __packed;
+
+#endif /* __LINUX_USB_MIDI_V2_H */
diff --git a/include/sound/asequencer.h b/include/sound/asequencer.h
index 18d4bc3ee0b7..ddbb6bf801bb 100644
--- a/include/sound/asequencer.h
+++ b/include/sound/asequencer.h
@@ -65,6 +65,10 @@
#define snd_seq_ev_is_abstime(ev) (snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS)
#define snd_seq_ev_is_reltime(ev) (snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL)
+/* check whether the given event is a UMP event */
+#define snd_seq_ev_is_ump(ev) \
+ (IS_ENABLED(CONFIG_SND_SEQ_UMP) && ((ev)->flags & SNDRV_SEQ_EVENT_UMP))
+
/* queue sync port */
#define snd_seq_queue_sync_port(q) ((q) + 16)
diff --git a/include/sound/core.h b/include/sound/core.h
index 3edc4ab08774..f6e0dd648b80 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -98,7 +98,7 @@ struct snd_card {
struct device ctl_dev; /* control device */
unsigned int last_numid; /* last used numeric ID */
- struct rw_semaphore controls_rwsem; /* controls list lock */
+ struct rw_semaphore controls_rwsem; /* controls lock (list and values) */
rwlock_t ctl_files_rwlock; /* ctl_files list lock */
int controls_count; /* count of all controls */
size_t user_ctl_alloc_size; // current memory allocation by user controls.
@@ -232,7 +232,7 @@ static inline struct device *snd_card_get_device_link(struct snd_card *card)
extern int snd_major;
extern int snd_ecards_limit;
-extern struct class *sound_class;
+extern const struct class sound_class;
#ifdef CONFIG_SND_DEBUG
extern struct dentry *sound_debugfs_root;
#endif
diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
index 002042b1c73c..1f9713d7ca76 100644
--- a/include/sound/cs35l56.h
+++ b/include/sound/cs35l56.h
@@ -223,6 +223,7 @@
#define CS35L56_MBOX_CMD_AUDIO_PLAY 0x0B000001
#define CS35L56_MBOX_CMD_AUDIO_PAUSE 0x0B000002
+#define CS35L56_MBOX_CMD_AUDIO_REINIT 0x0B000003
#define CS35L56_MBOX_CMD_HIBERNATE_NOW 0x02000001
#define CS35L56_MBOX_CMD_WAKEUP 0x02000002
#define CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE 0x02000003
diff --git a/include/sound/da7219-aad.h b/include/sound/da7219-aad.h
index 24ee7baa2589..41320522daa2 100644
--- a/include/sound/da7219-aad.h
+++ b/include/sound/da7219-aad.h
@@ -44,6 +44,11 @@ enum da7219_aad_jack_ins_deb {
DA7219_AAD_JACK_INS_DEB_1S,
};
+enum da7219_aad_jack_ins_det_pty {
+ DA7219_AAD_JACK_INS_DET_PTY_LOW = 0,
+ DA7219_AAD_JACK_INS_DET_PTY_HIGH,
+};
+
enum da7219_aad_jack_det_rate {
DA7219_AAD_JACK_DET_RATE_32_64MS = 0,
DA7219_AAD_JACK_DET_RATE_64_128MS,
@@ -80,6 +85,7 @@ struct da7219_aad_pdata {
enum da7219_aad_btn_cfg btn_cfg;
enum da7219_aad_mic_det_thr mic_det_thr;
enum da7219_aad_jack_ins_deb jack_ins_deb;
+ enum da7219_aad_jack_ins_det_pty jack_ins_det_pty;
enum da7219_aad_jack_det_rate jack_det_rate;
enum da7219_aad_jack_rem_deb jack_rem_deb;
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 8fe80dcee71b..386a5f3be3e0 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -38,6 +38,35 @@
#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL))
+// This is used to define hardware bit-fields (sub-registers) by combining
+// the bit shift and count with the actual register address. The passed
+// mask must represent a single run of adjacent bits.
+// The non-concatenating (_NC) variant should be used directly only for
+// sub-registers that do not follow the <register>_<field> naming pattern.
+#define SUB_REG_NC(reg, field, mask) \
+ enum { \
+ field ## _MASK = mask, \
+ field = reg | \
+ (__builtin_ctz(mask) << 16) | \
+ (__builtin_popcount(mask) << 24), \
+ };
+#define SUB_REG(reg, field, mask) SUB_REG_NC(reg, reg ## _ ## field, mask)
+
+// Macros for manipulating values of bit-fields declared using the above macros.
+// Best used with constant register addresses, as otherwise quite some code is
+// generated. The actual register read/write functions handle combined addresses
+// automatically, so use of these macros conveys no advantage when accessing a
+// single sub-register at a time.
+#define REG_SHIFT(r) (((r) >> 16) & 0x1f)
+#define REG_SIZE(r) (((r) >> 24) & 0x1f)
+#define REG_MASK0(r) ((1U << REG_SIZE(r)) - 1U)
+#define REG_MASK(r) (REG_MASK0(r) << REG_SHIFT(r))
+#define REG_VAL_GET(r, v) ((v & REG_MASK(r)) >> REG_SHIFT(r))
+#define REG_VAL_PUT(r, v) ((v) << REG_SHIFT(r))
+
+// List terminator for snd_emu10k1_ptr_write_multiple()
+#define REGLIST_END ~0
+
// Audigy specify registers are prefixed with 'A_'
/************************************************************************************************/
@@ -90,6 +119,10 @@
#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */
#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */
#define IPR_CHANNELLOOP 0x00000040 /* Channel (half) loop interrupt(s) pending */
+ /* The interrupt is triggered shortly after */
+ /* CCR_READADDRESS has crossed the boundary; */
+ /* due to the cache, this runs ahead of the */
+ /* actual playback position. */
#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */
/* highest set channel in CLIPL, CLIPH, HLIPL, */
/* or HLIPH. When IPR is written with CL set, */
@@ -148,12 +181,10 @@
#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */
#define WC 0x10 /* Wall Clock register */
-#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */
-#define WC_SAMPLECOUNTER 0x14060010
-#define WC_CURRENTCHANNEL_MASK 0x0000003F /* Channel [0..63] currently being serviced */
+SUB_REG(WC, SAMPLECOUNTER, 0x03FFFFC0) /* Sample periods elapsed since reset */
+SUB_REG(WC, CURRENTCHANNEL, 0x0000003F) /* Channel [0..63] currently being serviced */
/* NOTE: Each channel takes 1/64th of a sample */
/* period to be serviced. */
-#define WC_CURRENTCHANNEL 0x06000010
#define HCFG 0x14 /* Hardware config register */
/* NOTE: There is no reason to use the legacy */
@@ -225,9 +256,8 @@
/* async audio source */
#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */
/* NOTE: This should generally never be used. */
-#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */
+SUB_REG(HCFG, LOCKTANKCACHE, 0x00000004) /* 1 = Cancel bustmaster accesses to tankcache */
/* NOTE: This should generally never be used. */
-#define HCFG_LOCKTANKCACHE 0x01020014
#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */
/* NOTE: This is a 'cheap' way to implement a */
/* master mute function on the mute button, and */
@@ -381,56 +411,49 @@
// distortion), the modulation engine sets the target registers, towards
// which the current registers "swerve" gradually.
+// For the odd channel in a stereo pair, these registers are meaningless:
+// CPF_STEREO, CPF_CURRENTPITCH, PTRX_PITCHTARGET, CCR_CACHEINVALIDSIZE,
+// PSST_LOOPSTARTADDR, DSL_LOOPENDADDR, CCCA_CURRADDR
+// The somewhat non-obviously still meaningful ones are:
+// CPF_STOP, CPF_FRACADDRESS, CCR_READADDRESS (!),
+// CCCA_INTERPROM, CCCA_8BITSELECT (!)
+// (The envelope engine is ignored here, as stereo matters only for verbatim playback.)
+
#define CPF 0x00 /* Current pitch and fraction register */
-#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */
-#define CPF_CURRENTPITCH 0x10100000
+SUB_REG(CPF, CURRENTPITCH, 0xffff0000) /* Current pitch (linear, 0x4000 == unity pitch shift) */
#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */
-#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */
+SUB_REG(CPF, STOP, 0x00004000) /* 1 = Current pitch forced to 0 */
+ /* Can be set only while matching bit in SOLEx is 1 */
#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */
#define PTRX 0x01 /* Pitch target and send A/B amounts register */
-#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */
-#define PTRX_PITCHTARGET 0x10100001
-#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */
-#define PTRX_FXSENDAMOUNT_A 0x08080001
-#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */
-#define PTRX_FXSENDAMOUNT_B 0x08000001
+SUB_REG(PTRX, PITCHTARGET, 0xffff0000) /* Pitch target of specified channel */
+SUB_REG(PTRX, FXSENDAMOUNT_A, 0x0000ff00) /* Linear level of channel output sent to FX send bus A */
+SUB_REG(PTRX, FXSENDAMOUNT_B, 0x000000ff) /* Linear level of channel output sent to FX send bus B */
+// Note: the volumes are raw multpliers, so real 100% is impossible.
#define CVCF 0x02 /* Current volume and filter cutoff register */
-#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */
-#define CVCF_CURRENTVOL 0x10100002
-#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */
-#define CVCF_CURRENTFILTER 0x10000002
+SUB_REG(CVCF, CURRENTVOL, 0xffff0000) /* Current linear volume of specified channel */
+SUB_REG(CVCF, CURRENTFILTER, 0x0000ffff) /* Current filter cutoff frequency of specified channel */
#define VTFT 0x03 /* Volume target and filter cutoff target register */
-#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */
-#define VTFT_VOLUMETARGET 0x10100003
-#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */
-#define VTFT_FILTERTARGET 0x10000003
+SUB_REG(VTFT, VOLUMETARGET, 0xffff0000) /* Volume target of specified channel */
+SUB_REG(VTFT, FILTERTARGET, 0x0000ffff) /* Filter cutoff target of specified channel */
#define Z1 0x05 /* Filter delay memory 1 register */
#define Z2 0x04 /* Filter delay memory 2 register */
#define PSST 0x06 /* Send C amount and loop start address register */
-#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */
-
-#define PSST_FXSENDAMOUNT_C 0x08180006
-
-#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */
-#define PSST_LOOPSTARTADDR 0x18000006
+SUB_REG(PSST, FXSENDAMOUNT_C, 0xff000000) /* Linear level of channel output sent to FX send bus C */
+SUB_REG(PSST, LOOPSTARTADDR, 0x00ffffff) /* Loop start address of the specified channel */
#define DSL 0x07 /* Send D amount and loop end address register */
-#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */
-
-#define DSL_FXSENDAMOUNT_D 0x08180007
-
-#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */
-#define DSL_LOOPENDADDR 0x18000007
+SUB_REG(DSL, FXSENDAMOUNT_D, 0xff000000) /* Linear level of channel output sent to FX send bus D */
+SUB_REG(DSL, LOOPENDADDR, 0x00ffffff) /* Loop end address of the specified channel */
#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */
-#define CCCA_RESONANCE_MASK 0xf0000000 /* Lowpass filter resonance (Q) height */
-#define CCCA_RESONANCE 0x041c0008
+SUB_REG(CCCA, RESONANCE, 0xf0000000) /* Lowpass filter resonance (Q) height */
#define CCCA_INTERPROM_MASK 0x0e000000 /* Selects passband of interpolation ROM */
/* 1 == full band, 7 == lowpass */
/* ROM 0 is used when pitch shifting downward or less */
@@ -447,27 +470,24 @@
#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */
#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */
/* 8-bit samples are unsigned, 16-bit ones signed */
-#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */
-#define CCCA_CURRADDR 0x18000008
+SUB_REG(CCCA, CURRADDR, 0x00ffffff) /* Current address of the selected channel */
#define CCR 0x09 /* Cache control register */
-#define CCR_CACHEINVALIDSIZE 0x07190009
-#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples before the read address */
+SUB_REG(CCR, CACHEINVALIDSIZE, 0xfe000000) /* Number of invalid samples before the read address */
#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */
#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */
/* Auto-set from CPF_STEREO_MASK */
#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */
/* Auto-set from CCCA_8BITSELECT */
-#define CCR_READADDRESS 0x06100009
-#define CCR_READADDRESS_MASK 0x003f0000 /* Next cached sample to play */
-#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */
+SUB_REG(CCR, READADDRESS, 0x003f0000) /* Next cached sample to play */
+SUB_REG(CCR, LOOPINVALSIZE, 0x0000fe00) /* Number of invalid samples in cache prior to loop */
/* NOTE: This is valid only if CACHELOOPFLAG is set */
#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */
-#define CCR_CACHELOOPADDRHI 0x000000ff /* CLP_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */
+SUB_REG(CCR, CACHELOOPADDRHI, 0x000000ff) /* CLP_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */
#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */
/* NOTE: This register is normally not used */
-#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address low word */
+SUB_REG(CLP, CACHELOOPADDR, 0x0000ffff) /* Cache loop address low word */
#define FXRT 0x0b /* Effects send routing register */
/* NOTE: It is illegal to assign the same routing to */
@@ -537,20 +557,17 @@
#define IP_UNITY 0x0000e000 /* Unity pitch shift */
#define IFATN 0x19 /* Initial filter cutoff and attenuation register */
-#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */
+SUB_REG(IFATN, FILTERCUTOFF, 0x0000ff00) /* Initial filter cutoff frequency in exponential units */
/* 6 most significant bits are semitones */
/* 2 least significant bits are fractions */
-#define IFATN_FILTERCUTOFF 0x08080019
-#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */
-#define IFATN_ATTENUATION 0x08000019
+SUB_REG(IFATN, ATTENUATION, 0x000000ff) /* Initial attenuation in 0.375dB steps */
#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */
-#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */
+SUB_REG(PEFE, PITCHAMOUNT, 0x0000ff00) /* Pitch envlope amount */
/* Signed 2's complement, +/- one octave peak extremes */
-#define PEFE_PITCHAMOUNT 0x0808001a
-#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */
+SUB_REG(PEFE, FILTERAMOUNT, 0x000000ff) /* Filter envlope amount */
/* Signed 2's complement, +/- six octaves peak extremes */
-#define PEFE_FILTERAMOUNT 0x0800001a
+
#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */
#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */
@@ -577,24 +594,22 @@
/* 0x1f: not used */
-#define CD0 0x20 /* Cache data 0 register */
-#define CD1 0x21 /* Cache data 1 register */
-#define CD2 0x22 /* Cache data 2 register */
-#define CD3 0x23 /* Cache data 3 register */
-#define CD4 0x24 /* Cache data 4 register */
-#define CD5 0x25 /* Cache data 5 register */
-#define CD6 0x26 /* Cache data 6 register */
-#define CD7 0x27 /* Cache data 7 register */
-#define CD8 0x28 /* Cache data 8 register */
-#define CD9 0x29 /* Cache data 9 register */
-#define CDA 0x2a /* Cache data A register */
-#define CDB 0x2b /* Cache data B register */
-#define CDC 0x2c /* Cache data C register */
-#define CDD 0x2d /* Cache data D register */
-#define CDE 0x2e /* Cache data E register */
-#define CDF 0x2f /* Cache data F register */
-
-/* 0x30-3f seem to be the same as 0x20-2f */
+// 32 cache registers (== 128 bytes) per channel follow.
+// In stereo mode, the two channels' caches are concatenated into one,
+// and hold the interleaved frames.
+// The cache holds 64 frames, so the upper half is not used in 8-bit mode.
+// All registers mentioned below count in frames.
+// The cache is a ring buffer; CCR_READADDRESS operates modulo 64.
+// The cache is filled from (CCCA_CURRADDR - CCR_CACHEINVALIDSIZE)
+// into (CCR_READADDRESS - CCR_CACHEINVALIDSIZE).
+// The engine has a fetch threshold of 32 bytes, so it tries to keep
+// CCR_CACHEINVALIDSIZE below 8 (16-bit stereo), 16 (16-bit mono,
+// 8-bit stereo), or 32 (8-bit mono). The actual transfers are pretty
+// unpredictable, especially if several voices are running.
+// Frames are consumed at CCR_READADDRESS, which is incremented afterwards,
+// along with CCCA_CURRADDR and CCR_CACHEINVALIDSIZE. This implies that the
+// actual playback position always lags CCCA_CURRADDR by exactly 64 frames.
+#define CD0 0x20 /* Cache data registers 0 .. 0x1f */
#define PTB 0x40 /* Page table base register */
#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */
@@ -695,6 +710,8 @@
#define ADCBS_BUFSIZE_57344 0x0000001e
#define ADCBS_BUFSIZE_65536 0x0000001f
+// On Audigy, the FX send amounts are not applied instantly, but determine
+// targets towards which the following registers swerve gradually.
#define A_CSBA 0x4c /* FX send B & A current amounts */
#define A_CSDC 0x4d /* FX send D & C current amounts */
#define A_CSFE 0x4e /* FX send F & E current amounts */
@@ -755,6 +772,9 @@
#define CLIPL 0x5a /* Channel loop interrupt pending low register */
#define CLIPH 0x5b /* Channel loop interrupt pending high register */
+// These cause CPF_STOP_MASK to be set shortly after CCCA_CURRADDR passes DSL_LOOPENDADDR.
+// Subsequent changes to the address registers don't resume; clearing the bit here or in CPF does.
+// The registers are NOT synchronized; the next serviced channel picks up immediately.
#define SOLEL 0x5c /* Stop on loop enable low register */
#define SOLEH 0x5d /* Stop on loop enable high register */
@@ -793,22 +813,19 @@
#define SRCS_SPDIFRATE_96 0x00080000
#define MICIDX 0x63 /* Microphone recording buffer index register */
-#define MICIDX_MASK 0x0000ffff /* 16-bit value */
-#define MICIDX_IDX 0x10000063
+SUB_REG(MICIDX, IDX, 0x0000ffff)
#define ADCIDX 0x64 /* ADC recording buffer index register */
-#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */
-#define ADCIDX_IDX 0x10000064
+SUB_REG(ADCIDX, IDX, 0x0000ffff)
#define A_ADCIDX 0x63
-#define A_ADCIDX_IDX 0x10000063
+SUB_REG(A_ADCIDX, IDX, 0x0000ffff)
#define A_MICIDX 0x64
-#define A_MICIDX_IDX 0x10000064
+SUB_REG(A_MICIDX, IDX, 0x0000ffff)
#define FXIDX 0x65 /* FX recording buffer index register */
-#define FXIDX_MASK 0x0000ffff /* 16-bit value */
-#define FXIDX_IDX 0x10000065
+SUB_REG(FXIDX, IDX, 0x0000ffff)
/* The 32-bit HLIEx and HLIPx registers all have one bit per channel control/status */
#define HLIEL 0x66 /* Channel half loop interrupt enable low register */
@@ -852,8 +869,8 @@
#define A_SPDIF_44100 0x00000080
#define A_SPDIF_MUTED 0x000000c0
-#define A_I2S_CAPTURE_RATE_MASK 0x00000e00 /* This sets the capture PCM rate, but it is */
-#define A_I2S_CAPTURE_RATE 0x03090076 /* unclear if this sets the ADC rate as well. */
+SUB_REG_NC(A_EHC, A_I2S_CAPTURE_RATE, 0x00000e00) /* This sets the capture PCM rate, but it is */
+ /* unclear if this sets the ADC rate as well. */
#define A_I2S_CAPTURE_48000 0x0
#define A_I2S_CAPTURE_192000 0x1
#define A_I2S_CAPTURE_96000 0x2
@@ -1093,6 +1110,9 @@
#define EMU_DOCK_BOARD_ID0 0x00 /* ID bit 0 */
#define EMU_DOCK_BOARD_ID1 0x03 /* ID bit 1 */
+// The actual code disagrees about the bit width of the registers -
+// the formula used is freq = 0x1770000 / (((X_HI << 5) | X_LO) + 1)
+
#define EMU_HANA_WC_SPDIF_HI 0x28 /* 0xxxxxx 6 bit SPDIF IN Word clock, upper 6 bits */
#define EMU_HANA_WC_SPDIF_LO 0x29 /* 0xxxxxx 6 bit SPDIF IN Word clock, lower 6 bits */
@@ -1189,9 +1209,10 @@
* physical outputs of Hana, or outputs going to Alice2/Tina for capture -
* 16 x EMU_DST_ALICE2_EMU32_X (2x on rev2 boards). Which data is fed into
* a channel depends on the mixer control setting for each destination - see
- * emumixer.c - snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
+ * the register arrays in emumixer.c.
*/
#define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */
+ /* This channel is delayed by one sample. */
#define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */
#define EMU_DST_ALICE2_EMU32_2 0x0001 /* 16 EMU32 channels to Alice2 +0 to +0xf */
#define EMU_DST_ALICE2_EMU32_3 0x0002 /* 16 EMU32 channels to Alice2 +0 to +0xf */
@@ -1422,24 +1443,35 @@
/* 0x600 and 0x700 no used */
+
+/* ------------------- CONSTANTS -------------------- */
+
+extern const char * const snd_emu10k1_fxbus[32];
+extern const char * const snd_emu10k1_sblive_ins[16];
+extern const char * const snd_emu10k1_audigy_ins[16];
+extern const char * const snd_emu10k1_sblive_outs[32];
+extern const char * const snd_emu10k1_audigy_outs[32];
+extern const s8 snd_emu10k1_sblive51_fxbus2_map[16];
+
/* ------------------- STRUCTURES -------------------- */
enum {
+ EMU10K1_UNUSED, // This must be zero
EMU10K1_EFX,
+ EMU10K1_EFX_IRQ,
EMU10K1_PCM,
+ EMU10K1_PCM_IRQ,
EMU10K1_SYNTH,
- EMU10K1_MIDI
+ EMU10K1_NUM_TYPES
};
struct snd_emu10k1;
struct snd_emu10k1_voice {
- int number;
- unsigned int use: 1,
- pcm: 1,
- efx: 1,
- synth: 1,
- midi: 1;
+ unsigned char number;
+ unsigned char use;
+ unsigned char dirty;
+ unsigned char last;
void (*interrupt)(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice);
struct snd_emu10k1_pcm *epcm;
@@ -1461,7 +1493,9 @@ struct snd_emu10k1_pcm {
struct snd_emu10k1_voice *extra;
unsigned short running;
unsigned short first_ptr;
+ snd_pcm_uframes_t resume_pos;
struct snd_util_memblk *memblk;
+ unsigned int pitch_target;
unsigned int start_addr;
unsigned int ccca_start_addr;
unsigned int capture_ipr; /* interrupt acknowledge mask */
@@ -1479,6 +1513,8 @@ struct snd_emu10k1_pcm_mixer {
/* mono, left, right x 8 sends (4 on emu10k1) */
unsigned char send_routing[3][8];
unsigned char send_volume[3][8];
+ // 0x8000 is neutral. The mixer code rescales it to 0xffff to maintain
+ // backwards compatibility with user space.
unsigned short attn[3];
struct snd_emu10k1_pcm *epcm;
};
@@ -1492,6 +1528,9 @@ struct snd_emu10k1_pcm_mixer {
#define snd_emu10k1_compose_audigy_fxrt2(route) \
((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 24))
+#define snd_emu10k1_compose_audigy_sendamounts(vol) \
+(((unsigned int)vol[4] << 24) | ((unsigned int)vol[5] << 16) | ((unsigned int)vol[6] << 8) | (unsigned int)vol[7])
+
struct snd_emu10k1_memblk {
struct snd_util_memblk mem;
/* private part */
@@ -1510,9 +1549,9 @@ struct snd_emu10k1_fx8010_ctl {
unsigned int vcount;
unsigned int count; /* count of GPR (1..16) */
unsigned short gpr[32]; /* GPR number(s) */
- unsigned int value[32];
- unsigned int min; /* minimum range */
- unsigned int max; /* maximum range */
+ int value[32];
+ int min; /* minimum range */
+ int max; /* maximum range */
unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */
struct snd_kcontrol *kcontrol;
};
@@ -1600,35 +1639,43 @@ struct snd_emu_chip_details {
u32 device;
u32 subsystem;
unsigned char revision;
- unsigned char emu10k1_chip; /* Original SB Live. Not SB Live 24bit. */
- /* Redundant with emu10k2_chip being unset. */
- unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
- unsigned char ca0102_chip; /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
- /* Redundant with ca0108_chip being unset. */
- unsigned char ca0108_chip; /* Audigy 2 Value */
- unsigned char ca_cardbus_chip; /* Audigy 2 ZS Notebook */
- unsigned char ca0151_chip; /* P16V */
- unsigned char spk71; /* Has 7.1 speakers */
- unsigned char sblive51; /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
- unsigned char spdif_bug; /* Has Spdif phasing bug */
- unsigned char ac97_chip; /* Has an AC97 chip: 1 = mandatory, 2 = optional */
- unsigned char ecard; /* APS EEPROM */
- unsigned char emu_model; /* EMU model type */
- unsigned char spi_dac; /* SPI interface for DAC; requires ca0108_chip */
- unsigned char i2c_adc; /* I2C interface for ADC; requires ca0108_chip */
- unsigned char adc_1361t; /* Use Philips 1361T ADC */
- unsigned char invert_shared_spdif; /* analog/digital switch inverted */
+ unsigned char emu_model; /* EMU model type */
+ unsigned int emu10k1_chip:1; /* Original SB Live. Not SB Live 24bit. */
+ /* Redundant with emu10k2_chip being unset. */
+ unsigned int emu10k2_chip:1; /* Audigy 1 or Audigy 2. */
+ unsigned int ca0102_chip:1; /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
+ /* Redundant with ca0108_chip being unset. */
+ unsigned int ca0108_chip:1; /* Audigy 2 Value */
+ unsigned int ca_cardbus_chip:1; /* Audigy 2 ZS Notebook */
+ unsigned int ca0151_chip:1; /* P16V */
+ unsigned int spk20:1; /* Stereo only */
+ unsigned int spk71:1; /* Has 7.1 speakers */
+ unsigned int no_adat:1; /* Has no ADAT, only SPDIF */
+ unsigned int sblive51:1; /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
+ unsigned int spdif_bug:1; /* Has Spdif phasing bug */
+ unsigned int ac97_chip:2; /* Has an AC97 chip: 1 = mandatory, 2 = optional */
+ unsigned int ecard:1; /* APS EEPROM */
+ unsigned int spi_dac:1; /* SPI interface for DAC; requires ca0108_chip */
+ unsigned int i2c_adc:1; /* I2C interface for ADC; requires ca0108_chip */
+ unsigned int adc_1361t:1; /* Use Philips 1361T ADC */
+ unsigned int invert_shared_spdif:1; /* analog/digital switch inverted */
const char *driver;
const char *name;
const char *id; /* for backward compatibility - can be NULL if not needed */
};
+#define NUM_OUTPUT_DESTS 28
+#define NUM_INPUT_DESTS 22
+
struct snd_emu1010 {
- unsigned int output_source[64];
- unsigned int input_source[64];
+ unsigned char output_source[NUM_OUTPUT_DESTS];
+ unsigned char input_source[NUM_INPUT_DESTS];
unsigned int adc_pads; /* bit mask */
unsigned int dac_pads; /* bit mask */
- unsigned int internal_clock; /* 44100 or 48000 */
+ unsigned int wclock; /* Cached register value */
+ unsigned int word_clock; /* Cached effective value */
+ unsigned int clock_source;
+ unsigned int clock_fallback;
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
struct delayed_work firmware_work;
@@ -1653,7 +1700,6 @@ struct snd_emu10k1 {
unsigned int address_mode; /* address mode */
unsigned long dma_mask; /* PCI DMA mask */
bool iommu_workaround; /* IOMMU workaround needed */
- unsigned int delay_pcm_irq; /* in samples */
int max_cache_pages; /* max memory size / PAGE_SIZE */
struct snd_dma_buffer silent_page; /* silent page */
struct snd_dma_buffer ptb_pages; /* page table pages */
@@ -1775,6 +1821,7 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu);
/* I/O functions */
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn);
void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
+void snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...);
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn);
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
@@ -1782,6 +1829,9 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value);
void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src);
+u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst);
+int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src);
+void snd_emu1010_update_clock(struct snd_emu10k1 *emu);
unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
@@ -1791,13 +1841,17 @@ void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum);
void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum);
void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum);
void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum);
+#if 0
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum);
void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum);
+#endif
+void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices);
+void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices);
+int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices);
void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait);
static inline unsigned int snd_emu10k1_wc(struct snd_emu10k1 *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; }
unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data);
-unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate);
#ifdef CONFIG_PM_SLEEP
void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu);
@@ -1825,7 +1879,8 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me
int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk);
/* voice allocation */
-int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int pair, 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);
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice);
/* MIDI uart */
diff --git a/include/sound/emux_synth.h b/include/sound/emux_synth.h
index d499b68122a3..1cc530434b97 100644
--- a/include/sound/emux_synth.h
+++ b/include/sound/emux_synth.h
@@ -54,6 +54,7 @@ struct snd_emux_operators {
#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
int (*oss_ioctl)(struct snd_emux *emu, int cmd, int p1, int p2);
#endif
+ int (*get_pitch_shift)(struct snd_emux *emu);
};
@@ -82,7 +83,6 @@ struct snd_emux {
int max_voices; /* Number of voices */
int mem_size; /* memory size (in byte) */
int num_ports; /* number of ports to be created */
- int pitch_shift; /* pitch shift value (for Emu10k1) */
struct snd_emux_operators ops; /* operators */
void *hw; /* hardware */
unsigned long flags; /* other conditions */
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index 97f09acae302..2ffdf58bd6d4 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -347,6 +347,8 @@ struct hdac_bus {
bool corbrp_self_clear:1; /* CORBRP clears itself after reset */
bool polling_mode:1;
bool needs_damn_long_delay:1;
+ bool not_use_interrupts:1; /* prohibiting the RIRB IRQ */
+ bool access_sdnctl_in_dword:1; /* accessing the sdnctl register by dword */
int poll_count;
diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index e1f59b2940af..b0197b1d1fe4 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -18,6 +18,7 @@
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
#include <sound/seq_device.h>
#endif
+#include <sound/info.h>
/*
* Raw MIDI interface
@@ -47,6 +48,10 @@ struct snd_rawmidi_global_ops {
int (*dev_unregister) (struct snd_rawmidi * rmidi);
void (*get_port_info)(struct snd_rawmidi *rmidi, int number,
struct snd_seq_port_info *info);
+ long (*ioctl)(struct snd_rawmidi *rmidi, unsigned int cmd,
+ void __user *argp);
+ void (*proc_read)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buf);
};
struct snd_rawmidi_runtime {
@@ -61,6 +66,7 @@ struct snd_rawmidi_runtime {
size_t avail_min; /* min avail for wakeup */
size_t avail; /* max used buffer for wakeup */
size_t xruns; /* over/underruns counter */
+ size_t align; /* alignment (0 = byte stream, 3 = UMP) */
int buffer_ref; /* buffer reference count */
/* misc */
wait_queue_head_t sleep;
@@ -146,6 +152,13 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
const struct snd_rawmidi_ops *ops);
+/* internal */
+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);
+int snd_rawmidi_free(struct snd_rawmidi *rmidi);
+
/* callbacks */
int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
@@ -161,7 +174,7 @@ int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream);
/* main midi functions */
int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info);
-int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
+int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
int mode, struct snd_rawmidi_file *rfile);
int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile);
int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h
index 8899affe9155..dead74b022f4 100644
--- a/include/sound/seq_device.h
+++ b/include/sound/seq_device.h
@@ -78,5 +78,6 @@ void snd_seq_driver_unregister(struct snd_seq_driver *drv);
*/
#define SNDRV_SEQ_DEV_ID_MIDISYNTH "seq-midi"
#define SNDRV_SEQ_DEV_ID_OPL3 "opl3-synth"
+#define SNDRV_SEQ_DEV_ID_UMP "seq-ump-client"
#endif /* __SOUND_SEQ_DEVICE_H */
diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h
index 658911926f3a..c8621671fa70 100644
--- a/include/sound/seq_kernel.h
+++ b/include/sound/seq_kernel.h
@@ -70,9 +70,19 @@ int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg);
typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count);
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
int in_kernel, int size_aligned);
+int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
+ char *buf, int offset);
int snd_seq_dump_var_event(const struct snd_seq_event *event,
snd_seq_dump_func_t func, void *private_data);
+/* size of the event packet; it can be greater than snd_seq_event size */
+static inline size_t snd_seq_event_packet_size(struct snd_seq_event *ev)
+{
+ if (snd_seq_ev_is_ump(ev))
+ return sizeof(struct snd_seq_ump_event);
+ return sizeof(struct snd_seq_event);
+}
+
/* interface for OSS emulation */
int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo);
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index a3f3f3aa9e6e..b450d5873227 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -59,9 +59,6 @@ struct asoc_simple_priv {
struct simple_dai_props {
struct asoc_simple_dai *cpu_dai;
struct asoc_simple_dai *codec_dai;
- struct snd_soc_dai_link_component *cpus;
- struct snd_soc_dai_link_component *codecs;
- struct snd_soc_dai_link_component *platforms;
struct asoc_simple_data adata;
struct snd_soc_codec_conf *codec_conf;
struct prop_nums num;
@@ -73,7 +70,6 @@ struct asoc_simple_priv {
struct snd_soc_dai_link *dai_link;
struct asoc_simple_dai *dais;
struct snd_soc_dai_link_component *dlcs;
- struct snd_soc_dai_link_component dummy;
struct snd_soc_codec_conf *codec_conf;
struct gpio_desc *pa_gpio;
const struct snd_soc_ops *ops;
@@ -196,6 +192,9 @@ int asoc_simple_remove(struct platform_device *pdev);
int asoc_graph_card_probe(struct snd_soc_card *card);
int asoc_graph_is_ports0(struct device_node *port);
+int asoc_graph_parse_dai(struct device_node *ep,
+ struct snd_soc_dai_link_component *dlc,
+ int *is_single_link);
#ifdef DEBUG
static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv,
diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h
index 82a7db23db69..e49b97d9e3ff 100644
--- a/include/sound/soc-acpi-intel-match.h
+++ b/include/sound/soc-acpi-intel-match.h
@@ -31,6 +31,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[];
@@ -40,6 +41,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[];
/*
* generic table used for HDA codec-based platforms, possibly with
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index 0814ed143864..87f248a06271 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -159,6 +159,15 @@ struct snd_soc_component_driver {
int remove_order;
/*
+ * soc_pcm_trigger() start/stop sequence.
+ * see also
+ * snd_soc_dai_link
+ * soc_pcm_trigger()
+ */
+ enum snd_soc_trigger_order trigger_start;
+ enum snd_soc_trigger_order trigger_stop;
+
+ /*
* signal if the module handling the component should not be removed
* if a pcm is open. Setting this would prevent the module
* refcount being incremented in probe() but allow it be incremented
@@ -190,8 +199,6 @@ struct snd_soc_component_driver {
bool use_dai_pcm_id; /* use DAI link PCM ID as PCM device number */
int be_pcm_base; /* base device ID for all BE PCMs */
- unsigned int start_dma_last;
-
#ifdef CONFIG_DEBUG_FS
const char *debugfs_prefix;
#endif
@@ -454,6 +461,10 @@ int snd_soc_component_force_enable_pin_unlocked(
struct snd_soc_component *component,
const char *pin);
+/* component controls */
+int snd_soc_component_notify_control(struct snd_soc_component *component,
+ const char * const ctl);
+
/* component driver ops */
int snd_soc_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 05004c048dd5..b27f84580c5b 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -607,6 +607,14 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+enum snd_soc_trigger_order {
+ /* start stop */
+ SND_SOC_TRIGGER_ORDER_DEFAULT = 0, /* Link->Component->DAI DAI->Component->Link */
+ SND_SOC_TRIGGER_ORDER_LDC, /* Link->DAI->Component Component->DAI->Link */
+
+ SND_SOC_TRIGGER_ORDER_MAX,
+};
+
/* SoC PCM stream information */
struct snd_soc_pcm_stream {
const char *stream_name;
@@ -633,7 +641,6 @@ struct snd_soc_compr_ops {
int (*startup)(struct snd_compr_stream *);
void (*shutdown)(struct snd_compr_stream *);
int (*set_params)(struct snd_compr_stream *);
- int (*trigger)(struct snd_compr_stream *);
};
struct snd_soc_component*
@@ -646,6 +653,11 @@ struct snd_soc_dai_link_component {
const char *dai_name;
};
+struct snd_soc_dai_link_codec_ch_map {
+ unsigned int connected_cpu_id;
+ unsigned int ch_mask;
+};
+
struct snd_soc_dai_link {
/* config - must be set by machine driver */
const char *name; /* Codec name */
@@ -674,6 +686,7 @@ struct snd_soc_dai_link {
struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs;
+ struct snd_soc_dai_link_codec_ch_map *codec_ch_maps;
/*
* You MAY specify the link's platform/PCM/DMA driver, either by
* device name, or by DT/OF node, but not both. Some forms of link
@@ -708,6 +721,15 @@ struct snd_soc_dai_link {
const struct snd_soc_ops *ops;
const struct snd_soc_compr_ops *compr_ops;
+ /*
+ * soc_pcm_trigger() start/stop sequence.
+ * see also
+ * snd_soc_component_driver
+ * soc_pcm_trigger()
+ */
+ enum snd_soc_trigger_order trigger_start;
+ enum snd_soc_trigger_order trigger_stop;
+
/* Mark this pcm with non atomic ops */
unsigned int nonatomic:1;
@@ -746,12 +768,6 @@ struct snd_soc_dai_link {
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int ignore:1;
- /* This flag will reorder stop sequence. By enabling this flag
- * DMA controller stop sequence will be invoked first followed by
- * CPU DAI driver stop sequence
- */
- unsigned int stop_dma_first:1;
-
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj; /* For topology */
#endif
@@ -878,6 +894,7 @@ asoc_link_to_platform(struct snd_soc_dai_link *link, int n) {
#define COMP_DUMMY() { .name = "snd-soc-dummy", .dai_name = "snd-soc-dummy-dai", }
extern struct snd_soc_dai_link_component null_dailink_component[0];
+extern struct snd_soc_dai_link_component asoc_dummy_dlc;
struct snd_soc_codec_conf {
@@ -1291,11 +1308,18 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np,
snd_soc_daifmt_clock_provider_from_bitmap( \
snd_soc_daifmt_parse_clock_provider_as_bitmap(np, prefix))
+int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream);
+int snd_soc_get_dlc(const struct of_phandle_args *args,
+ struct snd_soc_dai_link_component *dlc);
+int snd_soc_of_get_dlc(struct device_node *of_node,
+ struct of_phandle_args *args,
+ struct snd_soc_dai_link_component *dlc,
+ int index);
int snd_soc_get_dai_id(struct device_node *ep);
int snd_soc_get_dai_name(const struct of_phandle_args *args,
const char **dai_name);
int snd_soc_of_get_dai_name(struct device_node *of_node,
- const char **dai_name);
+ const char **dai_name, int index);
int snd_soc_of_get_dai_link_codecs(struct device *dev,
struct device_node *of_node,
struct snd_soc_dai_link *dai_link);
diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h
new file mode 100644
index 000000000000..bd1b72bf47a5
--- /dev/null
+++ b/include/sound/tas2781-dsp.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+//
+
+#ifndef __TASDEVICE_DSP_H__
+#define __TASDEVICE_DSP_H__
+
+#define MAIN_ALL_DEVICES 0x0d
+#define MAIN_DEVICE_A 0x01
+#define MAIN_DEVICE_B 0x08
+#define MAIN_DEVICE_C 0x10
+#define MAIN_DEVICE_D 0x14
+#define COEFF_DEVICE_A 0x03
+#define COEFF_DEVICE_B 0x0a
+#define COEFF_DEVICE_C 0x11
+#define COEFF_DEVICE_D 0x15
+#define PRE_DEVICE_A 0x04
+#define PRE_DEVICE_B 0x0b
+#define PRE_DEVICE_C 0x12
+#define PRE_DEVICE_D 0x16
+
+#define PPC3_VERSION 0x4100
+#define PPC3_VERSION_TAS2781 0x14600
+#define TASDEVICE_DEVICE_SUM 8
+#define TASDEVICE_CONFIG_SUM 64
+
+#define TASDEVICE_MAX_CHANNELS 8
+
+enum tasdevice_dsp_dev_idx {
+ TASDEVICE_DSP_TAS_2555 = 0,
+ TASDEVICE_DSP_TAS_2555_STEREO,
+ TASDEVICE_DSP_TAS_2557_MONO,
+ TASDEVICE_DSP_TAS_2557_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2559,
+ TASDEVICE_DSP_TAS_2563,
+ TASDEVICE_DSP_TAS_2563_DUAL_MONO = 7,
+ TASDEVICE_DSP_TAS_2563_QUAD,
+ TASDEVICE_DSP_TAS_2563_21,
+ TASDEVICE_DSP_TAS_2781,
+ TASDEVICE_DSP_TAS_2781_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2781_21,
+ TASDEVICE_DSP_TAS_2781_QUAD,
+ TASDEVICE_DSP_TAS_MAX_DEVICE
+};
+
+struct tasdevice_fw_fixed_hdr {
+ unsigned int fwsize;
+ unsigned int ppcver;
+ unsigned int drv_ver;
+};
+
+struct tasdevice_dspfw_hdr {
+ struct tasdevice_fw_fixed_hdr fixed_hdr;
+ unsigned short device_family;
+ unsigned short device;
+ unsigned char ndev;
+};
+
+struct tasdev_blk {
+ int nr_retry;
+ unsigned int type;
+ unsigned char is_pchksum_present;
+ unsigned char pchksum;
+ unsigned char is_ychksum_present;
+ unsigned char ychksum;
+ unsigned int nr_cmds;
+ unsigned int blk_size;
+ unsigned int nr_subblocks;
+ unsigned char *data;
+};
+
+struct tasdevice_data {
+ char name[64];
+ unsigned int nr_blk;
+ struct tasdev_blk *dev_blks;
+};
+
+struct tasdevice_prog {
+ unsigned int prog_size;
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_config {
+ unsigned int cfg_size;
+ char name[64];
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_calibration {
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_fw {
+ struct tasdevice_dspfw_hdr fw_hdr;
+ unsigned short nr_programs;
+ struct tasdevice_prog *programs;
+ unsigned short nr_configurations;
+ struct tasdevice_config *configs;
+ unsigned short nr_calibrations;
+ struct tasdevice_calibration *calibrations;
+ struct device *dev;
+};
+
+enum tasdevice_dsp_fw_state {
+ TASDEVICE_DSP_FW_NONE = 0,
+ TASDEVICE_DSP_FW_PENDING,
+ TASDEVICE_DSP_FW_FAIL,
+ TASDEVICE_DSP_FW_ALL_OK,
+};
+
+enum tasdevice_bin_blk_type {
+ TASDEVICE_BIN_BLK_COEFF = 1,
+ TASDEVICE_BIN_BLK_POST_POWER_UP,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP,
+ TASDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+struct tasdevice_rca_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[TASDEVICE_DEVICE_SUM];
+ unsigned int nconfig;
+ unsigned int config_size[TASDEVICE_CONFIG_SUM];
+};
+
+struct tasdev_blk_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 tasdevice_config_info {
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct tasdev_blk_data **blk_data;
+};
+
+struct tasdevice_rca {
+ struct tasdevice_rca_hdr fw_hdr;
+ int ncfgs;
+ struct tasdevice_config_info **cfg_info;
+ int profile_cfg_id;
+};
+
+void tasdevice_select_cfg_blk(void *context, int conf_no,
+ unsigned char block_type);
+void tasdevice_config_info_remove(void *context);
+void tasdevice_dsp_remove(void *context);
+int tasdevice_dsp_parser(void *context);
+int tasdevice_rca_parser(void *context, const struct firmware *fmw);
+void tasdevice_dsp_remove(void *context);
+void tasdevice_calbin_remove(void *context);
+int tasdevice_select_tuningprm_cfg(void *context, int prm,
+ int cfg_no, int rca_conf_no);
+int tasdevice_prmg_load(void *context, int prm_no);
+int tasdevice_prmg_calibdata_load(void *context, int prm_no);
+void tasdevice_tuning_switch(void *context, int state);
+int tas2781_load_calibration(void *context, char *file_name,
+ unsigned short i);
+
+#endif
diff --git a/include/sound/tas2781-tlv.h b/include/sound/tas2781-tlv.h
new file mode 100644
index 000000000000..4038dd421150
--- /dev/null
+++ b/include/sound/tas2781-tlv.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#ifndef __TAS2781_TLV_H__
+#define __TAS2781_TLV_H__
+
+static const DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0);
+
+#endif
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
new file mode 100644
index 000000000000..a6c808b22318
--- /dev/null
+++ b/include/sound/tas2781.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+//
+
+#ifndef __TAS2781_H__
+#define __TAS2781_H__
+
+#include "tas2781-dsp.h"
+
+/* version number */
+#define TAS2781_DRV_VER 1
+#define SMARTAMP_MODULE_NAME "tas2781"
+#define TAS2781_GLOBAL_ADDR 0x40
+#define TASDEVICE_RATES (SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_88200)
+
+#define TASDEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/*PAGE Control Register (available in page0 of each book) */
+#define TASDEVICE_PAGE_SELECT 0x00
+#define TASDEVICE_BOOKCTL_PAGE 0x00
+#define TASDEVICE_BOOKCTL_REG 127
+#define TASDEVICE_BOOK_ID(reg) (reg / (256 * 128))
+#define TASDEVICE_PAGE_ID(reg) ((reg % (256 * 128)) / 128)
+#define TASDEVICE_PAGE_REG(reg) ((reg % (256 * 128)) % 128)
+#define TASDEVICE_PGRG(reg) (reg % (256 * 128))
+#define TASDEVICE_REG(book, page, reg) (((book * 256 * 128) + \
+ (page * 128)) + reg)
+
+/*Software Reset */
+#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01)
+#define TAS2781_REG_SWRESET_RESET BIT(0)
+
+/*I2C Checksum */
+#define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E)
+
+/* Volume control */
+#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A)
+#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+enum audio_device {
+ TAS2781 = 0,
+};
+
+enum device_catlog_id {
+ LENOVO = 0,
+ OTHERS
+};
+
+struct tasdevice {
+ struct tasdevice_fw *cali_data_fmw;
+ unsigned int dev_addr;
+ unsigned int err_code;
+ unsigned char cur_book;
+ short cur_prog;
+ short cur_conf;
+ bool is_loading;
+ bool is_loaderr;
+};
+
+struct tasdevice_irqinfo {
+ int irq_gpio;
+ int irq;
+};
+
+struct calidata {
+ unsigned char *data;
+ unsigned long total_sz;
+};
+
+struct tasdevice_priv {
+ struct tasdevice tasdevice[TASDEVICE_MAX_CHANNELS];
+ struct tasdevice_irqinfo irq_info;
+ struct tasdevice_rca rcabin;
+ struct calidata cali_data;
+ struct tasdevice_fw *fmw;
+ struct gpio_desc *reset;
+ struct mutex codec_lock;
+ struct regmap *regmap;
+ struct device *dev;
+ struct tm tm;
+
+ enum device_catlog_id catlog_id;
+ const char *acpi_subsystem_id;
+ unsigned char cal_binaryname[TASDEVICE_MAX_CHANNELS][64];
+ unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
+ unsigned char coef_binaryname[64];
+ unsigned char rca_binaryname[64];
+ unsigned char dev_name[32];
+ unsigned char ndev;
+ unsigned int magic_num;
+ unsigned int chip_id;
+ unsigned int sysclk;
+
+ int cur_prog;
+ int cur_conf;
+ int fw_state;
+ int index;
+ void *client;
+ void *codec;
+ bool force_fwload_status;
+ bool playback_started;
+ bool isacpi;
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block);
+};
+
+void tas2781_reset(struct tasdevice_priv *tas_dev);
+int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+ void (*cont)(const struct firmware *fw, void *context));
+struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c);
+int tasdevice_init(struct tasdevice_priv *tas_priv);
+void tasdevice_remove(struct tasdevice_priv *tas_priv);
+int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int *value);
+int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int value);
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tas_priv, unsigned short chn,
+ unsigned int reg, unsigned char *p_data, unsigned int n_length);
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tasdevice, unsigned short chn,
+ unsigned int reg, unsigned int mask, unsigned int value);
+int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+
+#endif /* __TAS2781_H__ */
diff --git a/include/sound/ump.h b/include/sound/ump.h
new file mode 100644
index 000000000000..44d2c2fd021d
--- /dev/null
+++ b/include/sound/ump.h
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Universal MIDI Packet (UMP) Support
+ */
+#ifndef __SOUND_UMP_H
+#define __SOUND_UMP_H
+
+#include <sound/rawmidi.h>
+
+struct snd_ump_endpoint;
+struct snd_ump_block;
+struct snd_ump_ops;
+struct ump_cvt_to_ump;
+struct snd_seq_ump_ops;
+
+struct snd_ump_endpoint {
+ struct snd_rawmidi core; /* raw UMP access */
+
+ struct snd_ump_endpoint_info info;
+
+ const struct snd_ump_ops *ops; /* UMP ops set by the driver */
+ struct snd_rawmidi_substream *substreams[2]; /* opened substreams */
+
+ void *private_data;
+ void (*private_free)(struct snd_ump_endpoint *ump);
+
+ /* UMP Stream message processing */
+ u32 stream_wait_for; /* expected stream message status */
+ bool stream_finished; /* set when message has been processed */
+ bool parsed; /* UMP / FB parse finished? */
+ bool no_process_stream; /* suppress UMP stream messages handling */
+ wait_queue_head_t stream_wait;
+ struct snd_rawmidi_file stream_rfile;
+
+ struct list_head block_list; /* list of snd_ump_block objects */
+
+ /* intermediate buffer for UMP input */
+ u32 input_buf[4];
+ int input_buf_head;
+ int input_pending;
+
+ struct mutex open_mutex;
+
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ spinlock_t legacy_locks[2];
+ struct snd_rawmidi *legacy_rmidi;
+ struct snd_rawmidi_substream *legacy_substreams[2][SNDRV_UMP_MAX_GROUPS];
+
+ /* for legacy output; need to open the actual substream unlike input */
+ int legacy_out_opens;
+ struct snd_rawmidi_file legacy_out_rfile;
+ struct ump_cvt_to_ump *out_cvts;
+#endif
+
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ struct snd_seq_device *seq_dev;
+ const struct snd_seq_ump_ops *seq_ops;
+ void *seq_client;
+#endif
+};
+
+/* ops filled by UMP drivers */
+struct snd_ump_ops {
+ int (*open)(struct snd_ump_endpoint *ump, int dir);
+ void (*close)(struct snd_ump_endpoint *ump, int dir);
+ void (*trigger)(struct snd_ump_endpoint *ump, int dir, int up);
+ void (*drain)(struct snd_ump_endpoint *ump, int dir);
+};
+
+/* ops filled by sequencer binding */
+struct snd_seq_ump_ops {
+ void (*input_receive)(struct snd_ump_endpoint *ump,
+ const u32 *data, int words);
+ int (*notify_fb_change)(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb);
+ int (*switch_protocol)(struct snd_ump_endpoint *ump);
+};
+
+struct snd_ump_block {
+ struct snd_ump_block_info info;
+ struct snd_ump_endpoint *ump;
+
+ void *private_data;
+ void (*private_free)(struct snd_ump_block *blk);
+
+ struct list_head list;
+};
+
+#define rawmidi_to_ump(rmidi) container_of(rmidi, struct snd_ump_endpoint, core)
+
+int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
+ int output, int input,
+ struct snd_ump_endpoint **ump_ret);
+int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump);
+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);
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count);
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count);
+
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
+ char *id, int device);
+#else
+static inline int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
+ char *id, int device)
+{
+ return 0;
+}
+#endif
+
+int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val);
+int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol);
+
+/*
+ * Some definitions for UMP
+ */
+
+/* MIDI 2.0 Message Type */
+enum {
+ UMP_MSG_TYPE_UTILITY = 0x00,
+ UMP_MSG_TYPE_SYSTEM = 0x01,
+ UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE = 0x02,
+ UMP_MSG_TYPE_DATA = 0x03,
+ UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE = 0x04,
+ UMP_MSG_TYPE_EXTENDED_DATA = 0x05,
+ UMP_MSG_TYPE_FLEX_DATA = 0x0d,
+ UMP_MSG_TYPE_STREAM = 0x0f,
+};
+
+/* MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */
+enum {
+ UMP_SYSEX_STATUS_SINGLE = 0,
+ UMP_SYSEX_STATUS_START = 1,
+ UMP_SYSEX_STATUS_CONTINUE = 2,
+ UMP_SYSEX_STATUS_END = 3,
+};
+
+/* UMP Utility Type Status (type 0x0) */
+enum {
+ UMP_UTILITY_MSG_STATUS_NOOP = 0x00,
+ UMP_UTILITY_MSG_STATUS_JR_CLOCK = 0x01,
+ UMP_UTILITY_MSG_STATUS_JR_TSTAMP = 0x02,
+ UMP_UTILITY_MSG_STATUS_DCTPQ = 0x03,
+ UMP_UTILITY_MSG_STATUS_DC = 0x04,
+};
+
+/* UMP Stream Message Status (type 0xf) */
+enum {
+ UMP_STREAM_MSG_STATUS_EP_DISCOVERY = 0x00,
+ UMP_STREAM_MSG_STATUS_EP_INFO = 0x01,
+ UMP_STREAM_MSG_STATUS_DEVICE_INFO = 0x02,
+ UMP_STREAM_MSG_STATUS_EP_NAME = 0x03,
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID = 0x04,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST = 0x05,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG = 0x06,
+ UMP_STREAM_MSG_STATUS_FB_DISCOVERY = 0x10,
+ UMP_STREAM_MSG_STATUS_FB_INFO = 0x11,
+ UMP_STREAM_MSG_STATUS_FB_NAME = 0x12,
+ UMP_STREAM_MSG_STATUS_START_CLIP = 0x20,
+ UMP_STREAM_MSG_STATUS_END_CLIP = 0x21,
+};
+
+/* UMP Endpoint Discovery filter bitmap */
+enum {
+ UMP_STREAM_MSG_REQUEST_EP_INFO = (1U << 0),
+ UMP_STREAM_MSG_REQUEST_DEVICE_INFO = (1U << 1),
+ UMP_STREAM_MSG_REQUEST_EP_NAME = (1U << 2),
+ UMP_STREAM_MSG_REQUEST_PRODUCT_ID = (1U << 3),
+ UMP_STREAM_MSG_REQUEST_STREAM_CFG = (1U << 4),
+};
+
+/* UMP Function Block Discovery filter bitmap */
+enum {
+ UMP_STREAM_MSG_REQUEST_FB_INFO = (1U << 0),
+ UMP_STREAM_MSG_REQUEST_FB_NAME = (1U << 1),
+};
+
+/* UMP Endpoint Info capability bits (used for protocol request/notify, too) */
+enum {
+ UMP_STREAM_MSG_EP_INFO_CAP_TXJR = (1U << 0), /* Sending JRTS */
+ UMP_STREAM_MSG_EP_INFO_CAP_RXJR = (1U << 1), /* Receiving JRTS */
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 = (1U << 8), /* MIDI 1.0 */
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 = (1U << 9), /* MIDI 2.0 */
+};
+
+/* UMP EP / FB name string format; same as SysEx string handling */
+enum {
+ UMP_STREAM_MSG_FORMAT_SINGLE = 0,
+ UMP_STREAM_MSG_FORMAT_START = 1,
+ UMP_STREAM_MSG_FORMAT_CONTINUE = 2,
+ UMP_STREAM_MSG_FORMAT_END = 3,
+};
+
+/*
+ * Helpers for retrieving / filling bits from UMP
+ */
+/* get the message type (4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_type(u32 data)
+{
+ return data >> 28;
+}
+
+/* get the group number (0-based, 4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_group(u32 data)
+{
+ return (data >> 24) & 0x0f;
+}
+
+/* get the MIDI status code (4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_status_code(u32 data)
+{
+ return (data >> 20) & 0x0f;
+}
+
+/* get the MIDI channel number (0-based, 4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_channel(u32 data)
+{
+ return (data >> 16) & 0x0f;
+}
+
+/* get the MIDI status + channel combo byte (8bit) from a UMP packet (header) */
+static inline unsigned char ump_message_status_channel(u32 data)
+{
+ return (data >> 16) & 0xff;
+}
+
+/* compose a UMP packet (header) from type, group and status values */
+static inline u32 ump_compose(unsigned char type, unsigned char group,
+ unsigned char status, unsigned char channel)
+{
+ return ((u32)type << 28) | ((u32)group << 24) | ((u32)status << 20) |
+ ((u32)channel << 16);
+}
+
+/* get SysEx message status (for both 7 and 8bits) from a UMP packet (header) */
+static inline unsigned char ump_sysex_message_status(u32 data)
+{
+ return (data >> 20) & 0xf;
+}
+
+/* get SysEx message length (for both 7 and 8bits) from a UMP packet (header) */
+static inline unsigned char ump_sysex_message_length(u32 data)
+{
+ return (data >> 16) & 0xf;
+}
+
+/* For Stream Messages */
+static inline unsigned char ump_stream_message_format(u32 data)
+{
+ return (data >> 26) & 0x03;
+}
+
+static inline unsigned int ump_stream_message_status(u32 data)
+{
+ return (data >> 16) & 0x3ff;
+}
+
+static inline u32 ump_stream_compose(unsigned char status, unsigned short form)
+{
+ return (UMP_MSG_TYPE_STREAM << 28) | ((u32)form << 26) |
+ ((u32)status << 16);
+}
+
+#define ump_is_groupless_msg(type) \
+ ((type) == UMP_MSG_TYPE_UTILITY || (type) == UMP_MSG_TYPE_STREAM)
+
+#endif /* __SOUND_UMP_H */
diff --git a/include/sound/ump_convert.h b/include/sound/ump_convert.h
new file mode 100644
index 000000000000..28c364c63245
--- /dev/null
+++ b/include/sound/ump_convert.h
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef __SOUND_UMP_CONVERT_H
+#define __SOUND_UMP_CONVERT_H
+
+#include <sound/ump_msg.h>
+
+/* context for converting from legacy control messages to UMP packet */
+struct ump_cvt_to_ump_bank {
+ bool rpn_set;
+ bool nrpn_set;
+ bool bank_set;
+ unsigned char cc_rpn_msb, cc_rpn_lsb;
+ unsigned char cc_nrpn_msb, cc_nrpn_lsb;
+ unsigned char cc_data_msb, cc_data_lsb;
+ unsigned char cc_bank_msb, cc_bank_lsb;
+};
+
+/* context for converting from MIDI1 byte stream to UMP packet */
+struct ump_cvt_to_ump {
+ /* MIDI1 intermediate buffer */
+ unsigned char buf[4];
+ int len;
+ int cmd_bytes;
+
+ /* UMP output packet */
+ u32 ump[4];
+ int ump_bytes;
+
+ /* various status */
+ unsigned int in_sysex;
+ struct ump_cvt_to_ump_bank bank[16]; /* per channel */
+};
+
+int snd_ump_convert_from_ump(const u32 *data, unsigned char *dst,
+ unsigned char *group_ret);
+void snd_ump_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
+ unsigned int protocol, unsigned char c);
+
+/* reset the converter context, called at each open to ump */
+static inline void snd_ump_convert_reset(struct ump_cvt_to_ump *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+}
+
+#endif /* __SOUND_UMP_CONVERT_H */
diff --git a/include/sound/ump_msg.h b/include/sound/ump_msg.h
new file mode 100644
index 000000000000..72f60ddfea75
--- /dev/null
+++ b/include/sound/ump_msg.h
@@ -0,0 +1,765 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal MIDI Packet (UMP): Message Definitions
+ */
+#ifndef __SOUND_UMP_MSG_H
+#define __SOUND_UMP_MSG_H
+
+/* MIDI 1.0 / 2.0 Status Code (4bit) */
+enum {
+ UMP_MSG_STATUS_PER_NOTE_RCC = 0x0,
+ UMP_MSG_STATUS_PER_NOTE_ACC = 0x1,
+ UMP_MSG_STATUS_RPN = 0x2,
+ UMP_MSG_STATUS_NRPN = 0x3,
+ UMP_MSG_STATUS_RELATIVE_RPN = 0x4,
+ UMP_MSG_STATUS_RELATIVE_NRPN = 0x5,
+ UMP_MSG_STATUS_PER_NOTE_PITCH_BEND = 0x6,
+ UMP_MSG_STATUS_NOTE_OFF = 0x8,
+ UMP_MSG_STATUS_NOTE_ON = 0x9,
+ UMP_MSG_STATUS_POLY_PRESSURE = 0xa,
+ UMP_MSG_STATUS_CC = 0xb,
+ UMP_MSG_STATUS_PROGRAM = 0xc,
+ UMP_MSG_STATUS_CHANNEL_PRESSURE = 0xd,
+ UMP_MSG_STATUS_PITCH_BEND = 0xe,
+ UMP_MSG_STATUS_PER_NOTE_MGMT = 0xf,
+};
+
+/* MIDI 1.0 Channel Control (7bit) */
+enum {
+ UMP_CC_BANK_SELECT = 0,
+ UMP_CC_MODULATION = 1,
+ UMP_CC_BREATH = 2,
+ UMP_CC_FOOT = 4,
+ UMP_CC_PORTAMENTO_TIME = 5,
+ UMP_CC_DATA = 6,
+ UMP_CC_VOLUME = 7,
+ UMP_CC_BALANCE = 8,
+ UMP_CC_PAN = 10,
+ UMP_CC_EXPRESSION = 11,
+ UMP_CC_EFFECT_CONTROL_1 = 12,
+ UMP_CC_EFFECT_CONTROL_2 = 13,
+ UMP_CC_GP_1 = 16,
+ UMP_CC_GP_2 = 17,
+ UMP_CC_GP_3 = 18,
+ UMP_CC_GP_4 = 19,
+ UMP_CC_BANK_SELECT_LSB = 32,
+ UMP_CC_MODULATION_LSB = 33,
+ UMP_CC_BREATH_LSB = 34,
+ UMP_CC_FOOT_LSB = 36,
+ UMP_CC_PORTAMENTO_TIME_LSB = 37,
+ UMP_CC_DATA_LSB = 38,
+ UMP_CC_VOLUME_LSB = 39,
+ UMP_CC_BALANCE_LSB = 40,
+ UMP_CC_PAN_LSB = 42,
+ UMP_CC_EXPRESSION_LSB = 43,
+ UMP_CC_EFFECT1_LSB = 44,
+ UMP_CC_EFFECT2_LSB = 45,
+ UMP_CC_GP_1_LSB = 48,
+ UMP_CC_GP_2_LSB = 49,
+ UMP_CC_GP_3_LSB = 50,
+ UMP_CC_GP_4_LSB = 51,
+ UMP_CC_SUSTAIN = 64,
+ UMP_CC_PORTAMENTO_SWITCH = 65,
+ UMP_CC_SOSTENUTO = 66,
+ UMP_CC_SOFT_PEDAL = 67,
+ UMP_CC_LEGATO = 68,
+ UMP_CC_HOLD_2 = 69,
+ UMP_CC_SOUND_CONTROLLER_1 = 70,
+ UMP_CC_SOUND_CONTROLLER_2 = 71,
+ UMP_CC_SOUND_CONTROLLER_3 = 72,
+ UMP_CC_SOUND_CONTROLLER_4 = 73,
+ UMP_CC_SOUND_CONTROLLER_5 = 74,
+ UMP_CC_SOUND_CONTROLLER_6 = 75,
+ UMP_CC_SOUND_CONTROLLER_7 = 76,
+ UMP_CC_SOUND_CONTROLLER_8 = 77,
+ UMP_CC_SOUND_CONTROLLER_9 = 78,
+ UMP_CC_SOUND_CONTROLLER_10 = 79,
+ UMP_CC_GP_5 = 80,
+ UMP_CC_GP_6 = 81,
+ UMP_CC_GP_7 = 82,
+ UMP_CC_GP_8 = 83,
+ UMP_CC_PORTAMENTO_CONTROL = 84,
+ UMP_CC_EFFECT_1 = 91,
+ UMP_CC_EFFECT_2 = 92,
+ UMP_CC_EFFECT_3 = 93,
+ UMP_CC_EFFECT_4 = 94,
+ UMP_CC_EFFECT_5 = 95,
+ UMP_CC_DATA_INC = 96,
+ UMP_CC_DATA_DEC = 97,
+ UMP_CC_NRPN_LSB = 98,
+ UMP_CC_NRPN_MSB = 99,
+ UMP_CC_RPN_LSB = 100,
+ UMP_CC_RPN_MSB = 101,
+ UMP_CC_ALL_SOUND_OFF = 120,
+ UMP_CC_RESET_ALL = 121,
+ UMP_CC_LOCAL_CONTROL = 122,
+ UMP_CC_ALL_NOTES_OFF = 123,
+ UMP_CC_OMNI_OFF = 124,
+ UMP_CC_OMNI_ON = 125,
+ UMP_CC_POLY_OFF = 126,
+ UMP_CC_POLY_ON = 127,
+};
+
+/* MIDI 1.0 / 2.0 System Messages (0xfx) */
+enum {
+ UMP_SYSTEM_STATUS_MIDI_TIME_CODE = 0xf1,
+ UMP_SYSTEM_STATUS_SONG_POSITION = 0xf2,
+ UMP_SYSTEM_STATUS_SONG_SELECT = 0xf3,
+ UMP_SYSTEM_STATUS_TUNE_REQUEST = 0xf6,
+ UMP_SYSTEM_STATUS_TIMING_CLOCK = 0xf8,
+ UMP_SYSTEM_STATUS_START = 0xfa,
+ UMP_SYSTEM_STATUS_CONTINUE = 0xfb,
+ UMP_SYSTEM_STATUS_STOP = 0xfc,
+ UMP_SYSTEM_STATUS_ACTIVE_SENSING = 0xfe,
+ UMP_SYSTEM_STATUS_RESET = 0xff,
+};
+
+/* MIDI 1.0 Realtime and SysEx status messages (0xfx) */
+enum {
+ UMP_MIDI1_MSG_REALTIME = 0xf0, /* mask */
+ UMP_MIDI1_MSG_SYSEX_START = 0xf0,
+ UMP_MIDI1_MSG_SYSEX_END = 0xf7,
+};
+
+/*
+ * UMP Message Definitions
+ */
+
+/* MIDI 1.0 Note Off / Note On (32bit) */
+struct snd_ump_midi1_msg_note {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 velocity:8;
+#else
+ u32 velocity:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* MIDI 1.0 Poly Pressure (32bit) */
+struct snd_ump_midi1_msg_paf {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 data:8;
+#else
+ u32 data:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* MIDI 1.0 Control Change (32bit) */
+struct snd_ump_midi1_msg_cc {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 index:8;
+ u32 data:8;
+#else
+ u32 data:8;
+ u32 index:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* MIDI 1.0 Program Change (32bit) */
+struct snd_ump_midi1_msg_program {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 program:8;
+ u32 reserved:8;
+#else
+ u32 reserved:8;
+ u32 program:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* MIDI 1.0 Channel Pressure (32bit) */
+struct snd_ump_midi1_msg_caf {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 data:8;
+ u32 reserved:8;
+#else
+ u32 reserved:8;
+ u32 data:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* MIDI 1.0 Pitch Bend (32bit) */
+struct snd_ump_midi1_msg_pitchbend {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 data_lsb:8;
+ u32 data_msb:8;
+#else
+ u32 data_msb:8;
+ u32 data_lsb:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* System Common and Real Time messages (32bit); no channel field */
+struct snd_ump_system_msg {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 type:4;
+ u32 group:4;
+ u32 status:8;
+ u32 parm1:8;
+ u32 parm2:8;
+#else
+ u32 parm2:8;
+ u32 parm1:8;
+ u32 status:8;
+ u32 group:4;
+ u32 type:4;
+#endif
+} __packed;
+
+/* MIDI 1.0 UMP CVM (32bit) */
+union snd_ump_midi1_msg {
+ struct snd_ump_midi1_msg_note note;
+ struct snd_ump_midi1_msg_paf paf;
+ struct snd_ump_midi1_msg_cc cc;
+ struct snd_ump_midi1_msg_program pg;
+ struct snd_ump_midi1_msg_caf caf;
+ struct snd_ump_midi1_msg_pitchbend pb;
+ struct snd_ump_system_msg system;
+ u32 raw;
+};
+
+/* MIDI 2.0 Note Off / Note On (64bit) */
+struct snd_ump_midi2_msg_note {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 attribute_type:8;
+ /* 1 */
+ u32 velocity:16;
+ u32 attribute_data:16;
+#else
+ /* 0 */
+ u32 attribute_type:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 attribute_data:16;
+ u32 velocity:16;
+#endif
+} __packed;
+
+/* MIDI 2.0 Poly Pressure (64bit) */
+struct snd_ump_midi2_msg_paf {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 reserved:8;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 reserved:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 Per-Note Controller (64bit) */
+struct snd_ump_midi2_msg_pernote_cc {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 index:8;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 index:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 Per-Note Management (64bit) */
+struct snd_ump_midi2_msg_pernote_mgmt {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 flags:8;
+ /* 1 */
+ u32 reserved;
+#else
+ /* 0 */
+ u32 flags:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 reserved;
+#endif
+} __packed;
+
+/* MIDI 2.0 Control Change (64bit) */
+struct snd_ump_midi2_msg_cc {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 index:8;
+ u32 reserved:8;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 reserved:8;
+ u32 index:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 Registered Controller (RPN) / Assignable Controller (NRPN) (64bit) */
+struct snd_ump_midi2_msg_rpn {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 bank:8;
+ u32 index:8;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 index:8;
+ u32 bank:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 Program Change (64bit) */
+struct snd_ump_midi2_msg_program {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 reserved:15;
+ u32 bank_valid:1;
+ /* 1 */
+ u32 program:8;
+ u32 reserved2:8;
+ u32 bank_msb:8;
+ u32 bank_lsb:8;
+#else
+ /* 0 */
+ u32 bank_valid:1;
+ u32 reserved:15;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 bank_lsb:8;
+ u32 bank_msb:8;
+ u32 reserved2:8;
+ u32 program:8;
+#endif
+} __packed;
+
+/* MIDI 2.0 Channel Pressure (64bit) */
+struct snd_ump_midi2_msg_caf {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 reserved:16;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 reserved:16;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 Pitch Bend (64bit) */
+struct snd_ump_midi2_msg_pitchbend {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 reserved:16;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 reserved:16;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 Per-Note Pitch Bend (64bit) */
+struct snd_ump_midi2_msg_pernote_pitchbend {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 group:4;
+ u32 status:4;
+ u32 channel:4;
+ u32 note:8;
+ u32 reserved:8;
+ /* 1 */
+ u32 data;
+#else
+ /* 0 */
+ u32 reserved:8;
+ u32 note:8;
+ u32 channel:4;
+ u32 status:4;
+ u32 group:4;
+ u32 type:4;
+ /* 1 */
+ u32 data;
+#endif
+} __packed;
+
+/* MIDI 2.0 UMP CVM (64bit) */
+union snd_ump_midi2_msg {
+ struct snd_ump_midi2_msg_note note;
+ struct snd_ump_midi2_msg_paf paf;
+ struct snd_ump_midi2_msg_pernote_cc pernote_cc;
+ struct snd_ump_midi2_msg_pernote_mgmt pernote_mgmt;
+ struct snd_ump_midi2_msg_cc cc;
+ struct snd_ump_midi2_msg_rpn rpn;
+ struct snd_ump_midi2_msg_program pg;
+ struct snd_ump_midi2_msg_caf caf;
+ struct snd_ump_midi2_msg_pitchbend pb;
+ struct snd_ump_midi2_msg_pernote_pitchbend pernote_pb;
+ u32 raw[2];
+};
+
+/* UMP Stream Message: Endpoint Discovery (128bit) */
+struct snd_ump_stream_msg_ep_discovery {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 ump_version_major:8;
+ u32 ump_version_minor:8;
+ /* 1 */
+ u32 reserved:24;
+ u32 filter_bitmap:8;
+ /* 2-3 */
+ u32 reserved2[2];
+#else
+ /* 0 */
+ u32 ump_version_minor:8;
+ u32 ump_version_major:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 filter_bitmap:8;
+ u32 reserved:24;
+ /* 2-3 */
+ u32 reserved2[2];
+#endif
+} __packed;
+
+/* UMP Stream Message: Endpoint Info Notification (128bit) */
+struct snd_ump_stream_msg_ep_info {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 ump_version_major:8;
+ u32 ump_version_minor:8;
+ /* 1 */
+ u32 static_function_block:1;
+ u32 num_function_blocks:7;
+ u32 reserved:8;
+ u32 protocol:8;
+ u32 reserved2:6;
+ u32 jrts:2;
+ /* 2-3 */
+ u32 reserved3[2];
+#else
+ /* 0 */
+ u32 ump_version_minor:8;
+ u32 ump_version_major:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 jrts:2;
+ u32 reserved2:6;
+ u32 protocol:8;
+ u32 reserved:8;
+ u32 num_function_blocks:7;
+ u32 static_function_block:1;
+ /* 2-3 */
+ u32 reserved3[2];
+#endif
+} __packed;
+
+/* UMP Stream Message: Device Info Notification (128bit) */
+struct snd_ump_stream_msg_devince_info {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 reserved:16;
+ /* 1 */
+ u32 manufacture_id;
+ /* 2 */
+ u8 family_lsb;
+ u8 family_msb;
+ u8 model_lsb;
+ u8 model_msb;
+ /* 3 */
+ u32 sw_revision;
+#else
+ /* 0 */
+ u32 reserved:16;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 manufacture_id;
+ /* 2 */
+ u8 model_msb;
+ u8 model_lsb;
+ u8 family_msb;
+ u8 family_lsb;
+ /* 3 */
+ u32 sw_revision;
+#endif
+} __packed;
+
+/* UMP Stream Message: Stream Config Request / Notification (128bit) */
+struct snd_ump_stream_msg_stream_cfg {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 protocol:8;
+ u32 reserved:6;
+ u32 jrts:2;
+ /* 1-3 */
+ u32 reserved2[3];
+#else
+ /* 0 */
+ u32 jrts:2;
+ u32 reserved:6;
+ u32 protocol:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1-3 */
+ u32 reserved2[3];
+#endif
+} __packed;
+
+/* UMP Stream Message: Function Block Discovery (128bit) */
+struct snd_ump_stream_msg_fb_discovery {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 function_block_id:8;
+ u32 filter:8;
+ /* 1-3 */
+ u32 reserved[3];
+#else
+ /* 0 */
+ u32 filter:8;
+ u32 function_block_id:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1-3 */
+ u32 reserved[3];
+#endif
+} __packed;
+
+/* UMP Stream Message: Function Block Info Notification (128bit) */
+struct snd_ump_stream_msg_fb_info {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 active:1;
+ u32 function_block_id:7;
+ u32 reserved:2;
+ u32 ui_hint:2;
+ u32 midi_10:2;
+ u32 direction:2;
+ /* 1 */
+ u32 first_group:8;
+ u32 num_groups:8;
+ u32 midi_ci_version:8;
+ u32 sysex8_streams:8;
+ /* 2-3 */
+ u32 reserved2[2];
+#else
+ /* 0 */
+ u32 direction:2;
+ u32 midi_10:2;
+ u32 ui_hint:2;
+ u32 reserved:2;
+ u32 function_block_id:7;
+ u32 active:1;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 sysex8_streams:8;
+ u32 midi_ci_version:8;
+ u32 num_groups:8;
+ u32 first_group:8;
+ /* 2-3 */
+ u32 reserved2[2];
+#endif
+} __packed;
+
+/* UMP Stream Message: Function Block Name Notification (128bit) */
+struct snd_ump_stream_msg_fb_name {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u16 type:4;
+ u16 format:2;
+ u16 status:10;
+ u8 function_block_id;
+ u8 name0;
+ /* 1-3 */
+ u8 name[12];
+#else
+ /* 0 */
+ u8 name0;
+ u8 function_block_id;
+ u16 status:10;
+ u16 format:2;
+ u16 type:4;
+ /* 1-3 */
+ u8 name[12]; // FIXME: byte order
+#endif
+} __packed;
+
+/* MIDI 2.0 Stream Messages (128bit) */
+union snd_ump_stream_msg {
+ struct snd_ump_stream_msg_ep_discovery ep_discovery;
+ struct snd_ump_stream_msg_ep_info ep_info;
+ struct snd_ump_stream_msg_devince_info device_info;
+ struct snd_ump_stream_msg_stream_cfg stream_cfg;
+ struct snd_ump_stream_msg_fb_discovery fb_discovery;
+ struct snd_ump_stream_msg_fb_info fb_info;
+ struct snd_ump_stream_msg_fb_name fb_name;
+ u32 raw[4];
+};
+
+#endif /* __SOUND_UMP_MSG_H */
diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h
index 00d2703e8fca..b5bc8604efe8 100644
--- a/include/uapi/sound/asequencer.h
+++ b/include/uapi/sound/asequencer.h
@@ -10,7 +10,7 @@
#include <sound/asound.h>
/** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)
/**
* definition of sequencer event types
@@ -174,6 +174,7 @@ struct snd_seq_connect {
#define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */
#define SNDRV_SEQ_PRIORITY_MASK (1<<4)
+#define SNDRV_SEQ_EVENT_UMP (1<<5) /* event holds a UMP packet */
/* note event */
struct snd_seq_ev_note {
@@ -252,6 +253,19 @@ struct snd_seq_ev_quote {
struct snd_seq_event *event; /* quoted event */
} __attribute__((packed));
+union snd_seq_event_data { /* event data... */
+ struct snd_seq_ev_note note;
+ struct snd_seq_ev_ctrl control;
+ struct snd_seq_ev_raw8 raw8;
+ struct snd_seq_ev_raw32 raw32;
+ struct snd_seq_ev_ext ext;
+ struct snd_seq_ev_queue_control queue;
+ union snd_seq_timestamp time;
+ struct snd_seq_addr addr;
+ struct snd_seq_connect connect;
+ struct snd_seq_result result;
+ struct snd_seq_ev_quote quote;
+};
/* sequencer event */
struct snd_seq_event {
@@ -262,25 +276,27 @@ struct snd_seq_event {
unsigned char queue; /* schedule queue */
union snd_seq_timestamp time; /* schedule time */
-
struct snd_seq_addr source; /* source address */
struct snd_seq_addr dest; /* destination address */
- union { /* event data... */
- struct snd_seq_ev_note note;
- struct snd_seq_ev_ctrl control;
- struct snd_seq_ev_raw8 raw8;
- struct snd_seq_ev_raw32 raw32;
- struct snd_seq_ev_ext ext;
- struct snd_seq_ev_queue_control queue;
- union snd_seq_timestamp time;
- struct snd_seq_addr addr;
- struct snd_seq_connect connect;
- struct snd_seq_result result;
- struct snd_seq_ev_quote quote;
- } data;
+ union snd_seq_event_data data;
};
+ /* (compatible) event for UMP-capable clients */
+struct snd_seq_ump_event {
+ snd_seq_event_type_t type; /* event type */
+ unsigned char flags; /* event flags */
+ char tag;
+ unsigned char queue; /* schedule queue */
+ union snd_seq_timestamp time; /* schedule time */
+ struct snd_seq_addr source; /* source address */
+ struct snd_seq_addr dest; /* destination address */
+
+ union {
+ union snd_seq_event_data data;
+ unsigned int ump[4];
+ };
+};
/*
* bounce event - stored as variable size data
@@ -331,6 +347,7 @@ typedef int __bitwise snd_seq_client_type_t;
#define SNDRV_SEQ_FILTER_BROADCAST (1U<<0) /* accept broadcast messages */
#define SNDRV_SEQ_FILTER_MULTICAST (1U<<1) /* accept multicast messages */
#define SNDRV_SEQ_FILTER_BOUNCE (1U<<2) /* accept bounce event in error */
+#define SNDRV_SEQ_FILTER_NO_CONVERT (1U<<30) /* don't convert UMP events */
#define SNDRV_SEQ_FILTER_USE_EVENT (1U<<31) /* use event filter */
struct snd_seq_client_info {
@@ -344,9 +361,18 @@ struct snd_seq_client_info {
int event_lost; /* number of lost events */
int card; /* RO: card number[kernel] */
int pid; /* RO: pid[user] */
- char reserved[56]; /* for future use */
+ unsigned int midi_version; /* MIDI version */
+ unsigned int group_filter; /* UMP group filter bitmap
+ * (bit 0 = groupless messages,
+ * bit 1-16 = messages for groups 1-16)
+ */
+ char reserved[48]; /* for future use */
};
+/* MIDI version numbers in client info */
+#define SNDRV_SEQ_CLIENT_LEGACY_MIDI 0 /* Legacy client */
+#define SNDRV_SEQ_CLIENT_UMP_MIDI_1_0 1 /* UMP MIDI 1.0 */
+#define SNDRV_SEQ_CLIENT_UMP_MIDI_2_0 2 /* UMP MIDI 2.0 */
/* client pool size */
struct snd_seq_client_pool {
@@ -406,6 +432,8 @@ struct snd_seq_remove_events {
#define SNDRV_SEQ_PORT_CAP_SUBS_READ (1<<5) /* allow read subscription */
#define SNDRV_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /* allow write subscription */
#define SNDRV_SEQ_PORT_CAP_NO_EXPORT (1<<7) /* routing not allowed */
+#define SNDRV_SEQ_PORT_CAP_INACTIVE (1<<8) /* inactive port */
+#define SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT (1<<9) /* MIDI 2.0 UMP Endpoint port */
/* port type */
#define SNDRV_SEQ_PORT_TYPE_SPECIFIC (1<<0) /* hardware specific */
@@ -415,6 +443,7 @@ struct snd_seq_remove_events {
#define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */
#define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */
#define SNDRV_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) /* General MIDI 2 compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_UMP (1<<7) /* UMP */
/* other standards...*/
#define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device (no MIDI compatible - direct wavetable) */
@@ -432,6 +461,12 @@ struct snd_seq_remove_events {
#define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1)
#define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<2)
+/* port direction */
+#define SNDRV_SEQ_PORT_DIR_UNKNOWN 0
+#define SNDRV_SEQ_PORT_DIR_INPUT 1
+#define SNDRV_SEQ_PORT_DIR_OUTPUT 2
+#define SNDRV_SEQ_PORT_DIR_BIDIRECTION 3
+
struct snd_seq_port_info {
struct snd_seq_addr addr; /* client/port numbers */
char name[64]; /* port name */
@@ -448,7 +483,9 @@ struct snd_seq_port_info {
void *kernel; /* reserved for kernel use (must be NULL) */
unsigned int flags; /* misc. conditioning */
unsigned char time_queue; /* queue # for timestamping */
- char reserved[59]; /* for future use */
+ unsigned char direction; /* port usage direction (r/w/bidir) */
+ unsigned char ump_group; /* 0 = UMP EP (no conversion), 1-16 = UMP group number */
+ char reserved[57]; /* for future use */
};
@@ -552,6 +589,18 @@ struct snd_seq_query_subs {
char reserved[64]; /* for future use */
};
+/*
+ * UMP-specific information
+ */
+/* type of UMP info query */
+#define SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT 0
+#define SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK 1
+
+struct snd_seq_client_ump_info {
+ int client; /* client number to inquire/set */
+ int type; /* type to inquire/set */
+ unsigned char info[512]; /* info (either UMP ep or block info) */
+} __packed;
/*
* IOCTL commands
@@ -561,9 +610,12 @@ struct snd_seq_query_subs {
#define SNDRV_SEQ_IOCTL_CLIENT_ID _IOR ('S', 0x01, int)
#define SNDRV_SEQ_IOCTL_SYSTEM_INFO _IOWR('S', 0x02, struct snd_seq_system_info)
#define SNDRV_SEQ_IOCTL_RUNNING_MODE _IOWR('S', 0x03, struct snd_seq_running_info)
+#define SNDRV_SEQ_IOCTL_USER_PVERSION _IOW('S', 0x04, int)
#define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO _IOWR('S', 0x10, struct snd_seq_client_info)
#define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO _IOW ('S', 0x11, struct snd_seq_client_info)
+#define SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO _IOWR('S', 0x12, struct snd_seq_client_ump_info)
+#define SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO _IOWR('S', 0x13, struct snd_seq_client_ump_info)
#define SNDRV_SEQ_IOCTL_CREATE_PORT _IOWR('S', 0x20, struct snd_seq_port_info)
#define SNDRV_SEQ_IOCTL_DELETE_PORT _IOW ('S', 0x21, struct snd_seq_port_info)
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 0aa955aa8246..f9939da41122 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -274,6 +274,7 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */
#define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */
#define SNDRV_PCM_INFO_SYNC_APPLPTR 0x00000020 /* need the explicit sync of appl_ptr update */
+#define SNDRV_PCM_INFO_PERFECT_DRAIN 0x00000040 /* silencing at the end of stream is not required */
#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */
#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */
#define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */
@@ -383,6 +384,9 @@ typedef int snd_pcm_hw_param_t;
#define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */
#define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */
#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */
+#define SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE (1<<3) /* suppress drain with the filling
+ * of the silence samples
+ */
struct snd_interval {
unsigned int min, max;
@@ -708,7 +712,7 @@ enum {
* Raw MIDI section - /dev/snd/midi??
*/
-#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2)
+#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4)
enum {
SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
@@ -719,6 +723,7 @@ enum {
#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001
#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002
#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004
+#define SNDRV_RAWMIDI_INFO_UMP 0x00000008
struct snd_rawmidi_info {
unsigned int device; /* RO/WR (control): device number */
@@ -779,6 +784,72 @@ struct snd_rawmidi_status {
};
#endif
+/* UMP EP info flags */
+#define SNDRV_UMP_EP_INFO_STATIC_BLOCKS 0x01
+
+/* UMP EP Protocol / JRTS capability bits */
+#define SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300
+#define SNDRV_UMP_EP_INFO_PROTO_MIDI1 0x0100 /* MIDI 1.0 */
+#define SNDRV_UMP_EP_INFO_PROTO_MIDI2 0x0200 /* MIDI 2.0 */
+#define SNDRV_UMP_EP_INFO_PROTO_JRTS_MASK 0x0003
+#define SNDRV_UMP_EP_INFO_PROTO_JRTS_TX 0x0001 /* JRTS Transmit */
+#define SNDRV_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 /* JRTS Receive */
+
+/* UMP Endpoint information */
+struct snd_ump_endpoint_info {
+ int card; /* card number */
+ int device; /* device number */
+ unsigned int flags; /* additional info */
+ unsigned int protocol_caps; /* protocol capabilities */
+ unsigned int protocol; /* current protocol */
+ unsigned int num_blocks; /* # of function blocks */
+ unsigned short version; /* UMP major/minor version */
+ unsigned short family_id; /* MIDI device family ID */
+ unsigned short model_id; /* MIDI family model ID */
+ unsigned int manufacturer_id; /* MIDI manufacturer ID */
+ unsigned char sw_revision[4]; /* software revision */
+ unsigned short padding;
+ unsigned char name[128]; /* endpoint name string */
+ unsigned char product_id[128]; /* unique product id string */
+ unsigned char reserved[32];
+} __packed;
+
+/* UMP direction */
+#define SNDRV_UMP_DIR_INPUT 0x01
+#define SNDRV_UMP_DIR_OUTPUT 0x02
+#define SNDRV_UMP_DIR_BIDIRECTION 0x03
+
+/* UMP block info flags */
+#define SNDRV_UMP_BLOCK_IS_MIDI1 (1U << 0) /* MIDI 1.0 port w/o restrict */
+#define SNDRV_UMP_BLOCK_IS_LOWSPEED (1U << 1) /* 31.25Kbps B/W MIDI1 port */
+
+/* UMP block user-interface hint */
+#define SNDRV_UMP_BLOCK_UI_HINT_UNKNOWN 0x00
+#define SNDRV_UMP_BLOCK_UI_HINT_RECEIVER 0x01
+#define SNDRV_UMP_BLOCK_UI_HINT_SENDER 0x02
+#define SNDRV_UMP_BLOCK_UI_HINT_BOTH 0x03
+
+/* UMP groups and blocks */
+#define SNDRV_UMP_MAX_GROUPS 16
+#define SNDRV_UMP_MAX_BLOCKS 32
+
+/* UMP Block information */
+struct snd_ump_block_info {
+ int card; /* card number */
+ int device; /* device number */
+ unsigned char block_id; /* block ID (R/W) */
+ unsigned char direction; /* UMP direction */
+ unsigned char active; /* Activeness */
+ unsigned char first_group; /* first group ID */
+ unsigned char num_groups; /* number of groups */
+ unsigned char midi_ci_version; /* MIDI-CI support version */
+ unsigned char sysex8_streams; /* max number of sysex8 streams */
+ unsigned char ui_hint; /* user interface hint */
+ unsigned int flags; /* various info flags */
+ unsigned char name[128]; /* block name string */
+ unsigned char reserved[32];
+} __packed;
+
#define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int)
#define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info)
#define SNDRV_RAWMIDI_IOCTL_USER_PVERSION _IOW('W', 0x02, int)
@@ -786,6 +857,9 @@ struct snd_rawmidi_status {
#define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status)
#define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int)
#define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int)
+/* Additional ioctls for UMP rawmidi devices */
+#define SNDRV_UMP_IOCTL_ENDPOINT_INFO _IOR('W', 0x40, struct snd_ump_endpoint_info)
+#define SNDRV_UMP_IOCTL_BLOCK_INFO _IOR('W', 0x41, struct snd_ump_block_info)
/*
* Timer section - /dev/snd/timer
@@ -961,7 +1035,7 @@ struct snd_timer_tread {
* *
****************************************************************************/
-#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8)
+#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9)
struct snd_ctl_card_info {
int card; /* card number */
@@ -1122,6 +1196,9 @@ struct snd_ctl_tlv {
#define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int)
#define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info)
#define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int)
+#define SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE _IOWR('U', 0x43, int)
+#define SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO _IOWR('U', 0x44, struct snd_ump_endpoint_info)
+#define SNDRV_CTL_IOCTL_UMP_BLOCK_INFO _IOWR('U', 0x45, struct snd_ump_block_info)
#define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int)
#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int)
diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h
index c8e131d6da00..4c32a116e7ad 100644
--- a/include/uapi/sound/emu10k1.h
+++ b/include/uapi/sound/emu10k1.h
@@ -308,6 +308,8 @@ struct snd_emu10k1_fx8010_info {
#define EMU10K1_GPR_TRANSLATION_BASS 2
#define EMU10K1_GPR_TRANSLATION_TREBLE 3
#define EMU10K1_GPR_TRANSLATION_ONOFF 4
+#define EMU10K1_GPR_TRANSLATION_NEGATE 5
+#define EMU10K1_GPR_TRANSLATION_NEG_TABLE100 6
enum emu10k1_ctl_elem_iface {
EMU10K1_CTL_ELEM_IFACE_MIXER = 2, /* virtual mixer device */
@@ -328,9 +330,9 @@ struct snd_emu10k1_fx8010_control_gpr {
unsigned int vcount; /* visible count */
unsigned int count; /* count of GPR (1..16) */
unsigned short gpr[32]; /* GPR number(s) */
- unsigned int value[32]; /* initial values */
- unsigned int min; /* minimum range */
- unsigned int max; /* maximum range */
+ int value[32]; /* initial values */
+ int min; /* minimum range */
+ int max; /* maximum range */
unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */
const unsigned int *tlv;
};
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c
index 4c75381f5ab8..a8a59d71dcec 100644
--- a/sound/aoa/codecs/onyx.c
+++ b/sound/aoa/codecs/onyx.c
@@ -1048,7 +1048,7 @@ static struct i2c_driver onyx_driver = {
.driver = {
.name = "aoa_codec_onyx",
},
- .probe_new = onyx_i2c_probe,
+ .probe = onyx_i2c_probe,
.remove = onyx_i2c_remove,
.id_table = onyx_i2c_id,
};
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
index f906e9aaddcf..ab1472390061 100644
--- a/sound/aoa/codecs/tas.c
+++ b/sound/aoa/codecs/tas.c
@@ -936,7 +936,7 @@ static struct i2c_driver tas_driver = {
.driver = {
.name = "aoa_codec_tas",
},
- .probe_new = tas_i2c_probe,
+ .probe = tas_i2c_probe,
.remove = tas_i2c_remove,
.id_table = tas_i2c_id,
};
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 12990d9a4dff..e41818e59a15 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -26,6 +26,19 @@ 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_COMPRESS_OFFLOAD
tristate
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 2762f03d9b7b..a6b444ee2832 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -28,6 +28,8 @@ snd-pcm-dmaengine-objs := pcm_dmaengine.o
snd-ctl-led-objs := control_led.o
snd-rawmidi-objs := rawmidi.o
+snd-ump-objs := ump.o
+snd-ump-$(CONFIG_SND_UMP_LEGACY_RAWMIDI) += ump_convert.o
snd-timer-objs := timer.o
snd-hrtimer-objs := hrtimer.o
snd-rtctimer-objs := rtctimer.o
@@ -45,6 +47,7 @@ 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_OSSEMUL) += oss/
obj-$(CONFIG_SND_SEQUENCER) += seq/
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 243acad89fd3..30f73097447b 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -589,7 +589,7 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
struct snd_compr_params *params;
int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ 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
@@ -612,6 +612,9 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
if (retval)
goto out;
+ if (stream->next_track)
+ goto out;
+
stream->metadata_set = false;
stream->next_track = false;
diff --git a/sound/core/control.c b/sound/core/control.c
index 82aa1af1d1d8..8386b53acdcd 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -730,12 +730,20 @@ 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);
kctl = snd_ctl_find_id(card, src_id);
@@ -743,10 +751,10 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
up_write(&card->controls_rwsem);
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;
+ kctl->id.numid = saved_numid;
add_hash_entries(card, kctl);
up_write(&card->controls_rwsem);
return 0;
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index d8a86d1a99d6..9cae5d74335c 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -197,7 +197,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
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:
@@ -234,8 +234,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;
@@ -244,7 +244,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;
@@ -267,8 +267,8 @@ static int copy_ctl_value_to_user(void __user *userdata,
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;
@@ -277,7 +277,7 @@ 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;
}
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index 3cadd40100f3..ee77547bf8dc 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -737,7 +737,7 @@ static int __init snd_ctl_led_init(void)
unsigned int group;
device_initialize(&snd_ctl_led_dev);
- snd_ctl_led_dev.class = sound_class;
+ 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)) {
diff --git a/sound/core/init.c b/sound/core/init.c
index df0c22480375..baef2688d0cf 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -129,7 +129,7 @@ void snd_device_initialize(struct device *dev, struct snd_card *card)
device_initialize(dev);
if (card)
dev->parent = &card->card_dev;
- dev->class = sound_class;
+ dev->class = &sound_class;
dev->release = default_release;
}
EXPORT_SYMBOL_GPL(snd_device_initialize);
@@ -331,7 +331,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
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;
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
index 4b5faae5d16e..07075071972d 100644
--- a/sound/core/pcm_drm_eld.c
+++ b/sound/core/pcm_drm_eld.c
@@ -2,11 +2,25 @@
/*
* PCM DRM helpers
*/
+#include <linux/bitfield.h>
#include <linux/export.h>
+#include <linux/hdmi.h>
#include <drm/drm_edid.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,
@@ -17,9 +31,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,
@@ -42,7 +109,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);
}
}
@@ -70,7 +137,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));
}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 39a65d1415ab..95fc56e403b1 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1605,10 +1605,6 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream,
{
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 (pause_pushed(state))
- snd_pcm_update_hw_ptr(substream);
/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
* a delta between the current jiffies, this gives a large enough
* delta, effectively to skip the check once.
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 7147fda66d93..2d3cec908154 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -21,6 +21,7 @@
#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.");
@@ -35,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 *rmidi);
static int snd_rawmidi_dev_free(struct snd_device *device);
static int snd_rawmidi_dev_register(struct snd_device *device);
static int snd_rawmidi_dev_disconnect(struct snd_device *device);
@@ -73,6 +73,9 @@ struct snd_rawmidi_status64 {
#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)
{
struct snd_rawmidi *rawmidi;
@@ -181,9 +184,23 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
}
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;
@@ -406,24 +423,15 @@ 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 snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
int mode, struct snd_rawmidi_file *rfile)
{
- struct snd_rawmidi *rmidi;
- int err = 0;
+ int err;
if (snd_BUG_ON(!rfile))
return -EINVAL;
-
- mutex_lock(&register_mutex);
- rmidi = snd_rawmidi_search(card, device);
- if (!rmidi)
- err = -ENODEV;
- else if (!try_module_get(rmidi->card->module))
- err = -ENXIO;
- mutex_unlock(&register_mutex);
- if (err < 0)
- return err;
+ if (!try_module_get(rmidi->card->module))
+ return -ENXIO;
mutex_lock(&rmidi->open_mutex);
err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
@@ -730,6 +738,8 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
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 = kvzalloc(params->buffer_size, GFP_KERNEL);
if (!newbuf)
@@ -902,6 +912,7 @@ static int snd_rawmidi_ioctl_status64(struct snd_rawmidi_file *rfile,
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;
@@ -993,12 +1004,67 @@ 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;
+ mutex_lock(&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;
+ mutex_unlock(&register_mutex);
+ 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, ret;
+
+ if (get_user(device, &info->device))
+ return -EFAULT;
+ mutex_lock(&register_mutex);
+ rmidi = snd_rawmidi_search(card, device);
+ if (rmidi && rmidi->ops && rmidi->ops->ioctl)
+ ret = rmidi->ops->ioctl(rmidi, cmd, argp);
+ else
+ ret = -ENXIO;
+ mutex_unlock(&register_mutex);
+ return ret;
+}
+#endif
+
static int snd_rawmidi_control_ioctl(struct snd_card *card,
struct snd_ctl_file *control,
unsigned int cmd,
@@ -1008,27 +1074,15 @@ 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;
@@ -1052,12 +1106,13 @@ static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
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 > 0) {
+ while (src_count > align) {
if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
runtime->xruns += src_count;
break;
@@ -1065,7 +1120,9 @@ static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
else {
- frame.length = src_count;
+ 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);
@@ -1129,6 +1186,10 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
goto unlock;
}
+ count = get_aligned_size(runtime, count);
+ if (!count)
+ goto unlock;
+
if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
} else if (count == 1) { /* special case, faster code */
@@ -1148,6 +1209,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)
+ goto unlock;
memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
runtime->hw_ptr += count1;
runtime->hw_ptr %= runtime->buffer_size;
@@ -1348,12 +1412,18 @@ static 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;
}
@@ -1410,6 +1480,7 @@ static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
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;
@@ -1696,6 +1767,11 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
rmidi = entry->private_data;
snd_iprintf(buffer, "%s\n\n", rmidi->name);
+ if (IS_ENABLED(CONFIG_SND_UMP))
+ snd_iprintf(buffer, "Type: %s\n",
+ rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
+ if (rmidi->ops->proc_read)
+ rmidi->ops->proc_read(entry, buffer);
mutex_lock(&rmidi->open_mutex);
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
list_for_each_entry(substream,
@@ -1806,25 +1882,12 @@ static void release_rawmidi_device(struct device *dev)
kfree(container_of(dev, struct snd_rawmidi, dev));
}
-/**
- * snd_rawmidi_new - create a rawmidi instance
- * @card: the card instance
- * @id: the id string
- * @device: the device index
- * @output_count: the number of output streams
- * @input_count: the number of input streams
- * @rrawmidi: the pointer to store the new rawmidi instance
- *
- * Creates a new rawmidi instance.
- * Use snd_rawmidi_set_ops() to set the operators to the new instance.
- *
- * Return: Zero if successful, or a negative error code on failure.
- */
-int snd_rawmidi_new(struct snd_card *card, char *id, int device,
- int output_count, int input_count,
- struct snd_rawmidi **rrawmidi)
+/* 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)
{
- struct snd_rawmidi *rmidi;
int err;
static const struct snd_device_ops ops = {
.dev_free = snd_rawmidi_dev_free,
@@ -1832,50 +1895,78 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
.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);
+ rmidi->info_flags = info_flags;
if (id != NULL)
strscpy(rmidi->id, id, sizeof(rmidi->id));
snd_device_initialize(&rmidi->dev, card);
rmidi->dev.release = release_rawmidi_device;
- dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device);
+ 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)
- goto error;
+ return err;
err = snd_rawmidi_alloc_substreams(rmidi,
&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
SNDRV_RAWMIDI_STREAM_OUTPUT,
output_count);
if (err < 0)
- goto error;
+ return err;
err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
if (err < 0)
- goto error;
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_rawmidi_init);
+
+/**
+ * snd_rawmidi_new - create a rawmidi instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index
+ * @output_count: the number of output streams
+ * @input_count: the number of input streams
+ * @rrawmidi: the pointer to store the new rawmidi instance
+ *
+ * Creates a new rawmidi instance.
+ * Use snd_rawmidi_set_ops() to set the operators to the new instance.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_new(struct snd_card *card, char *id, int device,
+ int output_count, int input_count,
+ struct snd_rawmidi **rrawmidi)
+{
+ struct snd_rawmidi *rmidi;
+ int err;
if (rrawmidi)
+ *rrawmidi = NULL;
+ rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
+ if (!rmidi)
+ return -ENOMEM;
+ err = snd_rawmidi_init(rmidi, card, id, device,
+ output_count, input_count, 0);
+ if (err < 0) {
+ snd_rawmidi_free(rmidi);
+ return err;
+ }
+ if (rrawmidi)
*rrawmidi = rmidi;
return 0;
-
- error:
- snd_rawmidi_free(rmidi);
- return err;
}
EXPORT_SYMBOL(snd_rawmidi_new);
@@ -1890,7 +1981,8 @@ 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;
@@ -1907,6 +1999,7 @@ static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
put_device(&rmidi->dev);
return 0;
}
+EXPORT_SYMBOL_GPL(snd_rawmidi_free);
static int snd_rawmidi_dev_free(struct snd_device *device)
{
@@ -1957,7 +2050,8 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
#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) {
@@ -1971,7 +2065,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) {
@@ -1995,7 +2090,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;
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index 68a93443583c..b81b30d82f88 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -111,6 +111,10 @@ 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);
diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig
index f84718a44980..c14981daf943 100644
--- a/sound/core/seq/Kconfig
+++ b/sound/core/seq/Kconfig
@@ -60,4 +60,18 @@ config SND_SEQ_MIDI_EMUL
config SND_SEQ_VIRMIDI
tristate
+config SND_SEQ_UMP
+ bool "Support for UMP events"
+ default y if SND_SEQ_UMP_CLIENT
+ 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
+ tristate
+ def_tristate SND_UMP
+
endif # SND_SEQUENCER
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 3a2177a7e50c..990eec7c83ad 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -8,17 +8,20 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
seq_system.o seq_ports.o
snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
+snd-seq-$(CONFIG_SND_SEQ_UMP) += seq_ump_convert.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-ump-client-objs := 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/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 2d707afa1ef1..e3f9ea67d019 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -14,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>
@@ -70,6 +72,10 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
struct snd_seq_event *event,
int filter, int atomic, int hop);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static void free_ump_info(struct snd_seq_client *client);
+#endif
+
/*
*/
static inline unsigned short snd_seq_file_flags(struct file *file)
@@ -239,6 +245,7 @@ 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_irq(&clients_lock);
@@ -380,6 +387,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);
}
@@ -387,6 +397,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:
@@ -400,6 +419,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;
@@ -431,43 +451,54 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
err = 0;
snd_seq_fifo_lock(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;
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) {
@@ -590,6 +621,27 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
return 1;
}
+/* 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)
+{
+ 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;
+}
/*
* deliver an event to the specified destination.
@@ -626,22 +678,22 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
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;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) {
+ if (snd_seq_ev_is_ump(event)) {
+ result = snd_seq_deliver_from_ump(client, dest, dest_port,
+ event, atomic, hop);
+ goto __skip;
+ } else if (snd_seq_client_is_ump(dest)) {
+ result = snd_seq_deliver_to_ump(client, dest, dest_port,
+ event, atomic, hop);
+ goto __skip;
+ }
+ }
+#endif /* CONFIG_SND_SEQ_UMP */
- case KERNEL_CLIENT:
- if (dest_port->event_input == NULL)
- break;
- result = dest_port->event_input(event, direct,
- dest_port->private_data,
+ result = __snd_seq_deliver_single_event(dest, dest_port, event,
atomic, hop);
- break;
- default:
- break;
- }
__skip:
if (dest_port)
@@ -659,21 +711,20 @@ 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,
+ struct snd_seq_client_port *src_port,
+ int atomic, int hop)
{
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 */
/* 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 */
@@ -700,103 +751,40 @@ 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);
- 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 */
+ memcpy(event, &event_saved, saved_size);
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 */
+ struct snd_seq_client_port *src_port;
+ int ret = 0, ret2;
- 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;
+ src_port = snd_seq_port_use_ptr(client, event->source.port);
+ if (src_port) {
+ ret = __deliver_to_subscribers(client, event, src_port, atomic, hop);
+ snd_seq_port_unlock(src_port);
}
- event->dest = addr; /* restore */
- return (result < 0) ? result : num_ev;
-}
+ if (client->ump_endpoint_port < 0 ||
+ event->source.port == client->ump_endpoint_port)
+ return ret;
-/* 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 */
+ src_port = snd_seq_port_use_ptr(client, client->ump_endpoint_port);
+ if (!src_port)
+ return ret;
+ ret2 = __deliver_to_subscribers(client, event, src_port, atomic, hop);
+ snd_seq_port_unlock(src_port);
+ return ret2 < 0 ? ret2 : ret;
}
-#endif /* SUPPORT_BROADCAST */
-
/* deliver an event to the destination port(s).
* if the event is to subscribers or broadcast, the event is dispatched
@@ -826,15 +814,6 @@ 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);
@@ -865,7 +844,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.
@@ -889,7 +869,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 */
@@ -936,14 +916,7 @@ 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)
@@ -953,7 +926,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
/* 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);
}
@@ -1023,7 +997,8 @@ 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, handled;
- struct snd_seq_event event;
+ union __snd_seq_event __event;
+ struct snd_seq_event *ev = &__event.legacy;
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
return -ENXIO;
@@ -1049,49 +1024,66 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
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, &client->ioctl_mutex);
if (err < 0)
@@ -1159,6 +1151,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;
@@ -1231,6 +1229,7 @@ static void get_client_info(struct snd_seq_client *cptr,
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)
@@ -1243,6 +1242,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));
}
@@ -1277,14 +1277,21 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
if (client->type != client_info->type)
return -EINVAL;
+ /* check validity of midi_version field */
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3) &&
+ client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0)
+ return -EINVAL;
+
/* fill the info fields */
if (client_info->name[0])
strscpy(client->name, client_info->name, sizeof(client->name));
client->filter = client_info->filter;
client->event_lost = client_info->event_lost;
+ 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;
return 0;
}
@@ -1297,22 +1304,27 @@ 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) {
callback = info->kernel;
if (callback) {
@@ -1331,6 +1343,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);
@@ -1350,8 +1364,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;
}
@@ -2079,6 +2096,135 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
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;
+ 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 = snd_seq_client_use_ptr(client);
+ if (!cptr)
+ return -ENOENT;
+
+ mutex_lock(&cptr->ioctl_mutex);
+ if (!cptr->midi_version) {
+ err = -EBADFD;
+ goto error;
+ }
+
+ 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;
+ goto error;
+ }
+ if (copy_to_user(argp->info, p, size)) {
+ err = -EFAULT;
+ goto error;
+ }
+ } else {
+ if (cptr->type != USER_CLIENT) {
+ err = -EBADFD;
+ goto error;
+ }
+ if (!cptr->ump_info) {
+ cptr->ump_info = kcalloc(NUM_UMP_INFOS,
+ sizeof(void *), GFP_KERNEL);
+ if (!cptr->ump_info) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+ p = memdup_user(argp->info, size);
+ if (IS_ERR(p)) {
+ err = PTR_ERR(p);
+ goto error;
+ }
+ kfree(cptr->ump_info[type]);
+ terminate_ump_info_strings(p, type);
+ cptr->ump_info[type] = p;
+ }
+
+ error:
+ mutex_unlock(&cptr->ioctl_mutex);
+ snd_seq_client_unlock(cptr);
+ return err;
+}
+#endif
+
/* -------------------------------------------------------- */
static const struct ioctl_handler {
@@ -2086,6 +2232,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 },
@@ -2148,6 +2295,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;
@@ -2226,6 +2382,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
client->accept_input = 1;
client->accept_output = 1;
client->data.kernel.card = card;
+ client->user_pversion = SNDRV_SEQ_VERSION;
va_start(args, name_fmt);
vsnprintf(client->name, sizeof(client->name), name_fmt, args);
@@ -2274,10 +2431,12 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
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;
@@ -2390,6 +2549,21 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
}
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_unlock(cptr);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_put);
+
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_SND_PROC_FS
@@ -2435,6 +2609,17 @@ 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)
{
@@ -2442,18 +2627,34 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
mutex_lock(&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]\n",
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));
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,
@@ -2478,9 +2679,13 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
continue;
}
- snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+ 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");
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 8cdd0ee53fb1..915b1017286e 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -12,7 +12,6 @@
#include "seq_ports.h"
#include "seq_lock.h"
-
/* client manager */
struct snd_seq_user_client {
@@ -35,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 */
@@ -48,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 */
@@ -56,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 */
@@ -82,10 +88,29 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype);
+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 */
bool snd_seq_client_ioctl_lock(int clientid);
void snd_seq_client_ioctl_unlock(int clientid);
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 54723566ce24..1e35bf086a51 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -81,10 +81,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 8c18d8c4177e..9308194b2d9a 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -127,6 +127,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;
@@ -151,6 +152,7 @@ static int __init
register_client(void)
{
struct snd_seq_dummy_port *rec1, *rec2;
+ struct snd_seq_client *client;
int i;
if (ports < 1) {
@@ -164,6 +166,13 @@ register_client(void)
if (my_client < 0)
return my_client;
+ /* don't convert events but just pass-through */
+ client = snd_seq_kernel_client_get(my_client);
+ if (!client)
+ return -EINVAL;
+ client->filter = SNDRV_SEQ_FILTER_NO_CONVERT;
+ snd_seq_kernel_client_put(client);
+
/* create ports */
for (i = 0; i < ports; i++) {
rec1 = create_port(i, 0);
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 47ef6bc30c0e..174585bf59d2 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -63,8 +63,9 @@ 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;
@@ -72,10 +73,16 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
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)
@@ -91,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);
@@ -132,11 +154,27 @@ static int seq_copy_in_user(void *ptr, void *src, int 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;
len = get_var_len(event);
if (len < 0)
@@ -146,21 +184,35 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
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))
- return -EFAULT;
- return newlen;
- }
- err = snd_seq_dump_var_event(event,
- in_kernel ? seq_copy_in_kernel : seq_copy_in_user,
- &buf);
- return err < 0 ? err : newlen;
+ err = expand_var_event(event, 0, len, buf, in_kernel);
+ if (err < 0)
+ return err;
+ if (len != newlen)
+ memset(buf + len, 0, newlen - len);
+ 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
*/
@@ -288,6 +340,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
int ncells, err;
unsigned int extlen;
struct snd_seq_event_cell *cell;
+ int size;
*cellp = NULL;
@@ -305,7 +358,12 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
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)) {
@@ -323,7 +381,7 @@ 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,
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 7d7ff80f915e..7f7a2c0b187d 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -11,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 */
};
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 4589aac09154..44302d98950e 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -38,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;
@@ -168,8 +169,7 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
struct snd_rawmidi_params params;
/* open midi port */
- err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
- msynth->subdevice,
+ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
SNDRV_RAWMIDI_LFLG_INPUT,
&msynth->input_rfile);
if (err < 0) {
@@ -212,8 +212,7 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
struct snd_rawmidi_params params;
/* open midi port */
- err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
- msynth->subdevice,
+ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
SNDRV_RAWMIDI_LFLG_OUTPUT,
&msynth->output_rfile);
if (err < 0) {
@@ -328,6 +327,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;
@@ -367,6 +367,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;
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 25fcf5a2c71c..9b80f8275026 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -69,11 +69,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);
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) {
@@ -107,33 +111,34 @@ 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)
{
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);
@@ -143,6 +148,10 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
mutex_lock(&client->ports_mutex);
write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
+ if (p->addr.port == port) {
+ num = -EBUSY;
+ goto unlock;
+ }
if (p->addr.port > num)
break;
if (port < 0) /* auto-probe mode */
@@ -153,10 +162,12 @@ 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);
+ *port_ret = new_port;
+ unlock:
write_unlock_irq(&client->ports_lock);
mutex_unlock(&client->ports_mutex);
- return new_port;
+ return num;
}
/* */
@@ -345,6 +356,20 @@ 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;
+ }
+
return 0;
}
@@ -382,6 +407,10 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
info->time_queue = port->time_queue;
}
+ /* UMP direction and group */
+ info->direction = port->direction;
+ info->ump_group = port->ump_group;
+
return 0;
}
diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h
index b1f2c4943174..b111382f697a 100644
--- a/sound/core/seq/seq_ports.h
+++ b/sound/core/seq/seq_ports.h
@@ -42,6 +42,17 @@ struct snd_seq_port_subs_info {
int (*close)(void *private_data, struct snd_seq_port_subscribe *info);
};
+/* context for converting from legacy control event to UMP packet */
+struct snd_seq_ump_midi2_bank {
+ bool rpn_set;
+ bool nrpn_set;
+ bool bank_set;
+ unsigned char cc_rpn_msb, cc_rpn_lsb;
+ unsigned char cc_nrpn_msb, cc_nrpn_lsb;
+ unsigned char cc_data_msb, cc_data_lsb;
+ unsigned char cc_bank_msb, cc_bank_lsb;
+};
+
struct snd_seq_client_port {
struct snd_seq_addr addr; /* client/port number */
@@ -72,6 +83,13 @@ struct snd_seq_client_port {
int midi_voices;
int synth_voices;
+ /* UMP direction and group */
+ unsigned char direction;
+ unsigned char ump_group;
+
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct snd_seq_ump_midi2_bank midi2_bank[16]; /* per channel */
+#endif
};
struct snd_seq_client;
@@ -86,8 +104,9 @@ 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);
+/* 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);
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index 32c2d9b57751..80267290190d 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -85,6 +85,7 @@ void snd_seq_system_broadcast(int client, int port, int type)
ev.type = type;
snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 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)
diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c
new file mode 100644
index 000000000000..fe21c801af74
--- /dev/null
+++ b/sound/core/seq/seq_ump_client.c
@@ -0,0 +1,541 @@
+// 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
+};
+
+/* object per UMP group; corresponding to a sequencer port */
+struct seq_ump_group {
+ int group; /* group index (0-based) */
+ unsigned int dir_bits; /* directions */
+ bool active; /* activeness */
+ char name[64]; /* seq port name */
+};
+
+/* 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 */
+ struct seq_ump_group groups[SNDRV_UMP_MAX_GROUPS]; /* table of groups */
+ 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 = 0;
+
+ mutex_lock(&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)
+ goto unlock;
+ }
+ client->opened[dir]++;
+ unlock:
+ mutex_unlock(&ump->open_mutex);
+ return err;
+}
+
+/* close the rawmidi */
+static int seq_ump_client_close(struct seq_ump_client *client, int dir)
+{
+ struct snd_ump_endpoint *ump = client->ump;
+
+ mutex_lock(&ump->open_mutex);
+ if (!--client->opened[dir])
+ if (dir == STR_OUT)
+ snd_rawmidi_kernel_release(&client->out_rfile);
+ mutex_unlock(&ump->open_mutex);
+ 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 seq_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;
+ 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 (%s)",
+ group->group + 1, group->name);
+ else
+ sprintf(port->name, "Group %d", group->group + 1);
+}
+
+/* create a new sequencer port per UMP group */
+static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
+{
+ struct seq_ump_group *group = &client->groups[group_index];
+ struct snd_seq_port_info *port;
+ struct snd_seq_port_callback pcallbacks;
+ int err;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ 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;
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ error:
+ kfree(port);
+ return err;
+}
+
+/* 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, *new;
+ int i, err;
+
+ old = kzalloc(sizeof(*old), GFP_KERNEL);
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!old || !new)
+ goto error;
+
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
+ old->addr.client = client->seq_client;
+ old->addr.port = i;
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_GET_PORT_INFO,
+ old);
+ if (err < 0)
+ goto error;
+ fill_port_info(new, client, &client->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)
+ goto error;
+ /* notify to system port */
+ snd_seq_system_client_ev_port_change(client->seq_client, i);
+ }
+ error:
+ kfree(new);
+ kfree(old);
+}
+
+/* update dir_bits and active flag for all groups in the client */
+static void update_group_attrs(struct seq_ump_client *client)
+{
+ struct snd_ump_block *fb;
+ struct seq_ump_group *group;
+ int i;
+
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
+ group = &client->groups[i];
+ *group->name = 0;
+ group->dir_bits = 0;
+ group->active = 0;
+ group->group = i;
+ }
+
+ list_for_each_entry(fb, &client->ump->block_list, list) {
+ if (fb->info.first_group < 0 ||
+ fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS)
+ break;
+ group = &client->groups[fb->info.first_group];
+ for (i = 0; i < fb->info.num_groups; i++, group++) {
+ if (fb->info.active)
+ group->active = 1;
+ switch (fb->info.direction) {
+ case SNDRV_UMP_DIR_INPUT:
+ group->dir_bits |= (1 << STR_IN);
+ break;
+ case SNDRV_UMP_DIR_OUTPUT:
+ group->dir_bits |= (1 << STR_OUT);
+ break;
+ case SNDRV_UMP_DIR_BIDIRECTION:
+ group->dir_bits |= (1 << STR_OUT) | (1 << STR_IN);
+ break;
+ }
+ if (!*fb->info.name)
+ continue;
+ if (!*group->name) {
+ /* store the first matching name */
+ strscpy(group->name, fb->info.name,
+ sizeof(group->name));
+ } else {
+ /* when overlapping, concat names */
+ strlcat(group->name, ", ", sizeof(group->name));
+ strlcat(group->name, fb->info.name,
+ sizeof(group->name));
+ }
+ }
+ }
+}
+
+/* create a UMP Endpoint port */
+static int create_ump_endpoint_port(struct seq_ump_client *client)
+{
+ struct snd_seq_port_info *port;
+ 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;
+ strcpy(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);
+ kfree(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);
+}
+
+/* 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_group_attrs(client);
+ update_port_infos(client);
+}
+
+/* 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);
+ return 0;
+}
+
+/* UMP protocol change notification; just update the midi_version field */
+static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump)
+{
+ if (!ump->seq_client)
+ return -ENODEV;
+ setup_client_midi_version(ump->seq_client);
+ return 0;
+}
+
+static const struct snd_seq_ump_ops seq_ump_ops = {
+ .input_receive = seq_ump_input_receive,
+ .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);
+ update_group_attrs(client);
+
+ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
+ err = seq_ump_group_init(client, p);
+ if (err < 0)
+ goto error;
+ }
+
+ 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..eb1d86ff6166
--- /dev/null
+++ b/sound/core/seq/seq_ump_convert.c
@@ -0,0 +1,1206 @@
+// 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.parm1 << 7) | val->system.parm2;
+}
+
+/* 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;
+
+ 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:
+ 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;
+ 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;
+ 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 << 4) {
+ 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:
+ 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)) {
+ if (snd_seq_client_is_midi2(dest) &&
+ type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE)
+ return cvt_ump_midi1_to_midi2(dest, dest_port,
+ event, atomic, hop);
+ else if (!snd_seq_client_is_midi2(dest) &&
+ 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.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.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.status = status;
+ data->system.parm1 = (event->data.control.value >> 7) & 0x7f;
+ data->system.parm1 = event->data.control.value & 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;
+}
+
+/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
+static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
+ union snd_ump_midi2_msg *data)
+{
+ 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;
+ cc->rpn_set = 0;
+ cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
+ } else {
+ data->rpn.status = UMP_MSG_STATUS_NRPN;
+ data->rpn.bank = cc->cc_nrpn_msb;
+ data->rpn.index = cc->cc_nrpn_lsb;
+ cc->nrpn_set = 0;
+ cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
+ }
+ data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
+ cc->cc_data_lsb);
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+}
+
+/* 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 snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
+
+ /* process special CC's (bank/rpn/nrpn) */
+ switch (index) {
+ case UMP_CC_RPN_MSB:
+ cc->rpn_set = 1;
+ cc->cc_rpn_msb = val;
+ return 0; // skip
+ case UMP_CC_RPN_LSB:
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = val;
+ return 0; // skip
+ case UMP_CC_NRPN_MSB:
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_msb = val;
+ return 0; // skip
+ case UMP_CC_NRPN_LSB:
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = val;
+ return 0; // skip
+ case UMP_CC_DATA:
+ cc->cc_data_msb = val;
+ return 0; // skip
+ 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 = val;
+ if (!(cc->rpn_set || cc->nrpn_set))
+ return 0; // skip
+ fill_rpn(cc, data);
+ return 1;
+ }
+
+ 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 snd_seq_ump_midi2_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;
+ cc->cc_bank_msb = cc->cc_bank_lsb = 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 snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
+ unsigned char msb, lsb;
+
+ 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:
+ cc->cc_rpn_msb = msb;
+ fallthrough;
+ case UMP_CC_RPN_LSB:
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = lsb;
+ return 0; // skip
+ case UMP_CC_NRPN_MSB:
+ cc->cc_nrpn_msb = msb;
+ fallthrough;
+ case UMP_CC_NRPN_LSB:
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = lsb;
+ return 0; // skip
+ case UMP_CC_DATA:
+ cc->cc_data_msb = msb;
+ fallthrough;
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_lsb = lsb;
+ if (!(cc->rpn_set || cc->nrpn_set))
+ return 0; // skip
+ fill_rpn(cc, data);
+ return 1;
+ }
+
+ 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_1p_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 },
+};
+
+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[6], *xbuf;
+ int offset = 0;
+ int len, err;
+
+ if (!snd_seq_ev_is_variable(event))
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ for (;;) {
+ len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
+ if (len <= 0)
+ break;
+ if (WARN_ON(len > 6))
+ break;
+ offset += len;
+ xbuf = buf;
+ if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
+ status = UMP_SYSEX_STATUS_START;
+ xbuf++;
+ len--;
+ if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
+ status = UMP_SYSEX_STATUS_SINGLE;
+ len--;
+ }
+ } else {
+ if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
+ status = UMP_SYSEX_STATUS_END;
+ len--;
+ } else {
+ status = UMP_SYSEX_STATUS_CONTINUE;
+ }
+ }
+ 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;
+ }
+ 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 (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))
+ return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop);
+ else
+ return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop);
+}
diff --git a/sound/core/seq/seq_ump_convert.h b/sound/core/seq/seq_ump_convert.h
new file mode 100644
index 000000000000..6c146d803280
--- /dev/null
+++ b/sound/core/seq/seq_ump_convert.h
@@ -0,0 +1,22 @@
+// 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);
+
+#endif /* __SEQ_UMP_CONVERT_H */
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index f5cae49500c8..1b9260108e48 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -385,6 +385,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;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index e08a37c23add..9d0d2a5c2e15 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1246,6 +1246,7 @@ 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);
list_for_each_entry(timer, &snd_timer_list, device_list) {
@@ -1269,10 +1270,13 @@ 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)
+ spin_lock_irq(&timer->lock);
+ resolution = snd_timer_hw_resolution(timer);
+ spin_unlock_irq(&timer->lock);
+ 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");
@@ -1662,7 +1666,9 @@ static int snd_timer_user_ginfo(struct file *file,
ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
strscpy(ginfo->id, t->id, sizeof(ginfo->id));
strscpy(ginfo->name, t->name, sizeof(ginfo->name));
- ginfo->resolution = t->hw.resolution;
+ spin_lock_irq(&t->lock);
+ ginfo->resolution = snd_timer_hw_resolution(t);
+ spin_unlock_irq(&t->lock);
if (t->hw.resolution_min > 0) {
ginfo->resolution_min = t->hw.resolution_min;
ginfo->resolution_max = t->hw.resolution_max;
@@ -1817,7 +1823,9 @@ static int snd_timer_user_info(struct file *file,
info->flags |= SNDRV_TIMER_FLG_SLAVE;
strscpy(info->id, t->id, sizeof(info->id));
strscpy(info->name, t->name, sizeof(info->name));
- info->resolution = t->hw.resolution;
+ spin_lock_irq(&t->lock);
+ info->resolution = snd_timer_hw_resolution(t);
+ spin_unlock_irq(&t->lock);
if (copy_to_user(_info, info, sizeof(*_info)))
err = -EFAULT;
kfree(info);
diff --git a/sound/core/ump.c b/sound/core/ump.c
new file mode 100644
index 000000000000..246348766ec1
--- /dev/null
+++ b/sound/core/ump.c
@@ -0,0 +1,1164 @@
+// 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);
+#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)
+{
+}
+#endif
+
+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 < 0 || 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 %d-%d",
+ 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%02x%02x%02x%02x\n",
+ ump->info.sw_revision[0],
+ ump->info.sw_revision[1],
+ ump->info.sw_revision[2],
+ ump->info.sw_revision[3]);
+ }
+ 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");
+ }
+}
+
+/*
+ * 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);
+}
+
+/* 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);
+ 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=%02x%02x%02x%02x\n",
+ ump->info.manufacturer_id,
+ ump->info.family_id,
+ ump->info.model_id,
+ ump->info.sw_revision[0],
+ ump->info.sw_revision[1],
+ ump->info.sw_revision[2],
+ ump->info.sw_revision[3]);
+ return 1; /* finished */
+}
+
+/* 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)
+{
+ return ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
+ buf->raw, 2);
+}
+
+/* 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)
+{
+ return ump_append_string(ump, ump->info.product_id,
+ sizeof(ump->info.product_id),
+ buf->raw, 2);
+}
+
+/* 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)
+{
+ protocol &= ump->info.protocol_caps;
+ if (protocol == ump->info.protocol)
+ 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;
+ info->flags = buf->fb_info.midi_10;
+ 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);
+}
+
+/* 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)
+ 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;
+
+ 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)
+ 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");
+
+ /* 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");
+
+ /* 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;
+ }
+
+ 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 = substream->number;
+ int err;
+
+ mutex_lock(&ump->open_mutex);
+ if (ump->legacy_substreams[dir][group]) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ 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)
+ goto unlock;
+ }
+ ump->legacy_out_opens++;
+ snd_ump_convert_reset(&ump->out_cvts[group]);
+ }
+ spin_lock_irq(&ump->legacy_locks[dir]);
+ ump->legacy_substreams[dir][group] = substream;
+ spin_unlock_irq(&ump->legacy_locks[dir]);
+ unlock:
+ mutex_unlock(&ump->open_mutex);
+ 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 = substream->number;
+
+ mutex_lock(&ump->open_mutex);
+ spin_lock_irq(&ump->legacy_locks[dir]);
+ ump->legacy_substreams[dir][group] = NULL;
+ spin_unlock_irq(&ump->legacy_locks[dir]);
+ if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+ if (!--ump->legacy_out_opens)
+ snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
+ }
+ mutex_unlock(&ump->open_mutex);
+ 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 char c;
+ int group, size = 0;
+ unsigned long flags;
+
+ if (!ump->out_cvts || !ump->legacy_out_opens)
+ return 0;
+
+ spin_lock_irqsave(&ump->legacy_locks[dir], flags);
+ for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
+ substream = ump->legacy_substreams[dir][group];
+ if (!substream)
+ continue;
+ ctx = &ump->out_cvts[group];
+ while (!ctx->ump_bytes &&
+ snd_rawmidi_transmit(substream, &c, 1) > 0)
+ snd_ump_convert_to_ump(ctx, group, ump->info.protocol, c);
+ if (ctx->ump_bytes && ctx->ump_bytes <= count) {
+ size = ctx->ump_bytes;
+ memcpy(buffer, ctx->ump, size);
+ ctx->ump_bytes = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
+ 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;
+ unsigned long flags;
+ const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
+ int size;
+
+ size = snd_ump_convert_from_ump(src, buf, &group);
+ if (size <= 0)
+ return;
+ spin_lock_irqsave(&ump->legacy_locks[dir], flags);
+ substream = ump->legacy_substreams[dir][group];
+ if (substream)
+ snd_rawmidi_receive(substream, buf, size);
+ spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
+}
+
+int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
+ char *id, int device)
+{
+ struct snd_rawmidi *rmidi;
+ bool input, output;
+ int err;
+
+ ump->out_cvts = kcalloc(16, sizeof(*ump->out_cvts), GFP_KERNEL);
+ if (!ump->out_cvts)
+ return -ENOMEM;
+
+ 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 ? 16 : 0, input ? 16 : 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_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..fb61df424a87
--- /dev/null
+++ b/sound/core/ump_convert.c
@@ -0,0 +1,505 @@
+// 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 1;
+ 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 fill_rpn(struct ump_cvt_to_ump_bank *cc,
+ union snd_ump_midi2_msg *midi2)
+{
+ 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;
+ cc->rpn_set = 0;
+ cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
+ } else {
+ midi2->rpn.status = UMP_MSG_STATUS_NRPN;
+ midi2->rpn.bank = cc->cc_nrpn_msb;
+ midi2->rpn.index = cc->cc_nrpn_lsb;
+ cc->nrpn_set = 0;
+ cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
+ }
+ midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
+ cc->cc_data_lsb);
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+}
+
+/* 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;
+
+ 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:
+ cc->rpn_set = 1;
+ cc->cc_rpn_msb = buf[2];
+ return 0; // skip
+ case UMP_CC_RPN_LSB:
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = buf[2];
+ return 0; // skip
+ case UMP_CC_NRPN_MSB:
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_msb = buf[2];
+ return 0; // skip
+ case UMP_CC_NRPN_LSB:
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = buf[2];
+ return 0; // skip
+ case UMP_CC_DATA:
+ cc->cc_data_msb = buf[2];
+ return 0; // skip
+ 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 = buf[2];
+ if (cc->rpn_set || cc->nrpn_set)
+ fill_rpn(cc, midi2);
+ else
+ return 0; // skip
+ break;
+ 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;
+ cc->cc_bank_msb = cc->cc_bank_lsb = 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/drivers/Kconfig b/sound/drivers/Kconfig
index be3009746f3a..41c171468c1e 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -109,6 +109,22 @@ config SND_ALOOP
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"
+ 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
@@ -128,6 +144,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
@@ -152,6 +169,7 @@ config 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
@@ -185,6 +203,7 @@ config 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
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index b60303180a1b..2c0c7092d396 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -8,6 +8,7 @@ snd-dummy-objs := dummy.o
snd-aloop-objs := aloop.o
snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
+snd-pcmtest-objs := pcmtest.o
snd-portman2x4-objs := portman2x4.o
snd-serial-u16550-objs := serial-u16550.o
snd-serial-generic-objs := serial-generic.o
@@ -17,6 +18,7 @@ snd-virmidi-objs := virmidi.o
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
diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c
new file mode 100644
index 000000000000..2ae912a64d16
--- /dev/null
+++ b/sound/drivers/pcmtest.c
@@ -0,0 +1,727 @@
+// 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/timer.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+
+#define DEVNAME "pcmtestd"
+#define CARD_NAME "pcm-test-card"
+#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 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 " CARD_NAME " soundcard");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard");
+module_param(enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " 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");
+
+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;
+ struct timer_list timer_instance;
+};
+
+static struct pcmtst *pcmtst;
+
+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),
+ .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;
+ 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++) {
+ current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, i % channels)];
+ if (!current_byte)
+ break;
+ ch_num = i % channels;
+ 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_user and copy_kernel
+ * 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, i % channels)] =
+ 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 = from_timer(v_iter, data, timer_instance);
+ substream = v_iter->substream;
+
+ 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);
+ }
+ 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;
+
+ v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL);
+ if (!v_iter)
+ return -ENOMEM;
+
+ runtime->hw = snd_pcmtst_hw;
+ runtime->private_data = v_iter;
+ v_iter->substream = substream;
+ v_iter->buf_pos = 0;
+ v_iter->is_buf_corrupted = false;
+ v_iter->period_pos = 0;
+ v_iter->total_bytes = 0;
+
+ playback_capture_test = 0;
+ ioctl_reset_test = 0;
+
+ timer_setup(&v_iter->timer_instance, timer_timeout, 0);
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+ 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);
+ v_iter->substream = NULL;
+ playback_capture_test = !v_iter->is_buf_corrupted;
+ kfree(v_iter);
+ return 0;
+}
+
+static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcmtst_buf_iter *v_iter = runtime->private_data;
+
+ if (inject_trigger_err)
+ return -EINVAL;
+
+ v_iter->sample_bytes = runtime->sample_bits / 8;
+ v_iter->period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) {
+ v_iter->chan_block = runtime->dma_bytes / runtime->channels;
+ v_iter->interleaved = false;
+ } else {
+ v_iter->interleaved = true;
+ }
+ // 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 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)
+{
+ if (inject_prepare_err)
+ return -EINVAL;
+ 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 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,
+ .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,
+ .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;
+ strcpy(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;
+ 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;
+
+ strcpy(card->driver, "PCM-TEST Driver");
+ strcpy(card->shortname, "PCM-Test");
+ strcpy(card->longname, "PCM-Test virtual driver");
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int pdev_remove(struct platform_device *dev)
+{
+ snd_pcmtst_free(pcmtst);
+ return 0;
+}
+
+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_LICENSE("GPL");
+MODULE_AUTHOR("Ivan Orlov");
+module_init(mod_init);
+module_exit(mod_exit);
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 06a7ced218e2..2ba5962beb30 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -15,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;
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 6036a5edbcb8..6c93e6e4982c 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -9,7 +9,7 @@
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
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index 995302808c27..704ae2a5316b 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -9,7 +9,7 @@
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
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
index 448e972028d9..82241058ea14 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -11,7 +11,7 @@
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)
{
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index ffb6dd796243..dd4298876ac0 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -18,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;
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
index 6655af53b367..806f82c9ceee 100644
--- a/sound/firewire/isight.c
+++ b/sound/firewire/isight.c
@@ -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),
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index e0a2337e8f27..654e1a6050a9 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -69,4 +69,4 @@ EXPORT_SYMBOL(snd_fw_transaction);
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/motu/motu.c b/sound/firewire/motu/motu.c
index f8b7fe38751c..d73599eb7d5a 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -11,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 */
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index b496f87841ae..9523479fa94a 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -32,7 +32,7 @@
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");
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
index eb58d3fcf087..86880089de28 100644
--- a/sound/firewire/tascam/tascam.c
+++ b/sound/firewire/tascam/tascam.c
@@ -9,7 +9,7 @@
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[] = {
{
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 3c7af6558249..7f3a000fab0c 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -79,7 +79,10 @@ 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);
+ 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);
spin_unlock_irq(&bus->reg_lock);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index 6c043fbd606f..bbf7bcdb449a 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -645,6 +645,7 @@ struct hda_vendor_id {
};
static const struct hda_vendor_id hda_vendor_ids[] = {
+ { 0x0014, "Loongson" },
{ 0x1002, "ATI" },
{ 0x1013, "Cirrus Logic" },
{ 0x1057, "Motorola" },
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index fe3587547cfe..f258cb3a6895 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -17,7 +17,6 @@
#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>
@@ -358,7 +357,7 @@ 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_read = true,
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 1f56fd33b9af..2633a4bb1d85 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -150,7 +150,11 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev)
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;
}
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 6ffa48dd5983..f8159179e38d 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -23,6 +23,7 @@ menuconfig SND_ISA
bool "ISA sound devices"
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.
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 861958451ef5..787868c9e91b 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -26,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+
@@ -36,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
@@ -51,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
@@ -96,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
@@ -110,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
@@ -123,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
@@ -157,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.
@@ -193,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
@@ -205,6 +210,7 @@ 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
@@ -221,6 +227,7 @@ config SND_OXYGEN_LIB
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
@@ -246,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
@@ -257,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
@@ -290,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
@@ -307,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
@@ -468,7 +479,7 @@ config SND_EMU10K1
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/0404/1010/1212/1616/1820 soundcards.
@@ -491,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!.
@@ -501,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
@@ -511,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
@@ -525,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.
@@ -537,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.
@@ -569,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
@@ -624,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.
@@ -640,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
@@ -712,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.
@@ -753,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
@@ -764,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
@@ -808,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
@@ -830,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.
@@ -842,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.
@@ -852,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
@@ -863,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
@@ -873,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
@@ -889,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.
@@ -898,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
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 9afc5906d662..80a65b8ad7b9 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -2069,8 +2069,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
.dev_disconnect = snd_ac97_dev_disconnect,
};
- if (rac97)
- *rac97 = NULL;
+ if (!rac97)
+ return -EINVAL;
if (snd_BUG_ON(!bus || !template))
return -EINVAL;
if (snd_BUG_ON(template->num >= 4))
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index b8163f26004a..23adace1b969 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -34,7 +34,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.");
@@ -56,8 +55,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
*/
@@ -103,13 +100,14 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
enable_ir[dev], subsystem[dev]);
if (err < 0)
return err;
- emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
err = snd_emu10k1_pcm(emu, 0);
if (err < 0)
return err;
- err = snd_emu10k1_pcm_mic(emu, 1);
- 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;
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 9455df18f7b2..d36234b88fb4 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -33,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
@@ -59,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
@@ -116,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 = (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK;
- snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
- dcysusv = (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | 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);
}
@@ -138,8 +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,
- DCYSUSV_PHASE1_MASK | DCYSUSV_DECAYTIME_MASK | 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;
@@ -162,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;
@@ -192,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);
}
@@ -255,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;
if (val >= vp->reg.loopstart)
bp = best + V_OFF;
}
@@ -289,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++;
@@ -310,6 +310,7 @@ start_voice(struct snd_emux_voice *vp)
{
unsigned int temp;
int ch;
+ u32 psst, dsl, map, ccca, vtarget;
unsigned int addr, mapped_offset;
struct snd_midi_channel *chan;
struct snd_emu10k1 *hw;
@@ -347,114 +348,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, 0);
- snd_emu10k1_ptr_write(hw, VTFT, ch, VTFT_FILTERTARGET_MASK);
- snd_emu10k1_ptr_write(hw, CVCF, ch, CVCF_CURRENTFILTER_MASK);
- snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
- snd_emu10k1_ptr_write(hw, CPF, ch, 0);
-
- /* 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;
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);
+ 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,
- /* 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 | CVCF_CURRENTFILTER_MASK);
+ /* 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;
}
@@ -464,7 +449,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;
@@ -479,24 +464,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;
@@ -506,15 +492,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;
@@ -524,13 +508,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)
{
- snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);
+ 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 192208c291d6..58ed72de6403 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -57,46 +57,49 @@ 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, VTFT_FILTERTARGET_MASK);
- snd_emu10k1_ptr_write(emu, CVCF, ch, CVCF_CURRENTFILTER_MASK);
- snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
- snd_emu10k1_ptr_write(emu, CPF, ch, 0);
- snd_emu10k1_ptr_write(emu, CCR, ch, 0);
-
- 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, IFATN_FILTERCUTOFF_MASK | IFATN_ATTENUATION_MASK);
- snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
- snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
- snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */
- 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, A_CSBA, ch, 0);
- snd_emu10k1_ptr_write(emu, A_CSDC, ch, 0);
- snd_emu10k1_ptr_write(emu, A_CSFE, ch, 0);
- snd_emu10k1_ptr_write(emu, A_CSHG, ch, 0);
- snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
- snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+ 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);
}
}
@@ -150,22 +153,26 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir)
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);
- /* disable stop on loop end */
- 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 */
@@ -179,9 +186,11 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir)
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->emu_model) {
} else if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
@@ -392,41 +401,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;
}
@@ -798,7 +814,6 @@ 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;
@@ -855,9 +870,14 @@ 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);
- /* Optical -> ADAT I/O */
- emu->emu1010.optical_in = 1; /* IN_ADAT */
- emu->emu1010.optical_out = 1; /* OUT_ADAT */
+ 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);
@@ -880,262 +900,20 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
/* IRQ Enable: All off */
snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
- emu->emu1010.internal_clock = 1; /* 48000 */
+ 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, 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, EMU_HANA_DOCK_LEDS_2_LOCK | EMU_HANA_DOCK_LEDS_2_48K);
-
-#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_update_clock(emu);
- 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);
+ // 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);
-#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;
- }
-
return 0;
}
/*
@@ -1310,7 +1088,7 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
/* 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,
@@ -1323,7 +1101,7 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
* 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,
@@ -1337,7 +1115,7 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
* 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,
@@ -1349,7 +1127,7 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
* 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,
@@ -1359,30 +1137,33 @@ static const struct snd_emu_chip_details emu_chip_details[] = {
/* 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 */
{.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
@@ -1645,7 +1426,7 @@ static const 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} ,
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 549013a4a80b..759e66e1105a 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -43,7 +43,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;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 3f64ccab0e63..9904bcfee106 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -46,26 +46,45 @@ MODULE_PARM_DESC(high_res_gpr_volume, "GPR mixer controls use 31-bit range.");
* Tables
*/
-static const char * const 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 const char * const 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",
@@ -84,7 +103,8 @@ static const char * const creative_ins[16] = {
/* 0x0f */ NULL
};
-static const char * const 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",
@@ -103,7 +123,8 @@ static const char * const audigy_ins[16] = {
/* 0x0f */ NULL
};
-static const char * const 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",
@@ -120,6 +141,7 @@ static const char * const 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",
@@ -138,7 +160,8 @@ static const char * const creative_outs[32] = {
/* 0x1f */ NULL,
};
-static const char * const 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",
@@ -173,6 +196,18 @@ static const char * const 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 },
@@ -318,16 +353,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;
}
@@ -336,12 +367,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)
@@ -355,9 +384,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;
@@ -380,7 +416,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;
}
@@ -776,6 +811,34 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
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;
+ }
}
for (i = 0; i < icode->gpr_list_control_count; i++) {
/* FIXME: we need to check the WRITE access */
@@ -1116,48 +1179,56 @@ 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 = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
- ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
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 = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
- ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
- 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,
@@ -1191,35 +1262,50 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
* 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, 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;
@@ -1253,44 +1339,40 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
strcpy(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;
+ 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++;
@@ -1298,160 +1380,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))
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), A_C_40000000, A_EXTIN(A_EXTIN_AC97_R));
+ /* Audigy CD Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
+ 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), A_C_40000000, A_GPR(stereo_mix+1));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+ 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;
}
@@ -1476,18 +1572,6 @@ 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 = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, "Tone Control - Bass");
@@ -1506,36 +1590,39 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
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);
@@ -1544,90 +1631,70 @@ 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);
@@ -1639,11 +1706,17 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
dev_info(emu->card->dev, "EMU2 inputs on\n");
/* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */
- for (z = 0; z < 0x10; z++) {
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+ icode, &ptr, tmp, bit_shifter16, 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");
@@ -1653,102 +1726,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
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
@@ -1772,11 +1757,12 @@ 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);
@@ -1801,30 +1787,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) \
@@ -1836,7 +1806,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) \
@@ -1852,7 +1822,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;
@@ -1895,7 +1865,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* 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 */
@@ -1903,18 +1873,18 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/*
* 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;
@@ -2008,7 +1978,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);
@@ -2199,13 +2169,6 @@ 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 = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(ctl->id.name, "Tone Control - Bass");
@@ -2237,12 +2200,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));
@@ -2264,20 +2234,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
*/
@@ -2285,7 +2246,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))) {
@@ -2294,7 +2255,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);
@@ -2309,9 +2270,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);
}
@@ -2328,29 +2289,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
}
@@ -2364,21 +2325,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));
@@ -2522,9 +2473,9 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
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 = 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++) {
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 3ebc7c36a444..f9500cd50a4b 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -29,6 +29,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;
@@ -41,17 +59,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;
}
@@ -65,292 +80,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 const 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 const 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 const 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 const 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 - FPGA outputs going to Alice2 (Audigy) for
* capture (EMU32 + I2S links)
* Each destination has an enum mixer control to choose a data source
*/
-static const 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,
@@ -375,29 +452,199 @@ static const 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;
@@ -407,41 +654,41 @@ 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;
+ 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;
@@ -451,134 +698,69 @@ 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;
-}
-
-#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 const struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] = {
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
- 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),
+ change = (emu->emu1010.input_source[channel] != val);
+ if (change) {
+ emu->emu1010.input_source[channel] = val;
+ snd_emu1010_input_source_apply(emu, channel, val);
+ }
+ return change;
+}
+
+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 const struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] = {
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
- 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 const struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] = {
- EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0),
- EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1),
- EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2),
- 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;
}
@@ -586,7 +768,7 @@ 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;
val = ucontrol->value.integer.value[0];
cache = emu->emu1010.adc_pads;
@@ -602,23 +784,29 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
return 0;
}
+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 const struct snd_kcontrol_new snd_emu1010_adc_pads[] = {
- EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1),
- EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2),
- EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3),
- 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
@@ -626,7 +814,8 @@ static const 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;
}
@@ -634,163 +823,237 @@ 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) {
+ change = (cache != emu->emu1010.dac_pads);
+ if (change) {
snd_emu1010_fpga_write(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 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 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 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 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_internal_clock_info(struct snd_kcontrol *kcontrol,
+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);
+ change = (emu->emu1010.clock_source != 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 44.1kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
- /* Word Clock source, Internal 44.1kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- 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_source = val;
+ emu->emu1010.wclock = emu_ci->vals[val];
+
+ 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);
+
+ snd_emu1010_update_clock(emu);
}
- return change;
+ return change;
}
-static const struct snd_kcontrol_new snd_emu1010_internal_clock =
+static const struct snd_kcontrol_new snd_emu1010_clock_source =
{
- .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 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.clock_fallback = val;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 1 - val);
+ }
+ return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_clock_fallback =
+{
+ .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,
@@ -1039,22 +1302,19 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
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 const 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
@@ -1070,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:
@@ -1088,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;
}
@@ -1146,7 +1402,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)
@@ -1155,13 +1410,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;
}
@@ -1189,10 +1442,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));
@@ -1206,11 +1459,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));
}
}
@@ -1229,7 +1479,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)];
@@ -1237,12 +1486,10 @@ 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;
}
@@ -1266,13 +1513,13 @@ 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]);
}
@@ -1305,17 +1552,14 @@ 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;
}
@@ -1337,13 +1581,13 @@ 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]);
}
@@ -1368,7 +1612,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;
}
@@ -1378,13 +1622,10 @@ 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;
}
@@ -1399,17 +1640,18 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
spin_lock_irqsave(&emu->reg_lock, flags);
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]);
}
}
@@ -1443,7 +1685,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)];
@@ -1451,11 +1692,9 @@ 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;
}
@@ -1513,17 +1752,14 @@ 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;
}
@@ -1572,7 +1808,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;
}
@@ -1582,11 +1818,8 @@ 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;
}
@@ -1598,9 +1831,11 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *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;
+ uval = ucontrol->value.integer.value[0] & 0x1ffff;
+ val = uval * 0x8000U / 0xffffU;
if (mix->attn[0] != val) {
mix->attn[0] = val;
change = 1;
@@ -1654,7 +1889,7 @@ 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);
+ spin_lock_irqsave(&emu->emu_lock, flags);
if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
@@ -1675,7 +1910,7 @@ 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);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
return change;
}
@@ -1777,7 +2012,7 @@ 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;
const char * const *c;
@@ -2041,49 +2276,7 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
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 */
kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu);
if (!kctl)
@@ -2135,105 +2328,64 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
return err;
}
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- /* 1616(m) cardbus */
- int i;
+ 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]);
+ snd_emu1010_apply_sources(emu);
- for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
- if (err < 0)
- return err;
- }
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+ snd_ctl_new1(&snd_emu1010_clock_source, emu));
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++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+ if (!emu->card_capabilities->no_adat) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_input_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_adc_pads); 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); i++) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+ snd_ctl_new1(&snd_emu1010_optical_in, 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/emupcm.c b/sound/pci/emu10k1/emupcm.c
index e8d2f0f6fbb3..387288d623d7 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -76,65 +76,64 @@ static void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu,
snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
}
-static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
+static void snd_emu10k1_pcm_free_voices(struct snd_emu10k1_pcm *epcm)
{
- 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) % NUM_G];
- 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,
@@ -145,9 +144,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,
+ .list = capture_buffer_sizes,
.mask = 0
};
@@ -178,12 +177,22 @@ static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate)
}
}
+static const unsigned int audigy_capture_rates[9] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_audigy_capture_rates = {
+ .count = 9,
+ .list = audigy_capture_rates,
+ .mask = 0
+};
+
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;
@@ -196,6 +205,34 @@ 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) {
+ // This also sets the rate constraint by deleting SNDRV_PCM_RATE_KNOT
+ 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;
+ return;
+ }
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ emu->audigy ? &hw_constraints_audigy_capture_rates :
+ &hw_constraints_capture_rates);
+}
+
+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;
@@ -232,147 +269,109 @@ 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 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) {
- 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));
- /* 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, VTFT, voice, VTFT_FILTERTARGET_MASK);
- snd_emu10k1_ptr_write(emu, CVCF, voice, CVCF_CURRENTFILTER_MASK);
- snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
- snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
- snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
- 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);
+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)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ 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]);
spin_unlock_irqrestore(&emu->reg_lock, flags);
}
+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,
struct snd_pcm_hw_params *hw_params)
{
@@ -380,9 +379,19 @@ static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *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;
- err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params));
+ 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;
@@ -415,7 +424,6 @@ static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm;
- int i;
if (runtime->private_data == NULL)
return 0;
@@ -424,12 +432,7 @@ static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
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;
@@ -444,26 +447,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;
}
@@ -472,28 +477,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;
- channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
+ start_addr = epcm->start_addr >> 1; // 16-bit voices
- snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
- start_addr, start_addr + (channel_size / 2), NULL);
+ extra_size = runtime->period_size;
+ channel_size = runtime->buffer_size;
- /* 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]);
+ snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, true,
+ start_addr, start_addr + extra_size);
- 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]);
+ 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;
}
@@ -510,13 +512,12 @@ 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,
};
@@ -534,9 +535,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;
@@ -547,7 +556,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;
}
@@ -557,132 +566,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 | VTFT_FILTERTARGET_MASK);
- snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | CVCF_CURRENTFILTER_MASK);
- snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
- snd_emu10k1_voice_clear_loop_stop(emu, voice);
+ 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;
+
+ voice = evoice->number;
+ snd_emu10k1_playback_commit_pitch(emu, voice, evoice->epcm->pitch_target << 16);
+}
+
+static void snd_emu10k1_playback_stop_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, VTFT_FILTERTARGET_MASK);
- snd_emu10k1_ptr_write(emu, CVCF, voice, CVCF_CURRENTFILTER_MASK);
- snd_emu10k1_ptr_write(emu, IP, voice, 0);
+ snd_emu10k1_playback_commit_pitch(emu, voice, 0);
}
-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_set_running(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm)
{
- unsigned int ptr, period_pos;
+ epcm->running = 1;
+ snd_emu10k1_voice_intr_enable(emu, epcm->extra->number);
+}
- /* 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);
+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,
@@ -692,6 +750,8 @@ 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;
+ bool w_16 = snd_pcm_format_width(runtime->format) == 16;
+ bool stereo = runtime->channels == 2;
int result = 0;
/*
@@ -702,29 +762,23 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
spin_lock(&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]);
+ 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;
@@ -759,8 +813,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,
@@ -787,8 +843,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;
@@ -808,24 +866,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",
@@ -835,6 +896,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)
@@ -842,45 +946,62 @@ 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);
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);
- fallthrough;
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);
+ goto leave;
+ }
+
+ 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;
}
+leave:
spin_unlock(&emu->reg_lock);
return result;
}
@@ -920,9 +1041,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,
};
@@ -938,7 +1058,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_KNOT,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
@@ -958,13 +1078,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),
@@ -1025,13 +1143,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)
@@ -1043,13 +1177,21 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_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);
}
@@ -1073,18 +1215,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;
- err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- kfree(epcm);
- return err;
- }
- err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
+ 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);
@@ -1093,12 +1230,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;
@@ -1134,10 +1271,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;
}
@@ -1172,10 +1310,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;
}
@@ -1194,7 +1332,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)
@@ -1210,20 +1348,9 @@ 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) {
- /* TODO
- * 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,
@@ -1234,41 +1361,17 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
* 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 {
+ spin_lock_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))) {
@@ -1276,13 +1379,20 @@ 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];
+ spin_unlock_irq(&emu->reg_lock);
}
- epcm->capture_cr_val = emu->efx_voices_mask[0];
- epcm->capture_cr_val2 = emu->efx_voices_mask[1];
- spin_unlock_irq(&emu->reg_lock);
+ 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;
+ }
+ 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;
}
@@ -1433,10 +1543,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;
}
@@ -1445,7 +1553,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;
@@ -1455,12 +1562,7 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
bits++;
}
- // Check that the number of requested channels is a power of two
- // not bigger than the number of available channels.
- for (idx = 0; idx < nefxb; idx++)
- if (1 << idx == bits)
- break;
- if (idx >= nefxb)
+ if (bits == 9 || bits == 11 || bits == 13 || bits == 15 || bits > 16)
return -EINVAL;
spin_lock_irq(&emu->reg_lock);
@@ -1592,12 +1694,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;
@@ -1745,37 +1849,29 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
strcpy(pcm->name, "Multichannel Capture/PT Playback");
emu->pcm_efx = pcm;
- /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs
- * to these
- */
-
- 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;
- err = snd_ctl_add(emu->card, kctl);
- if (err < 0)
- return err;
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
64*1024, 64*1024);
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index bec72dc60a41..7e2cc532471f 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -66,158 +66,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 const char * const 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 const char * const 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 Capture 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;
- const char * const *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,
@@ -226,27 +168,40 @@ 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");
+ // 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);
@@ -273,37 +228,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);
}
}
@@ -365,21 +431,32 @@ 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)
{
@@ -390,7 +467,39 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
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);
+ }
}
}
@@ -399,13 +508,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);
}
}
@@ -414,16 +520,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);
}
}
}
@@ -485,7 +588,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];
@@ -493,7 +597,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);
}
}
@@ -501,13 +605,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);
}
@@ -563,15 +669,19 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
snd_card_rw_proc_new(emu->card, "ptr_regs00b", emu,
snd_emu_proc_ptr_reg_read00b,
snd_emu_proc_ptr_reg_write00);
- snd_card_rw_proc_new(emu->card, "ptr_regs20a", emu,
- snd_emu_proc_ptr_reg_read20a,
- snd_emu_proc_ptr_reg_write20);
- snd_card_rw_proc_new(emu->card, "ptr_regs20b", emu,
- snd_emu_proc_ptr_reg_read20b,
- snd_emu_proc_ptr_reg_write20);
- snd_card_rw_proc_new(emu->card, "ptr_regs20c", emu,
- snd_emu_proc_ptr_reg_read20c,
- snd_emu_proc_ptr_reg_write20);
+ 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
snd_card_ro_proc_new(emu->card, "emu10k1", emu, snd_emu10k1_proc_read);
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index cfb96a67aa35..a0d66ce3ee83 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -18,33 +18,42 @@
#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;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
if (reg & 0xff000000) {
unsigned char size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
- mask = ((1 << size) - 1) << offset;
+ mask = (1 << size) - 1;
- 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 & 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;
}
}
@@ -57,34 +66,65 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
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);
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);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
}
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;
+ unsigned long flags;
+
+ 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);
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ 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);
+ }
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ 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)
@@ -233,16 +273,13 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
return err;
}
-void 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 (snd_BUG_ON(reg > 0x3f))
return;
reg += 0x40; /* 0x40 upwards are registers. */
if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
return;
- spin_lock_irqsave(&emu->emu_lock, flags);
outw(reg, emu->port + A_GPIO);
udelay(10);
outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
@@ -250,24 +287,38 @@ void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
outw(value, emu->port + A_GPIO);
udelay(10);
outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
+}
+
+void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ snd_emu1010_fpga_write_locked(emu, reg, value);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
-void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
+static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value)
{
// The higest input pin is used as the designated interrupt trigger,
// so it needs to be masked out.
u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
- unsigned long flags;
if (snd_BUG_ON(reg > 0x3f))
return;
reg += 0x40; /* 0x40 upwards are registers. */
- spin_lock_irqsave(&emu->emu_lock, flags);
outw(reg, emu->port + A_GPIO);
udelay(10);
outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10);
*value = ((inw(emu->port + A_GPIO) >> 8) & mask);
+}
+
+void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ snd_emu1010_fpga_read_locked(emu, reg, value);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -276,14 +327,106 @@ void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
*/
void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
{
+ unsigned long flags;
+
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);
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8);
+ snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
+{
+ unsigned long flags;
+ u32 hi, lo;
+
+ if (snd_BUG_ON(dst & ~0x71f))
+ return 0;
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi);
+ snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return (hi << 8) | lo;
+}
+
+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_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
@@ -416,6 +559,7 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int
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;
@@ -453,6 +597,89 @@ void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voi
outl(sol, emu->port + DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
+#endif
+
+void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ 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);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ 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);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+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)
{
@@ -496,64 +723,3 @@ void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned
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 const u32 logMagTable[128] = {
- 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
- 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
- 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
- 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 const char logSlopeTable[128] = {
- 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
- 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
- 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
- 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 dfb44e5e69a7..a813ef8c2f8d 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -22,15 +22,18 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
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);
@@ -44,12 +47,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);
@@ -65,6 +69,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);
@@ -79,9 +84,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);
@@ -147,31 +151,11 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
}
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 edb3f1763719..20b07117574b 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -315,10 +315,8 @@ 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);
+ blk = search_empty(emu, runtime->dma_bytes);
if (blk == NULL) {
mutex_unlock(&hdr->block_mutex);
return NULL;
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
index 2435d3ba68f7..f3c78adf3248 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -18,46 +18,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 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,
};
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index cbeb8443492c..6939498e26f0 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -23,110 +23,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) {
+ 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);
@@ -139,14 +130,15 @@ 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);
+ do {
+ last = pvoice->last;
+ voice_free(emu, pvoice++);
+ } while (!last);
spin_unlock_irqrestore(&emu->voice_lock, flags);
return 0;
}
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index b5210abb5141..ce5faa620517 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -308,8 +308,8 @@ out:
}
#if IS_ENABLED(CONFIG_EFI)
-static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, unsigned int ambient,
- unsigned int r0, unsigned int status, unsigned int checksum)
+static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, __be32 ambient, __be32 r0,
+ __be32 status, __be32 checksum)
{
int ret;
@@ -745,7 +745,7 @@ err:
static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
{
- int halo_sts;
+ __be32 halo_sts;
int ret;
ret = cs35l41_init_dsp(cs35l41);
@@ -773,7 +773,7 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
&halo_sts, sizeof(halo_sts));
if (ret) {
- dev_err(cs35l41->dev, "Timeout waiting for HALO Core to start. State: %d\n",
+ dev_err(cs35l41->dev, "Timeout waiting for HALO Core to start. State: %u\n",
halo_sts);
goto clean_dsp;
}
@@ -835,34 +835,26 @@ static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
- unsigned int ret = 0;
-
- mutex_lock(&cs35l41->fw_mutex);
if (cs35l41->request_fw_load == ucontrol->value.integer.value[0])
- goto err;
+ return 0;
if (cs35l41->fw_request_ongoing) {
dev_dbg(cs35l41->dev, "Existing request not complete\n");
- ret = -EBUSY;
- goto err;
+ 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");
- ret = -EBUSY;
- goto err;
+ return -EBUSY;
}
cs35l41->fw_request_ongoing = true;
cs35l41->request_fw_load = ucontrol->value.integer.value[0];
schedule_work(&cs35l41->fw_load_work);
-err:
- mutex_unlock(&cs35l41->fw_mutex);
-
- return ret;
+ return 1;
}
static int cs35l41_fw_type_ctl_get(struct snd_kcontrol *kcontrol,
@@ -881,8 +873,12 @@ static int cs35l41_fw_type_ctl_put(struct snd_kcontrol *kcontrol,
struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
if (ucontrol->value.enumerated.item[0] < HDA_CS_DSP_NUM_FW) {
- cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
- return 0;
+ if (cs35l41->firmware_type != ucontrol->value.enumerated.item[0]) {
+ cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
+ return 1;
+ } else {
+ return 0;
+ }
}
return -EINVAL;
diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c
index 7826b1a12d7d..b44536fbba17 100644
--- a/sound/pci/hda/cs35l41_hda_i2c.c
+++ b/sound/pci/hda/cs35l41_hda_i2c.c
@@ -58,7 +58,7 @@ static struct i2c_driver cs35l41_i2c_driver = {
.pm = &cs35l41_hda_pm_ops,
},
.id_table = cs35l41_hda_i2c_id,
- .probe_new = cs35l41_hda_i2c_probe,
+ .probe = cs35l41_hda_i2c_probe,
.remove = cs35l41_hda_i2c_remove,
};
module_i2c_driver(cs35l41_i2c_driver);
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 890c2f7c33fc..b7ca2a83fbb0 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -10,7 +10,6 @@
#include <linux/module.h>
#include <linux/export.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3226691ac923..ef831770ca7d 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -237,6 +237,7 @@ enum {
AZX_DRIVER_CTHDA,
AZX_DRIVER_CMEDIA,
AZX_DRIVER_ZHAOXIN,
+ AZX_DRIVER_LOONGSON,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -360,6 +361,7 @@ static const char * const driver_short_names[] = {
[AZX_DRIVER_CTHDA] = "HDA Creative",
[AZX_DRIVER_CMEDIA] = "HDA C-Media",
[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
+ [AZX_DRIVER_LOONGSON] = "HDA Loongson",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
@@ -653,6 +655,13 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
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)
return -1; /* bogus (too early) interrupt */
@@ -1873,6 +1882,12 @@ static int azx_first_init(struct azx *chip)
if (chip->driver_type == AZX_DRIVER_GFHDMI)
bus->polling_mode = 1;
+ if (chip->driver_type == AZX_DRIVER_LOONGSON) {
+ bus->polling_mode = 1;
+ bus->not_use_interrupts = 1;
+ bus->access_sdnctl_in_dword = 1;
+ }
+
err = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio");
if (err < 0)
return err;
@@ -2809,6 +2824,11 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
/* Zhaoxin */
{ PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
+ /* Loongson HDAudio*/
+ {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA),
+ .driver_data = AZX_DRIVER_LOONGSON },
+ {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
+ .driver_data = AZX_DRIVER_LOONGSON },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 5c0b1a09fd57..260d3e64f658 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -4505,6 +4505,7 @@ static int patch_gf_hdmi(struct hda_codec *codec)
* patch entries
*/
static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_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),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index dabfdecece26..afe8253f9a4f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6757,6 +6757,9 @@ static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char
else
spec->gen.pcm_playback_hook = comp_generic_playback_hook;
break;
+ case HDA_FIXUP_ACT_FREE:
+ component_master_del(dev, &comp_master_ops);
+ break;
}
}
@@ -7120,6 +7123,10 @@ enum {
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,
@@ -8432,6 +8439,47 @@ static const struct hda_fixup alc269_fixups[] = {
.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,
@@ -9490,9 +9538,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
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),
- SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2),
+ 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),
@@ -9535,6 +9583,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS),
SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally RC71L_RC71L", ALC294_FIXUP_ASUS_ALLY),
SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS),
SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC),
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 1b078b789604..7ceaf6a7a77e 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -98,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;
@@ -185,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);
@@ -565,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};
diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
index 2f0e29ed5d63..d39233e0e070 100644
--- a/sound/pci/mixart/mixart_core.h
+++ b/sound/pci/mixart/mixart_core.h
@@ -231,7 +231,7 @@ 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] */
+ struct mixart_uid pipe_uid; /* could be an array[pipe_count], in theory */
} __attribute__((packed));
struct mixart_group_state_resp
@@ -314,7 +314,7 @@ struct mixart_clock_properties
u32 format;
u32 board_mask;
u32 nb_callers; /* set to 1 (see below) */
- struct mixart_uid uid_caller[1];
+ struct mixart_uid uid_caller;
} __attribute__((packed));
struct mixart_clock_properties_resp
@@ -401,8 +401,7 @@ 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 */
-
+ struct mixart_txx_stream_desc stream_desc; /* only one stream per command, but this could be an array, in theory */
} __attribute__((packed));
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index 10291c43cb18..2e3dfc1ff540 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -4,6 +4,7 @@
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/ppc/keywest.c b/sound/ppc/keywest.c
index 0c4f43963c75..dfc1fc9b701d 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -90,7 +90,7 @@ static struct i2c_driver keywest_driver = {
.driver = {
.name = "PMac Keywest Audio",
},
- .probe_new = keywest_probe,
+ .probe = keywest_probe,
.remove = keywest_remove,
.id_table = keywest_i2c_id,
};
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 848fbae26c3b..bfa9622e1ab1 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -75,9 +75,11 @@ source "sound/soc/bcm/Kconfig"
source "sound/soc/cirrus/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/kirkwood/Kconfig"
+source "sound/soc/loongson/Kconfig"
source "sound/soc/img/Kconfig"
source "sound/soc/intel/Kconfig"
source "sound/soc/mediatek/Kconfig"
@@ -91,6 +93,7 @@ source "sound/soc/sh/Kconfig"
source "sound/soc/sof/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"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 507eaed1d6a1..8376fdb217ed 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -43,8 +43,10 @@ obj-$(CONFIG_SND_SOC) += bcm/
obj-$(CONFIG_SND_SOC) += cirrus/
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/
@@ -59,6 +61,7 @@ obj-$(CONFIG_SND_SOC) += sh/
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/
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 08e42082f5e9..1dd8579e8034 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -71,6 +71,7 @@ config SND_SOC_AMD_RENOIR_MACH
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
@@ -81,6 +82,7 @@ config SND_SOC_AMD_VANGOGH_MACH
tristate "AMD Vangogh support for NAU8821 CS35L41"
select SND_SOC_NAU8821
select SND_SOC_CS35L41_SPI
+ select SND_AMD_ACP_CONFIG
depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
help
This option enables machine driver for Vangogh platform
@@ -136,7 +138,8 @@ config SND_SOC_AMD_PS
help
This option enables Audio Coprocessor i.e ACP v6.3 support on
AMD Pink sardine platform. By enabling this flag build will be
- triggered for ACP PCI driver, ACP PDM DMA driver.
+ 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".
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index 375417bd7d6e..7464ca2b596c 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -524,7 +524,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
| SND_SOC_DAIFMT_CBP_CFP,
.init = cz_da7219_init,
.dpcm_playback = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_da7219_play_ops,
SND_SOC_DAILINK_REG(designware1, dlgs, platform),
},
@@ -534,7 +534,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_da7219_cap_ops,
SND_SOC_DAILINK_REG(designware2, dlgs, platform),
},
@@ -544,7 +544,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_playback = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_max_play_ops,
SND_SOC_DAILINK_REG(designware3, mx, platform),
},
@@ -555,7 +555,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_dmic0_cap_ops,
SND_SOC_DAILINK_REG(designware3, adau, platform),
},
@@ -566,7 +566,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_dmic1_cap_ops,
SND_SOC_DAILINK_REG(designware2, adau, platform),
},
@@ -580,7 +580,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
| SND_SOC_DAIFMT_CBP_CFP,
.init = cz_rt5682_init,
.dpcm_playback = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_play_ops,
SND_SOC_DAILINK_REG(designware1, rt5682, platform),
},
@@ -590,7 +590,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_cap_ops,
SND_SOC_DAILINK_REG(designware2, rt5682, platform),
},
@@ -600,7 +600,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_playback = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_max_play_ops,
SND_SOC_DAILINK_REG(designware3, mx, platform),
},
@@ -611,7 +611,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_dmic0_cap_ops,
SND_SOC_DAILINK_REG(designware3, adau, platform),
},
@@ -622,7 +622,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.dpcm_capture = 1,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_rt5682_dmic1_cap_ops,
SND_SOC_DAILINK_REG(designware2, adau, platform),
},
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
index 89499542c803..5e56d3a53be7 100644
--- a/sound/soc/amd/acp-es8336.c
+++ b/sound/soc/amd/acp-es8336.c
@@ -149,7 +149,7 @@ static struct snd_soc_dai_link st_dai_es8336[] = {
.stream_name = "ES8336 HiFi Play",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
- .stop_dma_first = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.dpcm_capture = 1,
.dpcm_playback = 1,
.init = st_es8336_init,
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index b4dcce4fbae9..6da17140beea 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -795,13 +795,6 @@ SND_SOC_DAILINK_DEF(dmic_codec,
DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
/* Declare ACP CPU components */
-static struct snd_soc_dai_link_component dummy_codec[] = {
- {
- .name = "snd-soc-dummy",
- .dai_name = "snd-soc-dummy-dai",
- }
-};
-
static struct snd_soc_dai_link_component platform_component[] = {
{
.name = "acp_asoc_renoir.0",
@@ -912,8 +905,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].no_pcm = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->hs_codec_id == RT5682) {
links[i].codecs = rt5682;
@@ -943,8 +936,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].no_pcm = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->hs_codec_id == NAU8825) {
links[i].codecs = nau8825;
@@ -973,8 +966,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].no_pcm = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->amp_codec_id == RT1019) {
links[i].codecs = rt1019;
@@ -1005,8 +998,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].no_pcm = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->amp_codec_id == MAX98360A) {
links[i].codecs = max98360a;
@@ -1076,8 +1069,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].dpcm_capture = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->hs_codec_id == RT5682) {
links[i].codecs = rt5682;
@@ -1110,8 +1103,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].dpcm_capture = 1;
if (!drv_data->hs_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->hs_codec_id == NAU8825) {
links[i].codecs = nau8825;
@@ -1138,8 +1131,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].dpcm_playback = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->amp_codec_id == RT1019) {
links[i].codecs = rt1019;
@@ -1173,8 +1166,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].dpcm_playback = 1;
if (!drv_data->amp_codec_id) {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
if (drv_data->amp_codec_id == MAX98360A) {
links[i].codecs = max98360a;
@@ -1201,8 +1194,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].num_codecs = ARRAY_SIZE(dmic_codec);
} else {
/* Use dummy codec if codec id not specified */
- links[i].codecs = dummy_codec;
- links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].num_codecs = 1;
}
links[i].cpus = pdm_dmic;
links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index a0c84cd07fde..8154fbfd1229 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <linux/module.h>
#include "amd.h"
diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c
index 66ec6b6a5972..f8030b79ac17 100644
--- a/sound/soc/amd/acp/acp-pdm.c
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -176,7 +176,7 @@ static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
/* Disable DMIC interrupts */
ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
- ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
}
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
index 447612a7a762..f220378ec20e 100644
--- a/sound/soc/amd/acp/acp-platform.c
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -18,7 +18,6 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
-#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include "amd.h"
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
index 5c455cc04113..1b997837c7d8 100644
--- a/sound/soc/amd/acp/acp-rembrandt.c
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -204,23 +204,6 @@ static int acp6x_power_on(void __iomem *base)
return -ETIMEDOUT;
}
-static int acp6x_power_off(void __iomem *base)
-{
- u32 val;
- int timeout;
-
- writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
- base + ACP6X_PGFSM_CONTROL);
- timeout = 0;
- while (++timeout < 500) {
- val = readl(base + ACP6X_PGFSM_STATUS);
- if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
- return 0;
- udelay(1);
- }
- return -ETIMEDOUT;
-}
-
static int acp6x_reset(void __iomem *base)
{
u32 val;
@@ -299,14 +282,6 @@ static int rmb_acp_deinit(void __iomem *base)
}
writel(0x00, base + ACP_CONTROL);
-
- /* power off */
- ret = acp6x_power_off(base);
- if (ret) {
- pr_err("ACP power off failed\n");
- return ret;
- }
-
return 0;
}
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
index b3cbc7f19ec5..f188365fe214 100644
--- a/sound/soc/amd/acp/acp-renoir.c
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -169,17 +169,6 @@ static int acp3x_power_on(void __iomem *base)
return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
}
-static int acp3x_power_off(void __iomem *base)
-{
- u32 val;
-
- writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL);
-
- return readl_poll_timeout(base + ACP_PGFSM_STATUS, val,
- (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF,
- DELAY_US, ACP_TIMEOUT);
-}
-
static int acp3x_reset(void __iomem *base)
{
u32 val;
@@ -246,12 +235,6 @@ static int rn_acp_deinit(void __iomem *base)
return ret;
writel(0x00, base + ACP_CONTROL);
-
- /* power off */
- ret = acp3x_power_off(base);
- if (ret)
- return ret;
-
return 0;
}
static int renoir_audio_probe(struct platform_device *pdev)
diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
index 383973a12f6a..f2a5eaf2fa4d 100644
--- a/sound/soc/amd/ps/Makefile
+++ b/sound/soc/amd/ps/Makefile
@@ -3,7 +3,9 @@
snd-pci-ps-objs := pci-ps.o
snd-ps-pdm-dma-objs := ps-pdm-dma.o
snd-soc-ps-mach-objs := ps-mach.o
+snd-ps-sdw-dma-objs := 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
index dd36790b25ae..e96e6dc9d90f 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -2,7 +2,7 @@
/*
* AMD ALSA SoC PDM Driver
*
- * Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved.
+ * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved.
*/
#include <sound/acp63_chip_offset_byte.h>
@@ -10,7 +10,7 @@
#define ACP_DEVICE_ID 0x15E2
#define ACP63_REG_START 0x1240000
#define ACP63_REG_END 0x1250200
-#define ACP63_DEVS 3
+#define ACP63_DEVS 5
#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
@@ -53,11 +53,99 @@
/* time in ms for runtime suspend delay */
#define ACP_SUSPEND_DELAY_MS 2000
-#define ACP63_DMIC_ADDR 2
-#define ACP63_PDM_MODE_DEVS 3
-#define ACP63_PDM_DEV_MASK 1
#define ACP_DMIC_DEV 2
+/* ACP63_PDM_MODE_DEVS corresponds to platform devices count for ACP PDM configuration */
+#define ACP63_PDM_MODE_DEVS 3
+
+/*
+ * ACP63_SDW0_MODE_DEVS corresponds to platform devices count for
+ * SW0 SoundWire manager instance configuration
+ */
+#define ACP63_SDW0_MODE_DEVS 2
+
+/*
+ * ACP63_SDW0_SDW1_MODE_DEVS corresponds to platform devices count for SW0 + SW1 SoundWire manager
+ * instances configuration
+ */
+#define ACP63_SDW0_SDW1_MODE_DEVS 3
+
+/*
+ * ACP63_SDW0_PDM_MODE_DEVS corresponds to platform devices count for SW0 manager
+ * instance + ACP PDM controller configuration
+ */
+#define ACP63_SDW0_PDM_MODE_DEVS 4
+
+/*
+ * ACP63_SDW0_SDW1_PDM_MODE_DEVS corresponds to platform devices count for
+ * SW0 + SW1 SoundWire manager instances + ACP PDM controller configuration
+ */
+#define ACP63_SDW0_SDW1_PDM_MODE_DEVS 5
+#define ACP63_DMIC_ADDR 2
+#define ACP63_SDW_ADDR 5
+#define AMD_SDW_MAX_MANAGERS 2
+
+/* time in ms for acp timeout */
+#define ACP_TIMEOUT 500
+
+/* ACP63_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM controller */
+#define ACP63_PDM_DEV_CONFIG BIT(0)
+
+/* ACP63_SDW_DEV_CONFIG corresponds to platform device configuration for SDW manager instances */
+#define ACP63_SDW_DEV_CONFIG BIT(1)
+
+/*
+ * ACP63_SDW_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM + SoundWire
+ * manager instance combination.
+ */
+#define ACP63_SDW_PDM_DEV_CONFIG GENMASK(1, 0)
+#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 ACP_P1_AUDIO1_TX_THRESHOLD BIT(6)
+#define ACP_P1_AUDIO1_RX_THRESHOLD BIT(5)
+#define ACP_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP_P1_SDW_DMA_IRQ_MASK 0x60
+#define ACP63_SDW0_DMA_MAX_STREAMS 6
+#define ACP63_SDW1_DMA_MAX_STREAMS 2
+#define ACP_P1_AUDIO_TX_THRESHOLD 6
+#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * (i)))
+#define SDW1_DMA_IRQ_MASK(i) (ACP_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
+
enum acp_config {
ACP_CONFIG_0 = 0,
ACP_CONFIG_1,
@@ -77,6 +165,20 @@ enum acp_config {
ACP_CONFIG_15,
};
+enum amd_sdw0_channel {
+ ACP_SDW0_AUDIO0_TX = 0,
+ ACP_SDW0_AUDIO1_TX,
+ ACP_SDW0_AUDIO2_TX,
+ ACP_SDW0_AUDIO0_RX,
+ ACP_SDW0_AUDIO1_RX,
+ ACP_SDW0_AUDIO2_RX,
+};
+
+enum amd_sdw1_channel {
+ ACP_SDW1_AUDIO1_TX,
+ ACP_SDW1_AUDIO1_RX,
+};
+
struct pdm_stream_instance {
u16 num_pages;
u16 channels;
@@ -92,24 +194,77 @@ struct pdm_dev_data {
struct snd_pcm_substream *capture_stream;
};
-static inline u32 acp63_readl(void __iomem *base_addr)
-{
- return readl(base_addr);
-}
+struct sdw_dma_dev_data {
+ void __iomem *acp_base;
+ struct mutex *acp_lock; /* used to protect acp common register access */
+ struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
+};
-static inline void acp63_writel(u32 val, void __iomem *base_addr)
-{
- writel(val, base_addr);
-}
+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 - acp pci driver context
+ * @acp63_base: acp mmio base
+ * @res: resource
+ * @pdev: array of child platform device node structures
+ * @acp_lock: used to protect acp common registers
+ * @sdw_fw_node: SoundWire controller fw node handle
+ * @pdev_config: platform device configuration
+ * @pdev_count: platform devices count
+ * @pdm_dev_index: pdm platform device index
+ * @sdw_manager_count: SoundWire manager instance count
+ * @sdw0_dev_index: SoundWire Manager-0 platform device index
+ * @sdw1_dev_index: SoundWire Manager-1 platform device index
+ * @sdw_dma_dev_index: SoundWire DMA controller platform device index
+ * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
+ * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
+ * @acp_reset: flag set to true when bus reset is applied across all
+ * the active SoundWire manager instances
+ */
struct acp63_dev_data {
void __iomem *acp63_base;
struct resource *res;
struct platform_device *pdev[ACP63_DEVS];
struct mutex acp_lock; /* protect shared registers */
- u16 pdev_mask;
+ struct fwnode_handle *sdw_fw_node;
+ u16 pdev_config;
u16 pdev_count;
u16 pdm_dev_index;
+ u8 sdw_manager_count;
+ u16 sdw0_dev_index;
+ u16 sdw1_dev_index;
+ u16 sdw_dma_dev_index;
+ u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
+ u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
+ bool acp_reset;
};
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
index b1337b96ea8d..5b46dc8573f8 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -6,6 +6,7 @@
*/
#include <linux/pci.h>
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -14,66 +15,55 @@
#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 "acp63.h"
static int acp63_power_on(void __iomem *acp_base)
{
u32 val;
- int timeout;
- val = acp63_readl(acp_base + ACP_PGFSM_STATUS);
+ val = readl(acp_base + ACP_PGFSM_STATUS);
if (!val)
return val;
if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
- acp63_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
- timeout = 0;
- while (++timeout < 500) {
- val = acp63_readl(acp_base + ACP_PGFSM_STATUS);
- if (!val)
- return 0;
- udelay(1);
- }
- return -ETIMEDOUT;
+ writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
}
static int acp63_reset(void __iomem *acp_base)
{
u32 val;
- int timeout;
-
- acp63_writel(1, acp_base + ACP_SOFT_RESET);
- timeout = 0;
- while (++timeout < 500) {
- val = acp63_readl(acp_base + ACP_SOFT_RESET);
- if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
- break;
- cpu_relax();
- }
- acp63_writel(0, acp_base + ACP_SOFT_RESET);
- timeout = 0;
- while (++timeout < 500) {
- val = acp63_readl(acp_base + ACP_SOFT_RESET);
- if (!val)
- return 0;
- cpu_relax();
- }
- return -ETIMEDOUT;
+ 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, ACP_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, ACP_TIMEOUT);
}
static void acp63_enable_interrupts(void __iomem *acp_base)
{
- acp63_writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ 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)
{
- acp63_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
- ACP_EXTERNAL_INTR_STAT);
- acp63_writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
- acp63_writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+ 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)
@@ -85,7 +75,7 @@ static int acp63_init(void __iomem *acp_base, struct device *dev)
dev_err(dev, "ACP power on failed\n");
return ret;
}
- acp63_writel(0x01, acp_base + ACP_CONTROL);
+ writel(0x01, acp_base + ACP_CONTROL);
ret = acp63_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
@@ -105,64 +95,306 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
dev_err(dev, "ACP reset failed\n");
return ret;
}
- acp63_writel(0, acp_base + ACP_CONTROL);
+ writel(0, acp_base + ACP_CONTROL);
return 0;
}
+static irqreturn_t acp63_irq_thread(int irq, void *context)
+{
+ struct sdw_dma_dev_data *sdw_dma_data;
+ struct acp63_dev_data *adata = context;
+ u32 stream_index;
+ u16 pdev_index;
+
+ pdev_index = adata->sdw_dma_dev_index;
+ sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+
+ for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
+ if (adata->sdw0_dma_intr_stat[stream_index]) {
+ if (sdw_dma_data->sdw0_dma_stream[stream_index])
+ snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
+ adata->sdw0_dma_intr_stat[stream_index] = 0;
+ }
+ }
+ for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
+ if (adata->sdw1_dma_intr_stat[stream_index]) {
+ if (sdw_dma_data->sdw1_dma_stream[stream_index])
+ snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
+ adata->sdw1_dma_intr_stat[stream_index] = 0;
+ }
+ }
+ 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;
- u32 val;
+ struct amd_sdw_manager *amd_manager;
+ u32 ext_intr_stat, ext_intr_stat1;
+ u32 stream_id = 0;
+ u16 irq_flag = 0;
+ u16 sdw_dma_irq_flag = 0;
u16 pdev_index;
+ u16 index;
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);
+ pdev_index = adata->sdw0_dev_index;
+ amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->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);
+ pdev_index = adata->sdw1_dev_index;
+ amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->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;
+ }
- val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
- if (val & BIT(PDM_DMA_STAT)) {
+ if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
pdev_index = adata->pdm_dev_index;
ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
- acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ 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;
+ }
+ if (ext_intr_stat & ACP_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 = ACP_SDW0_AUDIO0_TX;
+ break;
+ case ACP_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO1_TX;
+ break;
+ case ACP_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO2_TX;
+ break;
+ case ACP_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO0_RX;
+ break;
+ case ACP_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO1_RX;
+ break;
+ case ACP_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO2_RX;
+ break;
+ }
+
+ adata->sdw0_dma_intr_stat[stream_id] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+
+ if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
+ writel(ACP_P1_AUDIO1_RX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+
+ if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
+ writel(ACP_P1_AUDIO1_TX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+
+ if (sdw_dma_irq_flag)
+ return IRQ_WAKE_THREAD;
+
+ if (irq_flag)
return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static int sdw_amd_scan_controller(struct device *dev)
+{
+ struct acp63_dev_data *acp_data;
+ struct fwnode_handle *link;
+ char name[32];
+ u32 sdw_manager_bitmap;
+ u8 count = 0;
+ u32 acp_sdw_power_mode = 0;
+ int index;
+ int ret;
+
+ acp_data = dev_get_drvdata(dev);
+ /*
+ * Current implementation is based on MIPI DisCo 2.0 spec.
+ * Found controller, find links supported.
+ */
+ ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list",
+ &sdw_manager_bitmap, 1);
+
+ if (ret) {
+ dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
+ return -EINVAL;
+ }
+ count = hweight32(sdw_manager_bitmap);
+ /* Check count is within bounds */
+ if (count > AMD_SDW_MAX_MANAGERS) {
+ dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ dev_dbg(dev, "No SoundWire Managers detected\n");
+ return -EINVAL;
+ }
+ dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count);
+ acp_data->sdw_manager_count = count;
+ for (index = 0; index < count; index++) {
+ snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
+ link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
+ if (!link) {
+ dev_err(dev, "Manager node %s not found\n", name);
+ return -EIO;
+ }
+
+ ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode);
+ if (ret)
+ return ret;
+ /*
+ * when SoundWire configuration is selected from acp pin config,
+ * based on manager instances count, acp init/de-init sequence should be
+ * executed as part of PM ops only when Bus reset is applied for the active
+ * SoundWire manager instances.
+ */
+ if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) {
+ acp_data->acp_reset = false;
+ return 0;
+ }
}
- return IRQ_NONE;
+ return 0;
}
-static void get_acp63_device_config(u32 config, struct pci_dev *pci,
- struct acp63_dev_data *acp_data)
+static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
{
struct acpi_device *dmic_dev;
+ struct acpi_device *sdw_dev;
const union acpi_object *obj;
bool is_dmic_dev = false;
+ bool is_sdw_dev = false;
+ int ret;
dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
if (dmic_dev) {
+ /* is_dmic_dev flag will be set when ACP PDM controller device exists */
if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type",
ACPI_TYPE_INTEGER, &obj) &&
obj->integer.value == ACP_DMIC_DEV)
is_dmic_dev = true;
}
+ sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
+ if (sdw_dev) {
+ acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
+ ret = sdw_amd_scan_controller(&pci->dev);
+ /* is_sdw_dev flag will be set when SoundWire Manager device exists */
+ if (!ret)
+ is_sdw_dev = true;
+ }
+ if (!is_dmic_dev && !is_sdw_dev)
+ return -ENODEV;
+ dev_dbg(&pci->dev, "Audio Mode %d\n", config);
switch (config) {
- case ACP_CONFIG_0:
- case ACP_CONFIG_1:
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ if (is_dmic_dev) {
+ acp_data->pdev_config = ACP63_PDM_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+ }
+ break;
case ACP_CONFIG_2:
case ACP_CONFIG_3:
- case ACP_CONFIG_9:
- case ACP_CONFIG_15:
- dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+ if (is_sdw_dev) {
+ switch (acp_data->sdw_manager_count) {
+ case 1:
+ acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
+ break;
+ case 2:
+ acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
break;
- default:
- if (is_dmic_dev) {
- acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ if (is_dmic_dev && is_sdw_dev) {
+ switch (acp_data->sdw_manager_count) {
+ case 1:
+ acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
+ break;
+ case 2:
+ acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (is_dmic_dev) {
+ acp_data->pdev_config = ACP63_PDM_DEV_CONFIG;
acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+ } else if (is_sdw_dev) {
+ switch (acp_data->sdw_manager_count) {
+ case 1:
+ acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
+ break;
+ case 2:
+ acp_data->pdev_config = ACP63_SDW_DEV_CONFIG;
+ acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
+ break;
+ default:
+ return -EINVAL;
+ }
}
break;
+ default:
+ break;
}
+ return 0;
}
static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
@@ -186,6 +418,7 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
{
+ struct acp_sdw_pdata *sdw_pdata;
struct platform_device_info pdevinfo[ACP63_DEVS];
struct device *parent;
int index;
@@ -193,9 +426,9 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
parent = &pci->dev;
dev_dbg(&pci->dev,
- "%s pdev_mask:0x%x pdev_count:0x%x\n", __func__, adata->pdev_mask,
+ "%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config,
adata->pdev_count);
- if (adata->pdev_mask) {
+ if (adata->pdev_config) {
adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
if (!adata->res) {
ret = -ENOMEM;
@@ -207,8 +440,8 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
memset(&pdevinfo, 0, sizeof(pdevinfo));
}
- switch (adata->pdev_mask) {
- case ACP63_PDM_DEV_MASK:
+ switch (adata->pdev_config) {
+ case ACP63_PDM_DEV_CONFIG:
adata->pdm_dev_index = 0;
acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
0, adata->res, 1, NULL, 0);
@@ -217,8 +450,104 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
0, NULL, 0, NULL, 0);
break;
+ case ACP63_SDW_DEV_CONFIG:
+ if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) {
+ sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
+ GFP_KERNEL);
+ if (!sdw_pdata) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ sdw_pdata->instance = 0;
+ sdw_pdata->acp_sdw_lock = &adata->acp_lock;
+ adata->sdw0_dev_index = 0;
+ adata->sdw_dma_dev_index = 1;
+ acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
+ "amd_sdw_manager", 0, adata->res, 1,
+ sdw_pdata, sizeof(struct acp_sdw_pdata));
+ acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+ } else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) {
+ sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
+ GFP_KERNEL);
+ if (!sdw_pdata) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ sdw_pdata[0].instance = 0;
+ sdw_pdata[1].instance = 1;
+ sdw_pdata[0].acp_sdw_lock = &adata->acp_lock;
+ sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
+ sdw_pdata->acp_sdw_lock = &adata->acp_lock;
+ adata->sdw0_dev_index = 0;
+ adata->sdw1_dev_index = 1;
+ adata->sdw_dma_dev_index = 2;
+ acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
+ "amd_sdw_manager", 0, adata->res, 1,
+ &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
+ acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+ "amd_sdw_manager", 1, adata->res, 1,
+ &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
+ acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+ }
+ break;
+ case ACP63_SDW_PDM_DEV_CONFIG:
+ if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) {
+ sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
+ GFP_KERNEL);
+ if (!sdw_pdata) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ sdw_pdata->instance = 0;
+ sdw_pdata->acp_sdw_lock = &adata->acp_lock;
+ adata->pdm_dev_index = 0;
+ adata->sdw0_dev_index = 1;
+ adata->sdw_dma_dev_index = 2;
+ acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, NULL, 0);
+ acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+ "amd_sdw_manager", 0, adata->res, 1,
+ sdw_pdata, sizeof(struct acp_sdw_pdata));
+ acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+ acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ } else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) {
+ sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
+ GFP_KERNEL);
+ if (!sdw_pdata) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+ sdw_pdata[0].instance = 0;
+ sdw_pdata[1].instance = 1;
+ sdw_pdata[0].acp_sdw_lock = &adata->acp_lock;
+ sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
+ adata->pdm_dev_index = 0;
+ adata->sdw0_dev_index = 1;
+ adata->sdw1_dev_index = 2;
+ adata->sdw_dma_dev_index = 3;
+ acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, NULL, 0);
+ acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+ "amd_sdw_manager", 0, adata->res, 1,
+ &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
+ acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node,
+ "amd_sdw_manager", 1, adata->res, 1,
+ &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
+ acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+ acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ }
+ break;
default:
- dev_dbg(&pci->dev, "No PDM devices found\n");
+ dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
return 0;
}
@@ -289,25 +618,38 @@ static int snd_acp63_probe(struct pci_dev *pci,
ret = -ENOMEM;
goto release_regions;
}
+ /*
+ * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init()
+ * will be invoked for all ACP configurations during suspend/resume callbacks.
+ * This flag should be set to false only when SoundWire manager power mode
+ * set to ClockStopMode.
+ */
+ adata->acp_reset = true;
pci_set_master(pci);
pci_set_drvdata(pci, adata);
mutex_init(&adata->acp_lock);
ret = acp63_init(adata->acp63_base, &pci->dev);
if (ret)
goto release_regions;
- ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
- irqflags, "ACP_PCI_IRQ", adata);
+ 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;
}
- val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG);
- get_acp63_device_config(val, pci, adata);
+ val = readl(adata->acp63_base + ACP_PIN_CONFIG);
+ ret = get_acp63_device_config(val, pci, adata);
+ /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
+ if (ret) {
+ dev_err(&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;
}
+skip_pdev_creation:
pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&pci->dev);
pm_runtime_put_noidle(&pci->dev);
@@ -327,24 +669,28 @@ disable_pci:
static int __maybe_unused snd_acp63_suspend(struct device *dev)
{
struct acp63_dev_data *adata;
- int ret;
+ int ret = 0;
adata = dev_get_drvdata(dev);
- ret = acp63_deinit(adata->acp63_base, dev);
- if (ret)
- dev_err(dev, "ACP de-init failed\n");
+ if (adata->acp_reset) {
+ ret = acp63_deinit(adata->acp63_base, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ }
return ret;
}
static int __maybe_unused snd_acp63_resume(struct device *dev)
{
struct acp63_dev_data *adata;
- int ret;
+ int ret = 0;
adata = dev_get_drvdata(dev);
- ret = acp63_init(adata->acp63_base, dev);
- if (ret)
- dev_err(dev, "ACP init failed\n");
+ if (adata->acp_reset) {
+ ret = acp63_init(adata->acp63_base, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ }
return ret;
}
diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c
index 3a83dc178e7d..d48f7c5af289 100644
--- a/sound/soc/amd/ps/ps-pdm-dma.c
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -45,10 +45,10 @@ static const struct snd_pcm_hardware acp63_pdm_hardware_capture = {
static void acp63_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
u32 watermark_size, void __iomem *acp_base)
{
- acp63_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
- acp63_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
- acp63_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
- acp63_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+ 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)
@@ -58,11 +58,11 @@ static void acp63_enable_pdm_clock(void __iomem *acp_base)
pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
pdm_ctrl = 0x00;
- acp63_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
- pdm_ctrl = acp63_readl(acp_base + ACP_WOV_MISC_CTRL);
+ 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));
- acp63_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+ writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
}
static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
@@ -70,9 +70,9 @@ static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
u32 ext_int_ctrl;
mutex_lock(adata->acp_lock);
- ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl |= PDM_DMA_INTR_MASK;
- acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
mutex_unlock(adata->acp_lock);
}
@@ -81,9 +81,9 @@ static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata)
u32 ext_int_ctrl;
mutex_lock(adata->acp_lock);
- ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
- acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
mutex_unlock(adata->acp_lock);
}
@@ -93,8 +93,8 @@ static bool acp63_check_pdm_dma_status(void __iomem *acp_base)
u32 pdm_enable, pdm_dma_enable;
pdm_dma_status = false;
- pdm_enable = acp63_readl(acp_base + ACP_WOV_PDM_ENABLE);
- pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ 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;
@@ -111,11 +111,11 @@ static int acp63_start_pdm_dma(void __iomem *acp_base)
pdm_dma_enable = 0x01;
acp63_enable_pdm_clock(acp_base);
- acp63_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
- acp63_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ 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 = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ 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);
@@ -131,14 +131,14 @@ static int acp63_stop_pdm_dma(void __iomem *acp_base)
pdm_enable = 0x00;
pdm_dma_enable = 0x00;
- pdm_enable = acp63_readl(acp_base + ACP_WOV_PDM_ENABLE);
- pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ 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;
- acp63_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
timeout = 0;
while (++timeout < ACP_COUNTER) {
- pdm_dma_enable = acp63_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
if ((pdm_dma_enable & 0x02) == 0x00)
break;
udelay(DELAY_US);
@@ -148,9 +148,9 @@ static int acp63_stop_pdm_dma(void __iomem *acp_base)
}
if (pdm_enable == ACP_PDM_ENABLE) {
pdm_enable = ACP_PDM_DISABLE;
- acp63_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
}
- acp63_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
return 0;
}
@@ -164,18 +164,16 @@ static void acp63_config_dma(struct pdm_stream_instance *rtd, int direction)
val = PDM_PTE_OFFSET;
/* Group Enable */
- acp63_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base +
- ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
- acp63_writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base +
- ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ 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);
- acp63_writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
+ writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
high |= BIT(31);
- acp63_writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4);
+ writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4);
val += 8;
addr += PAGE_SIZE;
}
@@ -242,9 +240,9 @@ static u64 acp63_pdm_get_byte_count(struct pdm_stream_instance *rtd,
u32 high, low;
u64 byte_count;
- high = acp63_readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ high = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
byte_count = high;
- low = acp63_readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ low = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
byte_count = (byte_count << 32) | low;
return byte_count;
}
@@ -309,9 +307,8 @@ static int acp63_pdm_dai_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- acp63_writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS);
- acp63_writel(PDM_DECIMATION_FACTOR, rtd->acp63_base +
- ACP_WOV_PDM_DECIMATION_FACTOR);
+ 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)
@@ -394,8 +391,9 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev)
}
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);
- pm_runtime_allow(&pdev->dev);
return 0;
}
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..ade130a8062a
--- /dev/null
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Pink Sardine SoundWire DMA Driver
+ *
+ * Copyright 2023 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 sdw0_dma_ring_buf_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_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_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 sdw1_dma_ring_buf_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 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,
+};
+
+static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ ACP_SW1_AUDIO1_TX_EN,
+ ACP_SW1_AUDIO1_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, bool enable)
+{
+ u32 ext_intr_cntl, ext_intr_cntl1;
+ u32 irq_mask = ACP_SDW_DMA_IRQ_MASK;
+ u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK;
+
+ 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 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 (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr;
+ 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 = substream->private_data;
+ int ret;
+
+ runtime = substream->runtime;
+ cpu_dai = asoc_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 (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = sdw0_dma_ring_buf_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(SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg;
+ irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id));
+ 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);
+ 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)
+{
+ union acp_sdw_dma_count byte_count;
+ u32 pos_low_reg, pos_high_reg;
+
+ byte_count.bytescount = 0;
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (pos_low_reg) {
+ byte_count.bcount.high = readl(acp_base + pos_high_reg);
+ byte_count.bcount.low = readl(acp_base + pos_low_reg);
+ }
+ 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);
+ 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 (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ kfree(stream);
+ return 0;
+}
+
+static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
+ void __iomem *acp_base, 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 (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id];
+ 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, 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, 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,
+};
+
+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;
+ 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 int acp63_sdw_platform_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static int acp_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;
+ int index, instance, ret;
+
+ 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->sdw0_dma_stream[index];
+ water_mark_size_reg =
+ sdw0_dma_ring_buf_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->sdw1_dma_stream[index];
+ water_mark_size_reg =
+ sdw1_dma_ring_buf_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);
+ 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, true);
+ return 0;
+}
+
+static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev)
+{
+ struct sdw_dma_dev_data *sdw_data;
+
+ sdw_data = dev_get_drvdata(dev);
+ return acp_restore_sdw_dma_config(sdw_data);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ SET_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 = &acp63_pm_ops,
+ },
+};
+
+module_platform_driver(acp63_sdw_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
+MODULE_LICENSE("GPL");
+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
index 7362dd15ad30..9538f3ffc5d9 100644
--- a/sound/soc/amd/raven/acp3x-pcm-dma.c
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -416,8 +416,9 @@ static int acp3x_audio_probe(struct platform_device *pdev)
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);
- pm_runtime_allow(&pdev->dev);
return 0;
}
diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
index 4e299f96521f..c3b47e9bd239 100644
--- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -430,8 +430,9 @@ static int acp_pdm_audio_probe(struct platform_device *pdev)
}
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);
- pm_runtime_allow(&pdev->dev);
return 0;
}
diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
index 29901ee4bfe3..587dec5bb33d 100644
--- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c
+++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
@@ -409,9 +409,9 @@ static int acp5x_audio_probe(struct platform_device *pdev)
}
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);
- pm_runtime_allow(&pdev->dev);
-
return 0;
}
diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h
index bd9f1c5684d1..ac1936a8c43f 100644
--- a/sound/soc/amd/vangogh/acp5x.h
+++ b/sound/soc/amd/vangogh/acp5x.h
@@ -147,6 +147,8 @@ 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)
{
diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
index e0df17c88e8e..c4634a8a17cd 100644
--- a/sound/soc/amd/vangogh/pci-acp5x.c
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -125,10 +125,15 @@ static int snd_acp5x_probe(struct pci_dev *pci,
{
struct acp5x_dev_data *adata;
struct platform_device_info pdevinfo[ACP5x_DEVS];
- unsigned int irqflags;
+ unsigned int irqflags, flag;
int ret, i;
u32 addr, val;
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
irqflags = IRQF_SHARED;
if (pci->revision != 0x50)
return -ENODEV;
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index 246299a178f9..a2fe3bd4f9a1 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -321,6 +321,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.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, "System76"),
DMI_MATCH(DMI_PRODUCT_VERSION, "pang12"),
}
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
index d818eba48546..72c4591e451b 100644
--- a/sound/soc/amd/yc/acp6x-pdm-dma.c
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -383,8 +383,9 @@ static int acp6x_pdm_audio_probe(struct platform_device *pdev)
}
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);
- pm_runtime_allow(&pdev->dev);
return 0;
}
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 007ab746973d..4c1985711218 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -473,21 +473,19 @@ static int atmel_classd_asoc_card_init(struct device *dev,
if (!dai_link)
return -ENOMEM;
- comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
+ comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
if (!comp)
return -ENOMEM;
- dai_link->cpus = &comp[0];
- dai_link->codecs = &comp[1];
+ dai_link->cpus = comp;
+ dai_link->codecs = &asoc_dummy_dlc;
dai_link->num_cpus = 1;
dai_link->num_codecs = 1;
dai_link->name = "CLASSD";
dai_link->stream_name = "CLASSD PCM";
- dai_link->codecs->dai_name = "snd-soc-dummy-dai";
dai_link->cpus->dai_name = dev_name(dev);
- dai_link->codecs->name = "snd-soc-dummy";
card->dai_link = dai_link;
card->num_links = 1;
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index 00c7b3a34ef5..efcbdd1a629f 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -496,21 +496,19 @@ static int atmel_pdmic_asoc_card_init(struct device *dev,
if (!dai_link)
return -ENOMEM;
- comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
+ comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
if (!comp)
return -ENOMEM;
- dai_link->cpus = &comp[0];
- dai_link->codecs = &comp[1];
+ dai_link->cpus = comp;
+ dai_link->codecs = &asoc_dummy_dlc;
dai_link->num_cpus = 1;
dai_link->num_codecs = 1;
dai_link->name = "PDMIC";
dai_link->stream_name = "PDMIC PCM";
- dai_link->codecs->dai_name = "snd-soc-dummy-dai";
dai_link->cpus->dai_name = dev_name(dev);
- dai_link->codecs->name = "snd-soc-dummy";
card->dai_link = dai_link;
card->num_links = 1;
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c
index da23855a0e40..c79c73e6791e 100644
--- a/sound/soc/atmel/mchp-pdmc.c
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -423,7 +423,7 @@ static const struct snd_soc_component_driver mchp_pdmc_dai_component = {
.open = &mchp_pdmc_open,
.close = &mchp_pdmc_close,
.legacy_dai_naming = 1,
- .start_dma_last = 1,
+ .trigger_start = SND_SOC_TRIGGER_ORDER_LDC,
};
static const unsigned int mchp_pdmc_1mic[] = {1};
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index baf38964b491..0405e9e49140 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -23,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>
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0c4c5cbaa809..2a62dbd5339e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_AW88395
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
@@ -136,6 +137,7 @@ config SND_SOC_ALL_CODECS
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
@@ -207,6 +209,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT712_SDCA_DMIC_SDW
imply SND_SOC_RT715_SDW
imply SND_SOC_RT715_SDCA_SDW
+ imply SND_SOC_RT722_SDCA_SDW
imply SND_SOC_RT1308_SDW
imply SND_SOC_RT1316_SDW
imply SND_SOC_RT1318_SDW
@@ -234,6 +237,9 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2764
imply SND_SOC_TAS2770
imply SND_SOC_TAS2780
+ imply SND_SOC_TAS2781_COMLIB
+ imply SND_SOC_TAS2781_FMWLIB
+ imply SND_SOC_TAS2781_I2C
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
@@ -324,6 +330,7 @@ config SND_SOC_ALL_CODECS
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
@@ -643,6 +650,13 @@ config SND_SOC_BD28623
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
@@ -1166,6 +1180,15 @@ config SND_SOC_MAX98373_SDW
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
@@ -1539,6 +1562,12 @@ config SND_SOC_RT712_SDCA_DMIC_SDW
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
@@ -1652,6 +1681,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
@@ -1699,6 +1734,29 @@ config SND_SOC_TAS2780
Enable support for Texas Instruments TAS2780 high-efficiency
digital input mono Class-D audio power amplifiers.
+config SND_SOC_TAS2781_COMLIB
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ tristate
+
+config SND_SOC_TAS2781_FMWLIB
+ depends on SND_SOC_TAS2781_COMLIB
+ 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
+ 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_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
@@ -2162,6 +2220,15 @@ config SND_SOC_WSA883X
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
+ tristate
+ 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
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5cdbae88e6e3..b48a9a323b84 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,6 +51,7 @@ snd-soc-aw88395-objs := aw88395/aw88395.o \
aw88395/aw88395_device.o
snd-soc-bd28623-objs := bd28623.o
snd-soc-bt-sco-objs := bt-sco.o
+snd-soc-chv3-codec-objs := chv3-codec.o
snd-soc-cpcap-objs := cpcap.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cros-ec-codec-objs := cros_ec_codec.o
@@ -152,6 +153,7 @@ snd-soc-max98363-objs := max98363.o
snd-soc-max98373-objs := max98373.o
snd-soc-max98373-i2c-objs := max98373-i2c.o
snd-soc-max98373-sdw-objs := max98373-sdw.o
+snd-soc-max98388-objs := max98388.o
snd-soc-max98390-objs := max98390.o
snd-soc-max98396-objs := max98396.o
snd-soc-max9850-objs := max9850.o
@@ -237,6 +239,7 @@ snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o
snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o
snd-soc-rt715-objs := rt715.o rt715-sdw.o
snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
+snd-soc-rt722-sdca-objs := rt722-sdca.o rt722-sdca-sdw.o
snd-soc-rt9120-objs := rt9120.o
snd-soc-sdw-mockup-objs := sdw-mockup.o
snd-soc-sgtl5000-objs := sgtl5000.o
@@ -256,6 +259,7 @@ 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-ssm3515-objs := ssm3515.o
snd-soc-ssm4567-objs := ssm4567.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-sta350-objs := sta350.o
@@ -269,6 +273,9 @@ snd-soc-tas5805m-objs := tas5805m.o
snd-soc-tas6424-objs := tas6424.o
snd-soc-tda7419-objs := tda7419.o
snd-soc-tas2770-objs := tas2770.o
+snd-soc-tas2781-comlib-objs := tas2781-comlib.o
+snd-soc-tas2781-fmwlib-objs := tas2781-fmwlib.o
+snd-soc-tas2781-i2c-objs := tas2781-i2c.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tfa989x-objs := tfa989x.o
snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o
@@ -359,6 +366,7 @@ snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-wsa881x-objs := wsa881x.o
snd-soc-wsa883x-objs := wsa883x.o
+snd-soc-wsa884x-objs := wsa884x.o
snd-soc-zl38060-objs := zl38060.o
# Amp
snd-soc-max9877-objs := max9877.o
@@ -425,6 +433,7 @@ obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o
obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
+obj-$(CONFIG_SND_SOC_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
@@ -521,6 +530,7 @@ 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
@@ -607,6 +617,7 @@ 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_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o
obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o
obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
@@ -623,6 +634,7 @@ 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
@@ -633,6 +645,9 @@ 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_FMWLIB) += snd-soc-tas2781-fmwlib.o
+obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.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
@@ -733,6 +748,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o
+obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o
obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o
# Amp
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index 4cb8d87f9011..15d74bb31c4c 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
- .probe_new = ad193x_i2c_probe,
+ .probe = ad193x_i2c_probe,
.id_table = ad193x_id,
};
module_i2c_driver(ad193x_i2c_driver);
diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c
index 8ed0ffdedbc9..132b9e2cca59 100644
--- a/sound/soc/codecs/adau1372-i2c.c
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = {
.driver = {
.name = "adau1372",
},
- .probe_new = adau1372_i2c_probe,
+ .probe = adau1372_i2c_probe,
.id_table = adau1372_i2c_ids,
};
module_i2c_driver(adau1372_i2c_driver);
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 37e178e8a1d0..c5b087b8fffc 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1505,7 +1505,7 @@ static struct i2c_driver adau1373_i2c_driver = {
.driver = {
.name = "adau1373",
},
- .probe_new = adau1373_i2c_probe,
+ .probe = adau1373_i2c_probe,
.id_table = adau1373_i2c_id,
};
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 135a7db7fcf9..8c8de3b3c901 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -876,7 +876,7 @@ static struct i2c_driver adau1701_i2c_driver = {
.name = "adau1701",
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
- .probe_new = adau1701_i2c_probe,
+ .probe = adau1701_i2c_probe,
.id_table = adau1701_i2c_id,
};
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 0cefff49569c..a554255186ae 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -60,7 +60,7 @@ static struct i2c_driver adau1761_i2c_driver = {
.name = "adau1761",
.of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
},
- .probe_new = adau1761_i2c_probe,
+ .probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
.id_table = adau1761_i2c_ids,
};
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 39021b8cfb62..3a170fd78ff3 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -56,7 +56,7 @@ static struct i2c_driver adau1781_i2c_driver = {
.name = "adau1781",
.of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
},
- .probe_new = adau1781_i2c_probe,
+ .probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
.id_table = adau1781_i2c_ids,
};
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 634d4dbca5ec..f2932713b4de 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -1059,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;
/*
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
index 9f137a0634d5..24c7b9c84c19 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -42,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = {
.driver = {
.name = "adau1977",
},
- .probe_new = adau1977_i2c_probe,
+ .probe = adau1977_i2c_probe,
.id_table = adau1977_i2c_ids,
};
module_i2c_driver(adau1977_i2c_driver);
diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c
index afed48401b25..73f181f7757e 100644
--- a/sound/soc/codecs/adau7118-i2c.c
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -78,7 +78,7 @@ static struct i2c_driver adau7118_driver = {
.name = "adau7118",
.of_match_table = adau7118_of_match,
},
- .probe_new = adau7118_probe_i2c,
+ .probe = adau7118_probe_i2c,
.id_table = adau7118_id,
};
module_i2c_driver(adau7118_driver);
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
index bf181bbaabed..78a317947df9 100644
--- a/sound/soc/codecs/adav803.c
+++ b/sound/soc/codecs/adav803.c
@@ -29,7 +29,7 @@ static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
},
- .probe_new = adav803_probe,
+ .probe = adav803_probe,
.id_table = adav803_id,
};
module_i2c_driver(adav803_driver);
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
index b6d9a10bdccd..e34e5533765c 100644
--- a/sound/soc/codecs/ak4118.c
+++ b/sound/soc/codecs/ak4118.c
@@ -264,8 +264,6 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data)
struct ak4118_priv *ak4118 = data;
struct snd_soc_component *component = ak4118->component;
struct snd_kcontrol_new *kctl_new;
- struct snd_kcontrol *kctl;
- struct snd_ctl_elem_id *id;
unsigned int i;
if (!component)
@@ -273,13 +271,8 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data)
for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) {
kctl_new = &ak4118_iec958_controls[i];
- kctl = snd_soc_card_get_kcontrol(component->card,
- kctl_new->name);
- if (!kctl)
- continue;
- id = &kctl->id;
- snd_ctl_notify(component->card->snd_card,
- SNDRV_CTL_EVENT_MASK_VALUE, id);
+
+ snd_soc_component_notify_control(component, kctl_new->name);
}
return IRQ_HANDLED;
@@ -414,7 +407,7 @@ static struct i2c_driver ak4118_i2c_driver = {
.of_match_table = of_match_ptr(ak4118_of_match),
},
.id_table = ak4118_id_table,
- .probe_new = ak4118_i2c_probe,
+ .probe = ak4118_i2c_probe,
};
module_i2c_driver(ak4118_i2c_driver);
diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c
index 573389e402f8..f287acb98646 100644
--- a/sound/soc/codecs/ak4375.c
+++ b/sound/soc/codecs/ak4375.c
@@ -597,7 +597,7 @@ static struct i2c_driver ak4375_i2c_driver = {
.pm = &ak4375_pm,
.of_match_table = ak4375_of_match,
},
- .probe_new = ak4375_i2c_probe,
+ .probe = ak4375_i2c_probe,
.remove = ak4375_i2c_remove,
};
module_i2c_driver(ak4375_i2c_driver);
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
index 8c69c8cf9b67..77678f85ad94 100644
--- a/sound/soc/codecs/ak4458.c
+++ b/sound/soc/codecs/ak4458.c
@@ -818,7 +818,7 @@ static struct i2c_driver ak4458_i2c_driver = {
.pm = &ak4458_pm,
.of_match_table = ak4458_of_match,
},
- .probe_new = ak4458_i2c_probe,
+ .probe = ak4458_i2c_probe,
.remove = ak4458_i2c_remove,
};
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 8c8c93eea704..904bf91090aa 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -439,7 +439,7 @@ static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
},
- .probe_new = ak4535_i2c_probe,
+ .probe = ak4535_i2c_probe,
.id_table = ak4535_i2c_id,
};
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index f75c19ef3551..ad56caec9dac 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -925,7 +925,7 @@ static struct i2c_driver ak4613_i2c_driver = {
.name = "ak4613-codec",
.of_match_table = ak4613_of_match,
},
- .probe_new = ak4613_i2c_probe,
+ .probe = ak4613_i2c_probe,
.id_table = ak4613_i2c_id,
};
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 0d3ee195b3cc..5b7df2f0dd6a 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -628,7 +628,7 @@ static struct i2c_driver ak4641_i2c_driver = {
.driver = {
.name = "ak4641",
},
- .probe_new = ak4641_i2c_probe,
+ .probe = ak4641_i2c_probe,
.remove = ak4641_i2c_remove,
.id_table = ak4641_i2c_id,
};
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 914d5b1969f8..2a8984c1fa9c 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -698,7 +698,7 @@ static struct i2c_driver ak4642_i2c_driver = {
.name = "ak4642-codec",
.of_match_table = ak4642_of_match,
},
- .probe_new = ak4642_i2c_probe,
+ .probe = ak4642_i2c_probe,
.id_table = ak4642_i2c_id,
};
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index cd76765f8cc0..5b849b390c2a 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -655,7 +655,7 @@ static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671-codec",
},
- .probe_new = ak4671_i2c_probe,
+ .probe = ak4671_i2c_probe,
.id_table = ak4671_i2c_id,
};
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
index 60abcffe6a0c..442e2cb42df4 100644
--- a/sound/soc/codecs/ak5558.c
+++ b/sound/soc/codecs/ak5558.c
@@ -497,7 +497,7 @@ static struct i2c_driver ak5558_i2c_driver = {
.of_match_table = of_match_ptr(ak5558_i2c_dt_ids),
.pm = &ak5558_pm,
},
- .probe_new = ak5558_i2c_probe,
+ .probe = ak5558_i2c_probe,
.remove = ak5558_i2c_remove,
};
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index 9ef01b1dd294..b24c32206884 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -1083,7 +1083,7 @@ static struct i2c_driver alc5623_i2c_driver = {
.name = "alc562x-codec",
.of_match_table = of_match_ptr(alc5623_of_match),
},
- .probe_new = alc5623_i2c_probe,
+ .probe = alc5623_i2c_probe,
.id_table = alc5623_i2c_table,
};
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index a770704a4e17..d5021f266930 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1182,7 +1182,7 @@ static struct i2c_driver alc5632_i2c_driver = {
.name = "alc5632",
.of_match_table = of_match_ptr(alc5632_of_match),
},
- .probe_new = alc5632_i2c_probe,
+ .probe = alc5632_i2c_probe,
.id_table = alc5632_i2c_table,
};
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
index afdce6b7fa26..9dcd75dd799a 100644
--- a/sound/soc/codecs/aw88395/aw88395.c
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -570,7 +570,7 @@ static struct i2c_driver aw88395_i2c_driver = {
.driver = {
.name = AW88395_I2C_NAME,
},
- .probe_new = aw88395_i2c_probe,
+ .probe = aw88395_i2c_probe,
.id_table = aw88395_i2c_id,
};
module_i2c_driver(aw88395_i2c_driver);
diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c
new file mode 100644
index 000000000000..ab99effa6874
--- /dev/null
+++ b/sound/soc/codecs/chv3-codec.c
@@ -0,0 +1,41 @@
+// 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", },
+ { }
+};
+
+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/cs35l32.c b/sound/soc/codecs/cs35l32.c
index dc7a58d68076..6e658bb16fb0 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -260,7 +260,7 @@ 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,
@@ -572,7 +572,7 @@ static struct i2c_driver cs35l32_i2c_driver = {
.of_match_table = cs35l32_of_match,
},
.id_table = cs35l32_id,
- .probe_new = cs35l32_i2c_probe,
+ .probe = cs35l32_i2c_probe,
.remove = cs35l32_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index 15e79168d256..9968c2e189e6 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -852,7 +852,7 @@ 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,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -1282,7 +1282,7 @@ static struct i2c_driver cs35l33_i2c_driver = {
},
.id_table = cs35l33_id,
- .probe_new = cs35l33_i2c_probe,
+ .probe = cs35l33_i2c_probe,
.remove = cs35l33_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index b3f98023e6a7..6974dd461410 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -799,7 +799,7 @@ 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,
@@ -1213,7 +1213,7 @@ static struct i2c_driver cs35l34_i2c_driver = {
},
.id_table = cs35l34_id,
- .probe_new = cs35l34_i2c_probe,
+ .probe = cs35l34_i2c_probe,
.remove = cs35l34_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 947a440a3a47..0a4b5aa78185 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -1099,7 +1099,7 @@ 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,
};
@@ -1654,7 +1654,7 @@ static struct i2c_driver cs35l35_i2c_driver = {
.of_match_table = cs35l35_of_match,
},
.id_table = cs35l35_id,
- .probe_new = cs35l35_i2c_probe,
+ .probe = cs35l35_i2c_probe,
.remove = cs35l35_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index a078dd422ea1..04ba7f25012e 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -1944,7 +1944,7 @@ static struct i2c_driver cs35l36_i2c_driver = {
.of_match_table = cs35l36_of_match,
},
.id_table = cs35l36_id,
- .probe_new = cs35l36_i2c_probe,
+ .probe = cs35l36_i2c_probe,
.remove = cs35l36_i2c_remove,
};
module_i2c_driver(cs35l36_i2c_driver);
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
index 3676b596f60b..7ea890d7d387 100644
--- a/sound/soc/codecs/cs35l41-i2c.c
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -88,7 +88,7 @@ static struct i2c_driver cs35l41_i2c_driver = {
.acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
},
.id_table = cs35l41_id_i2c,
- .probe_new = cs35l41_i2c_probe,
+ .probe = cs35l41_i2c_probe,
.remove = cs35l41_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
index 562f73df7afa..77e0f8750f37 100644
--- a/sound/soc/codecs/cs35l45-i2c.c
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// SPDX-License-Identifier: GPL-2.0
//
// cs35l45-i2c.c -- CS35L45 I2C driver
//
@@ -65,12 +65,12 @@ static struct i2c_driver cs35l45_i2c_driver = {
.pm = &cs35l45_pm_ops,
},
.id_table = cs35l45_id_i2c,
- .probe_new = cs35l45_i2c_probe,
+ .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("Dual BSD/GPL");
+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
index a00b23b4180c..5efb77530cc3 100644
--- a/sound/soc/codecs/cs35l45-spi.c
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// SPDX-License-Identifier: GPL-2.0
//
// cs35l45-spi.c -- CS35L45 SPI driver
//
@@ -74,5 +74,5 @@ module_spi_driver(cs35l45_spi_driver);
MODULE_DESCRIPTION("SPI CS35L45 driver");
MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
-MODULE_LICENSE("Dual BSD/GPL");
+MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_CS35L45);
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 46610e64e818..066f83c0c7ac 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// SPDX-License-Identifier: GPL-2.0
//
// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver
//
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index c31597f6bfae..d1edb9876c10 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// SPDX-License-Identifier: GPL-2.0
//
// cs35l45.c - CS35L45 ALSA SoC audio driver
//
@@ -1296,4 +1296,4 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45);
MODULE_DESCRIPTION("ASoC CS35L45 driver");
MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
-MODULE_LICENSE("Dual BSD/GPL");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index 0da28439f628..61135a316df3 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* cs35l45.h - CS35L45 ALSA SoC audio driver
*
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
index 295caad26224..ed2a41943d97 100644
--- a/sound/soc/codecs/cs35l56-i2c.c
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -68,7 +68,7 @@ static struct i2c_driver cs35l56_i2c_driver = {
.pm = &cs35l56_pm_ops_i2c_spi,
},
.id_table = cs35l56_id_i2c,
- .probe_new = cs35l56_i2c_probe,
+ .probe = cs35l56_i2c_probe,
.remove = cs35l56_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index e0d2b9bb2326..c03f9d3c9a13 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -822,25 +822,23 @@ static void cs35l56_system_reset(struct cs35l56_private *cs35l56)
regcache_cache_only(cs35l56->regmap, false);
}
-static void cs35l56_dsp_work(struct work_struct *work)
+static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
{
- struct cs35l56_private *cs35l56 = container_of(work,
- struct cs35l56_private,
- dsp_work);
- unsigned int reg;
- unsigned int val;
- int ret = 0;
-
- if (!cs35l56->init_done)
- return;
-
- cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x",
- cs35l56->secured ? "s" : "", cs35l56->rev);
+ int ret;
- if (!cs35l56->dsp.part)
- return;
+ /* Use wm_adsp to load and apply the firmware patch and coefficient files */
+ ret = wm_adsp_power_up(&cs35l56->dsp);
+ if (ret)
+ dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
+ else
+ cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_REINIT);
+}
- pm_runtime_get_sync(cs35l56->dev);
+static void cs35l56_patch(struct cs35l56_private *cs35l56)
+{
+ unsigned int reg;
+ unsigned int val;
+ int ret;
/*
* Disable SoundWire interrupts to prevent race with IRQ work.
@@ -907,9 +905,6 @@ static void cs35l56_dsp_work(struct work_struct *work)
err_unlock:
mutex_unlock(&cs35l56->irq_lock);
err:
- pm_runtime_mark_last_busy(cs35l56->dev);
- pm_runtime_put_autosuspend(cs35l56->dev);
-
/* Re-enable SoundWire interrupts */
if (cs35l56->sdw_peripheral) {
cs35l56->sdw_irq_no_unmask = false;
@@ -918,6 +913,32 @@ err:
}
}
+static void cs35l56_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ dsp_work);
+
+ if (!cs35l56->init_done)
+ return;
+
+ pm_runtime_get_sync(cs35l56->dev);
+
+ /*
+ * When the device is running in secure mode the firmware files can
+ * only contain insecure tunings and therefore we do not need to
+ * shutdown the firmware to apply them and can use the lower cost
+ * reinit sequence instead.
+ */
+ if (cs35l56->secured)
+ cs35l56_secure_patch(cs35l56);
+ else
+ cs35l56_patch(cs35l56);
+
+ pm_runtime_mark_last_busy(cs35l56->dev);
+ pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
static int cs35l56_component_probe(struct snd_soc_component *component)
{
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
@@ -1505,6 +1526,12 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
cs35l56->secured ? "s" : "", cs35l56->rev, otpid);
+ /* Populate the DSP information with the revision and security state */
+ cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x",
+ cs35l56->secured ? "s" : "", cs35l56->rev);
+ if (!cs35l56->dsp.part)
+ return -ENOMEM;
+
/* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1,
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
index dee1a6662c2e..69287ba7e955 100644
--- a/sound/soc/codecs/cs4234.c
+++ b/sound/soc/codecs/cs4234.c
@@ -675,7 +675,7 @@ static const struct regmap_config cs4234_regmap = {
.writeable_reg = cs4234_writeable_register,
.reg_defaults = cs4234_default_reg,
.num_reg_defaults = ARRAY_SIZE(cs4234_default_reg),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -906,7 +906,7 @@ static struct i2c_driver cs4234_i2c_driver = {
.pm = &cs4234_pm,
.of_match_table = cs4234_of_match,
},
- .probe_new = cs4234_i2c_probe,
+ .probe = cs4234_i2c_probe,
.remove = cs4234_i2c_remove,
};
module_i2c_driver(cs4234_i2c_driver);
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 3573363b7e31..0cfc5ab36a13 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -649,7 +649,7 @@ static struct i2c_driver cs4265_i2c_driver = {
.of_match_table = cs4265_of_match,
},
.id_table = cs4265_id,
- .probe_new = cs4265_i2c_probe,
+ .probe = cs4265_i2c_probe,
.remove = cs4265_i2c_remove,
};
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 1b640d8232ba..ab32f15e3b44 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -751,7 +751,7 @@ static struct i2c_driver cs4270_i2c_driver = {
.of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
- .probe_new = cs4270_i2c_probe,
+ .probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
};
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index 4033be1c3bc1..89fe7d1665df 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -33,7 +33,7 @@ static struct i2c_driver cs4271_i2c_driver = {
.name = "cs4271",
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
- .probe_new = cs4271_i2c_probe,
+ .probe = cs4271_i2c_probe,
.id_table = cs4271_i2c_id,
};
module_i2c_driver(cs4271_i2c_driver);
diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c
index 67b253287daf..2552a1e6b82f 100644
--- a/sound/soc/codecs/cs42l42-i2c.c
+++ b/sound/soc/codecs/cs42l42-i2c.c
@@ -92,7 +92,7 @@ static struct i2c_driver cs42l42_i2c_driver = {
.acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
},
.id_table = cs42l42_id,
- .probe_new = cs42l42_i2c_probe,
+ .probe = cs42l42_i2c_probe,
.remove = cs42l42_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index e3edaa1a2761..a0de0329406a 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -393,7 +393,7 @@ 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,
@@ -646,12 +646,19 @@ static const struct cs42l42_pll_params pll_ratio_table[] = {
{ 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}
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index 85238339fbca..b2106ff6a7cb 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -43,7 +43,7 @@ static struct i2c_driver cs42l51_i2c_driver = {
.of_match_table = cs42l51_of_match,
.pm = &cs42l51_pm_ops,
},
- .probe_new = cs42l51_i2c_probe,
+ .probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
.id_table = cs42l51_i2c_id,
};
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index e88d9ff95cdf..a67cd3ee84e0 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -724,12 +724,9 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, cs42l51);
cs42l51->regmap = regmap;
- cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
- if (IS_ERR(cs42l51->mclk_handle)) {
- if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT)
- return PTR_ERR(cs42l51->mclk_handle);
- cs42l51->mclk_handle = NULL;
- }
+ 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];
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 90bf535fc5a5..1f1ded0ff0ac 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1226,7 +1226,7 @@ static struct i2c_driver cs42l52_i2c_driver = {
.of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
- .probe_new = cs42l52_i2c_probe,
+ .probe = cs42l52_i2c_probe,
};
module_i2c_driver(cs42l52_i2c_driver);
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index 3b0e715549c9..4c646e8d72aa 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1341,7 +1341,7 @@ static struct i2c_driver cs42l56_i2c_driver = {
.of_match_table = cs42l56_of_match,
},
.id_table = cs42l56_id,
- .probe_new = cs42l56_i2c_probe,
+ .probe = cs42l56_i2c_probe,
.remove = cs42l56_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 0a146319755a..6ab67d196d10 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1267,7 +1267,7 @@ 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,
@@ -1384,7 +1384,7 @@ static struct i2c_driver cs42l73_i2c_driver = {
.of_match_table = cs42l73_of_match,
},
.id_table = cs42l73_id,
- .probe_new = cs42l73_i2c_probe,
+ .probe = cs42l73_i2c_probe,
};
diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c
index 37629ebd90e0..f482b6a4f5c3 100644
--- a/sound/soc/codecs/cs42l83-i2c.c
+++ b/sound/soc/codecs/cs42l83-i2c.c
@@ -158,7 +158,7 @@ static const struct regmap_config cs42l83_regmap = {
.max_register = CS42L42_MAX_REGISTER,
.reg_defaults = cs42l83_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
@@ -228,7 +228,7 @@ static struct i2c_driver cs42l83_i2c_driver = {
.pm = &cs42l83_i2c_pm_ops,
.of_match_table = of_match_ptr(cs42l83_of_match),
},
- .probe_new = cs42l83_i2c_probe,
+ .probe = cs42l83_i2c_probe,
.remove = cs42l83_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
index 052ffb7dcfc6..a422472820fb 100644
--- a/sound/soc/codecs/cs42xx8-i2c.c
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -70,7 +70,7 @@ static struct i2c_driver cs42xx8_i2c_driver = {
.pm = &cs42xx8_pm,
.of_match_table = cs42xx8_of_match,
},
- .probe_new = cs42xx8_i2c_probe,
+ .probe = cs42xx8_i2c_probe,
.remove = cs42xx8_i2c_remove,
.id_table = cs42xx8_i2c_id,
};
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index db39abb2a31b..3292405024bc 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -2357,7 +2357,7 @@ 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,
+ .cache_type = REGCACHE_MAPLE,
/* needed for regcache_sync */
.use_single_read = true,
.use_single_write = true,
@@ -2697,7 +2697,7 @@ static struct i2c_driver cs43130_i2c_driver = {
.pm = &cs43130_runtime_pm,
},
.id_table = cs43130_i2c_id,
- .probe_new = cs43130_i2c_probe,
+ .probe = cs43130_i2c_probe,
.remove = cs43130_i2c_remove,
};
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
index ac1696034846..2ceca5d0e5bf 100644
--- a/sound/soc/codecs/cs4341.c
+++ b/sound/soc/codecs/cs4341.c
@@ -258,7 +258,7 @@ static struct i2c_driver cs4341_i2c_driver = {
.name = "cs4341-i2c",
.of_match_table = of_match_ptr(cs4341_dt_ids),
},
- .probe_new = cs4341_i2c_probe,
+ .probe = cs4341_i2c_probe,
.id_table = cs4341_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index ba94ffd0a7e4..8365dd0ebe2a 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -375,7 +375,7 @@ static struct i2c_driver cs4349_i2c_driver = {
.pm = &cs4349_runtime_pm,
},
.id_table = cs4349_i2c_id,
- .probe_new = cs4349_i2c_probe,
+ .probe = cs4349_i2c_probe,
.remove = cs4349_i2c_remove,
};
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index 69db0013d243..f4065555c36e 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -911,7 +911,7 @@ 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,
@@ -990,14 +990,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 */
@@ -1121,7 +1117,7 @@ static struct i2c_driver cs53l30_i2c_driver = {
.pm = &cs53l30_runtime_pm,
},
.id_table = cs53l30_id,
- .probe_new = cs53l30_i2c_probe,
+ .probe = cs53l30_i2c_probe,
.remove = cs53l30_i2c_remove,
};
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index 5deceaa89282..082231088a26 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -1706,7 +1706,7 @@ static struct i2c_driver cx2072x_i2c_driver = {
.acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
.pm = &cx2072x_runtime_pm,
},
- .probe_new = cx2072x_i2c_probe,
+ .probe = cx2072x_i2c_probe,
.remove = cx2072x_i2c_remove,
.id_table = cx2072x_i2c_id,
};
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index f838466bfebf..1e232d01809c 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1248,7 +1248,7 @@ static struct i2c_driver da7210_i2c_driver = {
.driver = {
.name = "da7210",
},
- .probe_new = da7210_i2c_probe,
+ .probe = da7210_i2c_probe,
.id_table = da7210_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 1c1f211a8e2e..3a6449c44b23 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -2069,7 +2069,7 @@ static struct i2c_driver da7213_i2c_driver = {
.acpi_match_table = ACPI_PTR(da7213_acpi_match),
.pm = &da7213_pm,
},
- .probe_new = da7213_i2c_probe,
+ .probe = da7213_i2c_probe,
.remove = da7213_i2c_remove,
.id_table = da7213_i2c_id,
};
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index d9c28e701613..3f456b08b809 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -3317,7 +3317,7 @@ static struct i2c_driver da7218_i2c_driver = {
.name = "da7218",
.of_match_table = da7218_of_match,
},
- .probe_new = da7218_i2c_probe,
+ .probe = da7218_i2c_probe,
.id_table = da7218_i2c_id,
};
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 993a0d00bc48..c65256bd526d 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -571,16 +571,29 @@ static enum da7219_aad_jack_ins_deb
}
}
+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 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(dev, "Invalid jack detect rate");
@@ -688,6 +701,12 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
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(dev, fw_str);
@@ -849,6 +868,21 @@ static void da7219_aad_handle_pdata(struct snd_soc_component *component)
mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
}
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;
+ }
}
}
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 7468ee4af2ea..600c2db58756 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -2714,7 +2714,7 @@ static struct i2c_driver da7219_i2c_driver = {
.of_match_table = of_match_ptr(da7219_of_match),
.acpi_match_table = ACPI_PTR(da7219_acpi_match),
},
- .probe_new = da7219_i2c_probe,
+ .probe = da7219_i2c_probe,
.id_table = da7219_i2c_id,
};
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 2c5b0b74201c..f8ca1afa8af5 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1555,7 +1555,7 @@ static struct i2c_driver da732x_i2c_driver = {
.driver = {
.name = "da7320",
},
- .probe_new = da732x_i2c_probe,
+ .probe = da732x_i2c_probe,
.id_table = da732x_i2c_id,
};
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index 28043b4530df..ae20086777b5 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1531,7 +1531,7 @@ static struct i2c_driver da9055_i2c_driver = {
.name = "da9055-codec",
.of_match_table = of_match_ptr(da9055_of_match),
},
- .probe_new = da9055_i2c_probe,
+ .probe = da9055_i2c_probe,
.id_table = da9055_i2c_id,
};
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index a27d80956459..34cf60769b62 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -52,7 +52,12 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0);
-static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(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),
@@ -115,7 +120,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),
@@ -364,13 +369,11 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int count = 0;
es8316->sysclk = freq;
+ es8316->sysclk_constraints.list = NULL;
+ es8316->sysclk_constraints.count = 0;
- if (freq == 0) {
- es8316->sysclk_constraints.list = NULL;
- es8316->sysclk_constraints.count = 0;
-
+ if (freq == 0)
return 0;
- }
ret = clk_set_rate(es8316->mclk, freq);
if (ret)
@@ -386,8 +389,10 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
es8316->allowed_rates[count++] = freq / ratio;
}
- 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;
}
@@ -820,7 +825,7 @@ static const struct regmap_config es8316_regmap = {
.use_single_write = true,
.max_register = 0x53,
.volatile_reg = es8316_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int es8316_i2c_probe(struct i2c_client *i2c_client)
@@ -887,7 +892,7 @@ static struct i2c_driver es8316_i2c_driver = {
.acpi_match_table = ACPI_PTR(es8316_acpi_match),
.of_match_table = of_match_ptr(es8316_of_match),
},
- .probe_new = es8316_i2c_probe,
+ .probe = es8316_i2c_probe,
.id_table = es8316_i2c_id,
};
module_i2c_driver(es8316_i2c_driver);
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 28a0565c2a95..7cb5b57ae655 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -896,7 +896,7 @@ static struct i2c_driver es8326_i2c_driver = {
.acpi_match_table = ACPI_PTR(es8326_acpi_match),
.of_match_table = of_match_ptr(es8326_of_match),
},
- .probe_new = es8326_i2c_probe,
+ .probe = es8326_i2c_probe,
.id_table = es8326_i2c_id,
};
module_i2c_driver(es8326_i2c_driver);
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index 68072e99fcc7..3c4aaa0032a0 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -40,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
- .probe_new = es8328_i2c_probe,
+ .probe = es8328_i2c_probe,
.id_table = es8328_id,
};
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 160adc706cc6..0bd9ba5a11b4 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -822,7 +822,7 @@ const struct regmap_config es8328_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ES8328_REG_MAX,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index 6d980fbc4207..d21f69f05342 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -495,31 +495,43 @@ static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
struct hdmi_codec_params *hp)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
- int idx;
-
- /* Select a channel allocation that matches with ELD and pcm channels */
- idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
- if (idx < 0) {
- dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
- idx);
- hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
- return idx;
+ 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);
- hp->cea.channels = channels;
+
+ 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 = hdmi_codec_channel_alloc[idx].ca_id;
+ hp->cea.channel_allocation = ca_id;
hp->sample_width = sample_width;
hp->sample_rate = sample_rate;
hp->channels = channels;
- hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+ hcp->chmap_idx = idx;
return 0;
}
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index 50105d72b2b7..f9456133a89a 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1142,7 +1142,7 @@ static struct i2c_driver isabelle_i2c_driver = {
.driver = {
.name = "isabelle",
},
- .probe_new = isabelle_i2c_probe,
+ .probe = isabelle_i2c_probe,
.id_table = isabelle_i2c_id,
};
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index dba161305de8..e7542f71323d 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -137,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "lm4857",
},
- .probe_new = lm4857_i2c_probe,
+ .probe = lm4857_i2c_probe,
.id_table = lm4857_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index a2e782cc4276..a4094689b3dd 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1451,7 +1451,7 @@ static struct i2c_driver lm49453_i2c_driver = {
.driver = {
.name = "lm49453",
},
- .probe_new = lm49453_i2c_probe,
+ .probe = lm49453_i2c_probe,
.id_table = lm49453_i2c_id,
};
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index d711eb1da0a8..d22b4ba51ed8 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -214,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = {
.driver = {
.name = "max9768",
},
- .probe_new = max9768_i2c_probe,
+ .probe = max9768_i2c_probe,
.id_table = max9768_i2c_id,
};
module_i2c_driver(max9768_i2c_driver);
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 405ec16be2b6..8b56ee550c09 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -310,24 +310,24 @@ static const struct regmap_config max98088_regmap = {
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_component_write(component, eq_reg++, M98088_BYTE1(coefs[i]));
- snd_soc_component_write(component, 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]));
+ }
}
/*
@@ -1789,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = {
.name = "max98088",
.of_match_table = of_match_ptr(max98088_of_match),
},
- .probe_new = max98088_i2c_probe,
+ .probe = max98088_i2c_probe,
.id_table = max98088_i2c_id,
};
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index b419c49e1e08..2adf744c6526 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -10,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>
@@ -1582,7 +1581,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
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];
@@ -1591,6 +1590,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
cdata->fmt = fmt;
regval = 0;
+ 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 */
@@ -1636,7 +1636,8 @@ 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(component->dev, "DAI format unsupported");
return -EINVAL;
@@ -1665,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_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;
@@ -1680,33 +1690,22 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
{
struct snd_soc_component *component = codec_dai->component;
struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
- struct max98090_cdata *cdata;
- cdata = &max98090->dai[0];
if (slots < 0 || slots > 4)
return -EINVAL;
- max98090->tdm_slots = slots;
- max98090->tdm_width = slot_width;
-
- if (max98090->tdm_slots > 1) {
- /* SLOTL SLOTR SLOTDLY */
- snd_soc_component_write(component, M98090_REG_TDM_FORMAT,
- 0 << M98090_TDM_SLOTL_SHIFT |
- 1 << M98090_TDM_SLOTR_SHIFT |
- 0 << M98090_TDM_SLOTDLY_SHIFT);
-
- /* FSW TDM */
- snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL,
- M98090_TDM_MASK,
- M98090_TDM_MASK);
- }
+ 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;
}
@@ -2409,6 +2408,9 @@ static int max98090_probe(struct snd_soc_component *component)
max98090->pa1en = 0;
max98090->pa2en = 0;
+ max98090->tdm_lslot = 0;
+ max98090->tdm_rslot = 1;
+
ret = snd_soc_component_read(component, M98090_REG_REVISION_ID);
if (ret < 0) {
dev_err(component->dev, "Failed to read device revision: %d\n",
@@ -2691,7 +2693,7 @@ static struct i2c_driver max98090_i2c_driver = {
.of_match_table = of_match_ptr(max98090_of_match),
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
},
- .probe_new = 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 a197114b0dad..6ce8dd176e48 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1533,7 +1533,8 @@ struct max98090_priv {
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;
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 44aa58fcc23f..7e525d49328d 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -2155,7 +2155,7 @@ static struct i2c_driver max98095_i2c_driver = {
.name = "max98095",
.of_match_table = of_match_ptr(max98095_of_match),
},
- .probe_new = max98095_i2c_probe,
+ .probe = max98095_i2c_probe,
.id_table = max98095_i2c_id,
};
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
index e6b84e222b50..b5c69bba0e48 100644
--- a/sound/soc/codecs/max98363.c
+++ b/sound/soc/codecs/max98363.c
@@ -15,11 +15,6 @@
#include "max98363.h"
static struct reg_default max98363_reg[] = {
- {MAX98363_R2001_INTR_RAW, 0x0},
- {MAX98363_R2003_INTR_STATE, 0x0},
- {MAX98363_R2005_INTR_FALG, 0x0},
- {MAX98363_R2007_INTR_EN, 0x0},
- {MAX98363_R2009_INTR_CLR, 0x0},
{MAX98363_R2021_ERR_MON_CTRL, 0x0},
{MAX98363_R2022_SPK_MON_THRESH, 0x0},
{MAX98363_R2023_SPK_MON_DURATION, 0x0},
@@ -28,7 +23,6 @@ static struct reg_default max98363_reg[] = {
{MAX98363_R2040_AMP_VOL, 0x0},
{MAX98363_R2041_AMP_GAIN, 0x5},
{MAX98363_R2042_DSP_CFG, 0x0},
- {MAX98363_R21FF_REV_ID, 0x0},
};
static bool max98363_readable_register(struct device *dev, unsigned int reg)
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
index bac9d1bcf60a..f0e49179c38f 100644
--- a/sound/soc/codecs/max98371.c
+++ b/sound/soc/codecs/max98371.c
@@ -419,7 +419,7 @@ static struct i2c_driver max98371_i2c_driver = {
.name = "max98371",
.of_match_table = of_match_ptr(max98371_of_match),
},
- .probe_new = max98371_i2c_probe,
+ .probe = max98371_i2c_probe,
.id_table = max98371_i2c_id,
};
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
index ec0905df65d1..0fa5ceca62a2 100644
--- a/sound/soc/codecs/max98373-i2c.c
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -9,7 +9,7 @@
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/cdev.h>
@@ -624,7 +624,7 @@ static struct i2c_driver max98373_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98373_acpi_match),
.pm = &max98373_pm,
},
- .probe_new = max98373_i2c_probe,
+ .probe = max98373_i2c_probe,
.id_table = max98373_i2c_id,
};
diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c
new file mode 100644
index 000000000000..cde5e85946cb
--- /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.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98388.h"
+
+static 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);
+ 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", 0},
+ { },
+};
+
+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
index 7a5260ff8d6b..5b8e78e51630 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -1133,7 +1133,7 @@ static struct i2c_driver max98390_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98390_acpi_match),
.pm = &max98390_pm,
},
- .probe_new = max98390_i2c_probe,
+ .probe = max98390_i2c_probe,
.id_table = max98390_i2c_id,
};
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
index db3de41f10e0..3a1d8c211f3c 100644
--- a/sound/soc/codecs/max98396.c
+++ b/sound/soc/codecs/max98396.c
@@ -1907,7 +1907,7 @@ static struct i2c_driver max98396_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98396_acpi_match),
.pm = &max98396_pm,
},
- .probe_new = max98396_i2c_probe,
+ .probe = max98396_i2c_probe,
.id_table = max98396_i2c_id,
};
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index a6733396b0ca..8b012a85360a 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -329,7 +329,7 @@ static struct i2c_driver max9850_i2c_driver = {
.driver = {
.name = "max9850",
},
- .probe_new = max9850_i2c_probe,
+ .probe = max9850_i2c_probe,
.id_table = max9850_i2c_id,
};
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
index 0daa2a3dd621..93412b966b33 100644
--- a/sound/soc/codecs/max98504.c
+++ b/sound/soc/codecs/max98504.c
@@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = {
.name = "max98504",
.of_match_table = of_match_ptr(max98504_of_match),
},
- .probe_new = max98504_i2c_probe,
+ .probe = max98504_i2c_probe,
.id_table = max98504_i2c_id,
};
module_i2c_driver(max98504_i2c_driver);
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
index 5edd6f90f8a7..8637fff307ad 100644
--- a/sound/soc/codecs/max98520.c
+++ b/sound/soc/codecs/max98520.c
@@ -756,7 +756,7 @@ static struct i2c_driver max98520_i2c_driver = {
.of_match_table = of_match_ptr(max98520_of_match),
.pm = &max98520_pm,
},
- .probe_new = max98520_i2c_probe,
+ .probe = max98520_i2c_probe,
.id_table = max98520_i2c_id,
};
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
index 9611ab1e79e5..4015ed2c47ec 100644
--- a/sound/soc/codecs/max9860.c
+++ b/sound/soc/codecs/max9860.c
@@ -723,7 +723,7 @@ static const struct of_device_id max9860_of_match[] = {
MODULE_DEVICE_TABLE(of, max9860_of_match);
static struct i2c_driver max9860_i2c_driver = {
- .probe_new = max9860_probe,
+ .probe = max9860_probe,
.remove = max9860_remove,
.id_table = max9860_i2c_id,
.driver = {
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index ae552d72beec..b616ad39858c 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -702,7 +702,7 @@ static struct i2c_driver max9867_i2c_driver = {
.name = "max9867",
.of_match_table = of_match_ptr(max9867_of_match),
},
- .probe_new = max9867_i2c_probe,
+ .probe = max9867_i2c_probe,
.id_table = max9867_i2c_id,
};
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 3bf86328d5cd..2ae64fcf29c7 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -160,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = {
.driver = {
.name = "max9877",
},
- .probe_new = max9877_i2c_probe,
+ .probe = max9877_i2c_probe,
.id_table = max9877_i2c_id,
};
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index c24d9f2c8874..a9c1d85cd0d5 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -635,7 +635,7 @@ static struct i2c_driver max98925_i2c_driver = {
.name = "max98925",
.of_match_table = of_match_ptr(max98925_of_match),
},
- .probe_new = max98925_i2c_probe,
+ .probe = max98925_i2c_probe,
.id_table = max98925_i2c_id,
};
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index bffd56e240e9..bdc508e23e59 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -582,7 +582,7 @@ static struct i2c_driver max98926_i2c_driver = {
.name = "max98926",
.of_match_table = of_match_ptr(max98926_of_match),
},
- .probe_new = max98926_i2c_probe,
+ .probe = max98926_i2c_probe,
.id_table = max98926_i2c_id,
};
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index 331d3e1d735c..0aaf2e6ae78d 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -973,7 +973,7 @@ static struct i2c_driver max98927_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98927_acpi_match),
.pm = &max98927_pm,
},
- .probe_new = max98927_i2c_probe,
+ .probe = max98927_i2c_probe,
.remove = max98927_i2c_remove,
.id_table = max98927_i2c_id,
};
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 3c6ac77379cb..a45ef9d65703 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -581,7 +581,7 @@ static struct i2c_driver ml26124_i2c_driver = {
.driver = {
.name = "ml26124",
},
- .probe_new = ml26124_i2c_probe,
+ .probe = ml26124_i2c_probe,
.id_table = ml26124_i2c_id,
};
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
index cb487e63615c..30690479ec17 100644
--- a/sound/soc/codecs/mt6359.c
+++ b/sound/soc/codecs/mt6359.c
@@ -18,6 +18,20 @@
#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 */
@@ -360,8 +374,34 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
(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;
@@ -373,6 +413,8 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
(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);
@@ -380,35 +422,82 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
(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 */
@@ -1070,9 +1159,10 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w,
{
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, dapm_kcontrol_get_value(w->kcontrols[0]));
+ __func__, event, mux);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1110,14 +1200,29 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w,
/* Enable AUD_CLK */
mt6359_set_decoder_clk(priv, true);
- /* Enable Audio DAC (3rd DAC) */
- regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113);
- /* Enable low-noise mode of DAC */
- if (priv->dev_counter[DEVICE_HP] == 0)
- regmap_write(priv->regmap,
- MT6359_AUDDEC_ANA_CON9, 0x0001);
- /* Switch LOL MUX to audio 3rd DAC */
- regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b);
+ /* 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 */
@@ -1129,6 +1234,15 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w,
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);
@@ -2358,6 +2472,10 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = {
{"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"},
@@ -2497,6 +2615,7 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = {
/* Lineout Path */
{"LOL Mux", "Playback", "DAC_3RD"},
+ {"LOL Mux", "Playback_L_DAC", "DACL"},
{"LINEOUT L", NULL, "LOL Mux"},
/* Headphone Path */
@@ -2666,6 +2785,8 @@ static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt)
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);
@@ -2697,22 +2818,23 @@ static void mt6359_codec_remove(struct snd_soc_component *cmpnt)
cmpnt->regmap = NULL;
}
-static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0);
static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0);
static const struct snd_kcontrol_new mt6359_snd_controls[] = {
/* dl pga gain */
SOC_DOUBLE_EXT_TLV("Headset Volume",
- MT6359_ZCD_CON2, 0, 7, 0x1E, 0,
- snd_soc_get_volsw, mt6359_put_volsw,
- hp_playback_tlv),
+ 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,
- snd_soc_get_volsw, mt6359_put_volsw, playback_tlv),
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
SOC_SINGLE_EXT_TLV("Handset Volume",
MT6359_ZCD_CON3, 0, 0x12, 0,
- snd_soc_get_volsw, mt6359_put_volsw, playback_tlv),
+ mt6359_get_playback_volsw, mt6359_put_volsw,
+ playback_tlv),
/* ul pga gain */
SOC_SINGLE_EXT_TLV("PGA1 Volume",
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
index cc2df5f7ea19..5c50c7de26cd 100644
--- a/sound/soc/codecs/mt6660.c
+++ b/sound/soc/codecs/mt6660.c
@@ -570,7 +570,7 @@ static struct i2c_driver mt6660_i2c_driver = {
.of_match_table = of_match_ptr(mt6660_of_id),
.pm = &mt6660_dev_pm_ops,
},
- .probe_new = mt6660_i2c_probe,
+ .probe = mt6660_i2c_probe,
.remove = mt6660_i2c_remove,
.id_table = mt6660_i2c_id,
};
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index 0626d5694c22..2174a89772fc 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -890,7 +890,7 @@ static struct i2c_driver nau8540_i2c_driver = {
.name = "nau8540",
.of_match_table = of_match_ptr(nau8540_of_ids),
},
- .probe_new = nau8540_i2c_probe,
+ .probe = nau8540_i2c_probe,
.id_table = nau8540_i2c_ids,
};
module_i2c_driver(nau8540_i2c_driver);
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
index ccb512c21d74..47f000cd4d99 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -914,7 +914,7 @@ static struct i2c_driver nau8810_i2c_driver = {
.name = "nau8810",
.of_match_table = of_match_ptr(nau8810_of_match),
},
- .probe_new = nau8810_i2c_probe,
+ .probe = nau8810_i2c_probe,
.id_table = nau8810_i2c_id,
};
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index fee970427a24..96d75882b33a 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -1859,7 +1859,7 @@ static struct i2c_driver nau8821_driver = {
.of_match_table = of_match_ptr(nau8821_of_ids),
.acpi_match_table = ACPI_PTR(nau8821_acpi_match),
},
- .probe_new = nau8821_i2c_probe,
+ .probe = nau8821_i2c_probe,
.id_table = nau8821_i2c_ids,
};
module_i2c_driver(nau8821_driver);
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
index d5006d8de639..ff3024899f45 100644
--- a/sound/soc/codecs/nau8822.c
+++ b/sound/soc/codecs/nau8822.c
@@ -1166,7 +1166,7 @@ static struct i2c_driver nau8822_i2c_driver = {
.name = "nau8822",
.of_match_table = of_match_ptr(nau8822_of_match),
},
- .probe_new = nau8822_i2c_probe,
+ .probe = nau8822_i2c_probe,
.id_table = nau8822_i2c_id,
};
module_i2c_driver(nau8822_i2c_driver);
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
index 5a4db8944d06..704af1cf8cbf 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -2030,7 +2030,7 @@ static struct i2c_driver nau8824_i2c_driver = {
.of_match_table = of_match_ptr(nau8824_of_ids),
.acpi_match_table = ACPI_PTR(nau8824_acpi_match),
},
- .probe_new = nau8824_i2c_probe,
+ .probe = nau8824_i2c_probe,
.id_table = nau8824_i2c_ids,
};
module_i2c_driver(nau8824_i2c_driver);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index f4eb999761a4..9e0e4ddf128e 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -53,6 +53,7 @@ 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,6 +203,23 @@ static struct reg_default nau8825_xtalk_baktab[] = {
{ NAU8825_REG_DACR_CTRL, 0x02cf },
};
+/* 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 },
+};
+
+
static const unsigned short logtable[256] = {
0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
@@ -608,8 +628,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 +647,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 +885,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 +911,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,6 +942,32 @@ 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)
{
@@ -970,10 +1027,25 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
/* 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;
@@ -1127,8 +1199,8 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
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 |
@@ -1181,12 +1253,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),
@@ -1678,6 +1751,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.
*/
@@ -1929,6 +2006,9 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
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.
@@ -2173,9 +2253,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.
@@ -2307,9 +2388,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;
}
@@ -2323,8 +2407,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);
@@ -2366,6 +2458,11 @@ static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int
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) {
@@ -2897,8 +2994,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;
}
@@ -2944,7 +3052,7 @@ static struct i2c_driver nau8825_driver = {
.of_match_table = of_match_ptr(nau8825_of_ids),
.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
},
- .probe_new = nau8825_i2c_probe,
+ .probe = nau8825_i2c_probe,
.remove = nau8825_i2c_remove,
.id_table = nau8825_i2c_ids,
};
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index 44b62bc3880f..2abfbb5184da 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -75,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
@@ -386,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)
@@ -442,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)
@@ -490,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;
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 3591f6f53901..735e1942b530 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -327,7 +327,7 @@ static struct i2c_driver pcm1681_i2c_driver = {
.of_match_table = of_match_ptr(pcm1681_dt_ids),
},
.id_table = pcm1681_i2c_id,
- .probe_new = pcm1681_i2c_probe,
+ .probe = pcm1681_i2c_probe,
};
module_i2c_driver(pcm1681_i2c_driver);
diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c
index fafe0dcbe4ea..f2d0b4d21e41 100644
--- a/sound/soc/codecs/pcm1789-i2c.c
+++ b/sound/soc/codecs/pcm1789-i2c.c
@@ -52,7 +52,7 @@ static struct i2c_driver pcm1789_i2c_driver = {
.of_match_table = of_match_ptr(pcm1789_of_match),
},
.id_table = pcm1789_i2c_ids,
- .probe_new = pcm1789_i2c_probe,
+ .probe = pcm1789_i2c_probe,
.remove = pcm1789_i2c_remove,
};
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
index e20fe3a85af8..10579681f44b 100644
--- a/sound/soc/codecs/pcm179x-i2c.c
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -49,7 +49,7 @@ static struct i2c_driver pcm179x_i2c_driver = {
.of_match_table = of_match_ptr(pcm179x_of_match),
},
.id_table = pcm179x_i2c_ids,
- .probe_new = pcm179x_i2c_probe,
+ .probe = pcm179x_i2c_probe,
};
module_i2c_driver(pcm179x_i2c_driver);
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c
index 932c8d41c3ea..a514ebd1b68a 100644
--- a/sound/soc/codecs/pcm186x-i2c.c
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -46,7 +46,7 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c)
}
static struct i2c_driver pcm186x_i2c_driver = {
- .probe_new = pcm186x_i2c_probe,
+ .probe = pcm186x_i2c_probe,
.id_table = pcm186x_i2c_id,
.driver = {
.name = "pcm186x",
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c
index dd21803ba13c..451a8fd8fac5 100644
--- a/sound/soc/codecs/pcm186x.c
+++ b/sound/soc/codecs/pcm186x.c
@@ -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>
diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c
index 2388098eeca3..5330cf46b127 100644
--- a/sound/soc/codecs/pcm3060-i2c.c
+++ b/sound/soc/codecs/pcm3060-i2c.c
@@ -49,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = {
#endif /* CONFIG_OF */
},
.id_table = pcm3060_i2c_id,
- .probe_new = pcm3060_i2c_probe,
+ .probe = pcm3060_i2c_probe,
};
module_i2c_driver(pcm3060_i2c_driver);
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
index a0eec82e9872..7052cc0c97d1 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -44,7 +44,7 @@ static const struct of_device_id pcm3168a_of_match[] = {
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
static struct i2c_driver pcm3168a_i2c_driver = {
- .probe_new = pcm3168a_i2c_probe,
+ .probe = pcm3168a_i2c_probe,
.remove = pcm3168a_i2c_remove,
.id_table = pcm3168a_i2c_id,
.driver = {
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
index 9dfbbe8f4a0b..5cd2b64b9337 100644
--- a/sound/soc/codecs/pcm512x-i2c.c
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match);
#endif
static struct i2c_driver pcm512x_i2c_driver = {
- .probe_new = pcm512x_i2c_probe,
+ .probe = pcm512x_i2c_probe,
.remove = pcm512x_i2c_remove,
.id_table = pcm512x_i2c_id,
.driver = {
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
index 1d523bfd9d84..9697aefc6e03 100644
--- a/sound/soc/codecs/rk3328_codec.c
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <sound/dmaengine_pcm.h>
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index c1568216126e..42bac8150f62 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -2184,7 +2184,7 @@ static const struct regmap_config rt1011_regmap = {
.max_register = RT1011_MAX_REG + 1,
.volatile_reg = rt1011_volatile_register,
.readable_reg = rt1011_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt1011_reg,
.num_reg_defaults = ARRAY_SIZE(rt1011_reg),
.use_single_read = true,
@@ -2483,7 +2483,7 @@ static struct i2c_driver rt1011_i2c_driver = {
.of_match_table = of_match_ptr(rt1011_of_match),
.acpi_match_table = ACPI_PTR(rt1011_acpi_match)
},
- .probe_new = rt1011_i2c_probe,
+ .probe = rt1011_i2c_probe,
.shutdown = rt1011_i2c_shutdown,
.id_table = rt1011_i2c_id,
};
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
index 57d0f1c69e46..38d9f69b08d6 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -1170,7 +1170,7 @@ static struct i2c_driver rt1015_i2c_driver = {
.of_match_table = of_match_ptr(rt1015_of_match),
.acpi_match_table = ACPI_PTR(rt1015_acpi_match),
},
- .probe_new = rt1015_i2c_probe,
+ .probe = rt1015_i2c_probe,
.shutdown = rt1015_i2c_shutdown,
.id_table = rt1015_i2c_id,
};
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
index 37eeec650f03..b1e69fa290b2 100644
--- a/sound/soc/codecs/rt1016.c
+++ b/sound/soc/codecs/rt1016.c
@@ -683,7 +683,7 @@ static struct i2c_driver rt1016_i2c_driver = {
.of_match_table = of_match_ptr(rt1016_of_match),
.acpi_match_table = ACPI_PTR(rt1016_acpi_match),
},
- .probe_new = rt1016_i2c_probe,
+ .probe = rt1016_i2c_probe,
.shutdown = rt1016_i2c_shutdown,
.id_table = rt1016_i2c_id,
};
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index dff2596c81eb..fd55049920c1 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -535,7 +535,7 @@ static const struct regmap_config rt1019_regmap = {
.max_register = RT1019_BEEP_2,
.volatile_reg = rt1019_volatile_register,
.readable_reg = rt1019_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt1019_reg,
.num_reg_defaults = ARRAY_SIZE(rt1019_reg),
};
@@ -600,7 +600,7 @@ static struct i2c_driver rt1019_i2c_driver = {
.of_match_table = of_match_ptr(rt1019_of_match),
.acpi_match_table = ACPI_PTR(rt1019_acpi_match),
},
- .probe_new = rt1019_i2c_probe,
+ .probe = rt1019_i2c_probe,
.id_table = rt1019_i2c_id,
};
module_i2c_driver(rt1019_i2c_driver);
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
index 5b39a440b6dc..59895131e6e0 100644
--- a/sound/soc/codecs/rt1305.c
+++ b/sound/soc/codecs/rt1305.c
@@ -955,7 +955,7 @@ static const struct regmap_config rt1305_regmap = {
RT1305_PR_SPACING),
.volatile_reg = rt1305_volatile_register,
.readable_reg = rt1305_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt1305_reg,
.num_reg_defaults = ARRAY_SIZE(rt1305_reg),
.ranges = rt1305_ranges,
@@ -1170,7 +1170,7 @@ static struct i2c_driver rt1305_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt1305_acpi_match)
#endif
},
- .probe_new = rt1305_i2c_probe,
+ .probe = rt1305_i2c_probe,
.shutdown = rt1305_i2c_shutdown,
.id_table = rt1305_i2c_id,
};
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index 1797af824f60..f43520ca3187 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -68,7 +68,7 @@ static const struct regmap_config rt1308_sdw_regmap = {
.max_register = 0xcfff,
.reg_defaults = rt1308_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -304,9 +304,6 @@ static int rt1308_update_status(struct sdw_slave *slave,
{
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt1308->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt1308->hw_init = false;
@@ -314,7 +311,7 @@ static int rt1308_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED)
+ if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h
index 04ff18fa18e2..f816c73e247e 100644
--- a/sound/soc/codecs/rt1308-sdw.h
+++ b/sound/soc/codecs/rt1308-sdw.h
@@ -159,7 +159,6 @@ struct rt1308_sdw_priv {
struct snd_soc_component *component;
struct regmap *regmap;
struct sdw_slave *sdw_slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
index d2a8e9fe3e23..9d07756cda40 100644
--- a/sound/soc/codecs/rt1308.c
+++ b/sound/soc/codecs/rt1308.c
@@ -773,7 +773,7 @@ static const struct regmap_config rt1308_regmap = {
.max_register = RT1308_MAX_REG,
.volatile_reg = rt1308_volatile_register,
.readable_reg = rt1308_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt1308_reg,
.num_reg_defaults = ARRAY_SIZE(rt1308_reg),
.use_single_read = true,
@@ -862,7 +862,7 @@ static struct i2c_driver rt1308_i2c_driver = {
.of_match_table = of_match_ptr(rt1308_of_match),
.acpi_match_table = ACPI_PTR(rt1308_acpi_match),
},
- .probe_new = rt1308_i2c_probe,
+ .probe = rt1308_i2c_probe,
.shutdown = rt1308_i2c_shutdown,
.id_table = rt1308_i2c_id,
};
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index 2ee5e763e345..721821d9e9af 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -188,7 +188,7 @@ static const struct regmap_config rt1316_sdw_regmap = {
.max_register = 0x4108ffff,
.reg_defaults = rt1316_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -323,9 +323,6 @@ static int rt1316_update_status(struct sdw_slave *slave,
{
struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt1316->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt1316->hw_init = false;
@@ -333,7 +330,7 @@ static int rt1316_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt1316->hw_init || rt1316->status != SDW_SLAVE_ATTACHED)
+ if (rt1316->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h
index e37121655bc1..dc1bfe40edd3 100644
--- a/sound/soc/codecs/rt1316-sdw.h
+++ b/sound/soc/codecs/rt1316-sdw.h
@@ -42,7 +42,6 @@ struct rt1316_sdw_priv {
struct snd_soc_component *component;
struct regmap *regmap;
struct sdw_slave *sdw_slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
index 795accedc22c..16d750102c8c 100644
--- a/sound/soc/codecs/rt1318-sdw.c
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -337,7 +337,7 @@ static const struct regmap_config rt1318_sdw_regmap = {
.max_register = 0x41081488,
.reg_defaults = rt1318_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -456,9 +456,6 @@ static int rt1318_update_status(struct sdw_slave *slave,
{
struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt1318->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt1318->hw_init = false;
@@ -466,7 +463,7 @@ static int rt1318_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt1318->hw_init || rt1318->status != SDW_SLAVE_ATTACHED)
+ if (rt1318->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h
index 85918c184f16..86e83d63a017 100644
--- a/sound/soc/codecs/rt1318-sdw.h
+++ b/sound/soc/codecs/rt1318-sdw.h
@@ -88,7 +88,6 @@ struct rt1318_sdw_priv {
struct snd_soc_component *component;
struct regmap *regmap;
struct sdw_slave *sdw_slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 4667bf7561b1..9a33e3776b55 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1221,7 +1221,7 @@ static struct i2c_driver rt274_i2c_driver = {
.of_match_table = of_match_ptr(rt274_of_match),
#endif
},
- .probe_new = rt274_i2c_probe,
+ .probe = rt274_i2c_probe,
.remove = rt274_i2c_remove,
.id_table = rt274_i2c_id,
};
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index ceb56647e369..981155b046fd 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1263,7 +1263,7 @@ static struct i2c_driver rt286_i2c_driver = {
.name = "rt286",
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
- .probe_new = rt286_i2c_probe,
+ .probe = rt286_i2c_probe,
.remove = rt286_i2c_remove,
.id_table = rt286_i2c_id,
};
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index cea26f3a02b6..8fbd25ad9b47 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1311,7 +1311,7 @@ static struct i2c_driver rt298_i2c_driver = {
.name = "rt298",
.acpi_match_table = ACPI_PTR(rt298_acpi_match),
},
- .probe_new = rt298_i2c_probe,
+ .probe = rt298_i2c_probe,
.remove = rt298_i2c_remove,
.id_table = rt298_i2c_id,
};
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index b9bcf04d4dc9..b3aac2373357 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -1195,7 +1195,7 @@ 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_read = true,
@@ -1328,7 +1328,7 @@ static struct i2c_driver rt5514_i2c_driver = {
.of_match_table = of_match_ptr(rt5514_of_match),
.pm = &rt5514_i2_pm_ops,
},
- .probe_new = rt5514_i2c_probe,
+ .probe = rt5514_i2c_probe,
.id_table = rt5514_i2c_id,
};
module_i2c_driver(rt5514_i2c_driver);
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 948abde10463..c13108b51eaf 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1315,7 +1315,7 @@ static const struct regmap_config rt5616_regmap = {
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,
@@ -1404,7 +1404,7 @@ static struct i2c_driver rt5616_i2c_driver = {
.name = "rt5616",
.of_match_table = of_match_ptr(rt5616_of_match),
},
- .probe_new = rt5616_i2c_probe,
+ .probe = rt5616_i2c_probe,
.remove = rt5616_i2c_remove,
.shutdown = rt5616_i2c_shutdown,
.id_table = rt5616_i2c_id,
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 55c232413e2b..a64e66c2d3c4 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1693,7 +1693,7 @@ 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,
};
@@ -1728,7 +1728,7 @@ static struct i2c_driver rt5631_i2c_driver = {
.name = "rt5631",
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
},
- .probe_new = 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 139257055507..0ed4fa261abf 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -2949,7 +2949,7 @@ static const struct regmap_config rt5640_regmap = {
.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,
@@ -3079,7 +3079,7 @@ static struct i2c_driver rt5640_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5640_acpi_match),
.of_match_table = of_match_ptr(rt5640_of_match),
},
- .probe_new = rt5640_i2c_probe,
+ .probe = rt5640_i2c_probe,
.id_table = rt5640_i2c_id,
};
module_i2c_driver(rt5640_i2c_driver);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 7c7cbb6362ea..acc7fb1581b2 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3546,7 +3546,7 @@ static const struct regmap_config rt5645_regmap = {
.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,
@@ -3563,7 +3563,7 @@ static const struct regmap_config rt5650_regmap = {
.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,
@@ -4184,7 +4184,7 @@ static struct i2c_driver rt5645_i2c_driver = {
.of_match_table = of_match_ptr(rt5645_of_match),
.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
},
- .probe_new = rt5645_i2c_probe,
+ .probe = rt5645_i2c_probe,
.remove = rt5645_i2c_remove,
.shutdown = rt5645_i2c_shutdown,
.id_table = rt5645_i2c_id,
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index df90af906563..0cee4fd1c84b 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -2172,7 +2172,7 @@ 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,
@@ -2279,7 +2279,7 @@ static struct i2c_driver rt5651_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5651_acpi_match),
.of_match_table = of_match_ptr(rt5651_of_match),
},
- .probe_new = rt5651_i2c_probe,
+ .probe = rt5651_i2c_probe,
.id_table = rt5651_i2c_id,
};
module_i2c_driver(rt5651_i2c_driver);
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 5e21e3c37ab5..df6f0d769bbd 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -4141,13 +4141,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);
@@ -4340,7 +4336,7 @@ static struct i2c_driver rt5659_i2c_driver = {
.of_match_table = of_match_ptr(rt5659_of_match),
.acpi_match_table = ACPI_PTR(rt5659_acpi_match),
},
- .probe_new = rt5659_i2c_probe,
+ .probe = rt5659_i2c_probe,
.shutdown = rt5659_i2c_shutdown,
.id_table = rt5659_i2c_id,
};
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index 341baa29fdb1..eade087b2d8b 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1221,7 +1221,7 @@ static const struct regmap_config rt5660_regmap = {
.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,
@@ -1341,7 +1341,7 @@ static struct i2c_driver rt5660_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5660_acpi_match),
.of_match_table = of_match_ptr(rt5660_of_match),
},
- .probe_new = rt5660_i2c_probe,
+ .probe = rt5660_i2c_probe,
.id_table = rt5660_i2c_id,
};
module_i2c_driver(rt5660_i2c_driver);
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index f73751dbde30..77246f84de29 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3268,7 +3268,7 @@ static const struct regmap_config rt5663_v2_regmap = {
.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),
};
@@ -3281,7 +3281,7 @@ static const struct regmap_config rt5663_regmap = {
.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),
};
@@ -3733,7 +3733,7 @@ static struct i2c_driver rt5663_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5663_acpi_match),
.of_match_table = of_match_ptr(rt5663_of_match),
},
- .probe_new = rt5663_i2c_probe,
+ .probe = rt5663_i2c_probe,
.remove = rt5663_i2c_remove,
.shutdown = rt5663_i2c_shutdown,
.id_table = rt5663_i2c_id,
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 17afaef85c77..83c367af91da 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4626,7 +4626,7 @@ 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_read = true,
@@ -4968,7 +4968,7 @@ static struct i2c_driver rt5665_i2c_driver = {
.of_match_table = of_match_ptr(rt5665_of_match),
.acpi_match_table = ACPI_PTR(rt5665_acpi_match),
},
- .probe_new = rt5665_i2c_probe,
+ .probe = rt5665_i2c_probe,
.shutdown = rt5665_i2c_shutdown,
.id_table = rt5665_i2c_id,
};
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index ecf3b0527dbe..f04c810fd710 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2370,7 +2370,7 @@ static const struct regmap_config rt5668_regmap = {
.max_register = RT5668_I2C_MODE,
.volatile_reg = rt5668_volatile_register,
.readable_reg = rt5668_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5668_reg,
.num_reg_defaults = ARRAY_SIZE(rt5668_reg),
.use_single_read = true,
@@ -2618,7 +2618,7 @@ static struct i2c_driver rt5668_i2c_driver = {
.of_match_table = of_match_ptr(rt5668_of_match),
.acpi_match_table = ACPI_PTR(rt5668_acpi_match),
},
- .probe_new = rt5668_i2c_probe,
+ .probe = rt5668_i2c_probe,
.shutdown = rt5668_i2c_shutdown,
.id_table = rt5668_i2c_id,
};
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index a230f441559a..0e34293f3395 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2863,7 +2863,7 @@ static const struct regmap_config rt5670_regmap = {
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,
@@ -3328,7 +3328,7 @@ static struct i2c_driver rt5670_i2c_driver = {
.name = "rt5670",
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
- .probe_new = rt5670_i2c_probe,
+ .probe = rt5670_i2c_probe,
.remove = rt5670_i2c_remove,
.id_table = rt5670_i2c_id,
};
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 3bf019b3f700..ad14d18860fc 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5704,7 +5704,7 @@ static struct i2c_driver rt5677_i2c_driver = {
.of_match_table = rt5677_of_match,
.acpi_match_table = ACPI_PTR(rt5677_acpi_match),
},
- .probe_new = rt5677_i2c_probe,
+ .probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
};
module_i2c_driver(rt5677_i2c_driver);
@@ -5712,3 +5712,5 @@ 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/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index 5bc46b041786..fb8ffb5b2ff6 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
@@ -47,7 +46,7 @@ static const struct regmap_config rt5682_regmap = {
.max_register = RT5682_I2C_MODE,
.volatile_reg = rt5682_volatile_register,
.readable_reg = rt5682_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5682_reg,
.num_reg_defaults = RT5682_REG_NUM,
.use_single_read = true,
@@ -334,7 +333,7 @@ static struct i2c_driver rt5682_i2c_driver = {
.acpi_match_table = rt5682_acpi_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
- .probe_new = rt5682_i2c_probe,
+ .probe = rt5682_i2c_probe,
.remove = rt5682_i2c_remove,
.shutdown = rt5682_i2c_shutdown,
.id_table = rt5682_i2c_id,
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index 23f17f70d7e9..67404f45389f 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -79,7 +79,7 @@ static const struct regmap_config rt5682_sdw_indirect_regmap = {
.max_register = RT5682_I2C_MODE,
.volatile_reg = rt5682_volatile_register,
.readable_reg = rt5682_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5682_reg,
.num_reg_defaults = RT5682_REG_NUM,
.use_single_read = true,
@@ -500,9 +500,6 @@ static int rt5682_update_status(struct sdw_slave *slave,
{
struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt5682->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt5682->hw_init = false;
@@ -510,7 +507,7 @@ static int rt5682_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt5682->hw_init || rt5682->status != SDW_SLAVE_ATTACHED)
+ if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
index e8efd8a84a6c..1a43d595f341 100644
--- a/sound/soc/codecs/rt5682.h
+++ b/sound/soc/codecs/rt5682.h
@@ -1440,7 +1440,6 @@ struct rt5682_priv {
bool disable_irq;
struct mutex calibrate_mutex;
struct sdw_slave *slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index 9c34dca58f54..c77c675bd5f5 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
@@ -2848,14 +2847,9 @@ static int rt5682s_dai_probe_clks(struct snd_soc_component *component)
int ret;
/* Check if MCLK provided */
- rt5682s->mclk = devm_clk_get(component->dev, "mclk");
- if (IS_ERR(rt5682s->mclk)) {
- if (PTR_ERR(rt5682s->mclk) != -ENOENT) {
- ret = PTR_ERR(rt5682s->mclk);
- return ret;
- }
- rt5682s->mclk = NULL;
- }
+ 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);
@@ -3046,7 +3040,7 @@ static const struct regmap_config rt5682s_regmap = {
.max_register = RT5682S_MAX_REG,
.volatile_reg = rt5682s_volatile_register,
.readable_reg = rt5682s_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5682s_reg,
.num_reg_defaults = ARRAY_SIZE(rt5682s_reg),
.use_single_read = true,
@@ -3316,7 +3310,7 @@ static struct i2c_driver rt5682s_i2c_driver = {
.acpi_match_table = rt5682s_acpi_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
- .probe_new = rt5682s_i2c_probe,
+ .probe = rt5682s_i2c_probe,
.remove = rt5682s_i2c_remove,
.shutdown = rt5682s_i2c_shutdown,
.id_table = rt5682s_i2c_id,
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
index 96fc5f36d0d0..8b28e47775cc 100644
--- a/sound/soc/codecs/rt700-sdw.c
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -292,7 +292,7 @@ static const struct regmap_config rt700_regmap = {
.max_register = 0x755800,
.reg_defaults = rt700_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
.reg_read = rt700_sdw_read,
@@ -315,9 +315,6 @@ static int rt700_update_status(struct sdw_slave *slave,
{
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt700->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt700->hw_init = false;
@@ -325,7 +322,7 @@ static int rt700_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED)
+ if (rt700->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h
index 93c44005d38c..491774d207de 100644
--- a/sound/soc/codecs/rt700.h
+++ b/sound/soc/codecs/rt700.h
@@ -15,7 +15,6 @@ struct rt700_priv {
struct regmap *regmap;
struct regmap *sdw_regmap;
struct sdw_slave *slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
index 51f3335343e0..119e1f9605d7 100644
--- a/sound/soc/codecs/rt711-sdca-sdw.c
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -119,7 +119,7 @@ static const struct regmap_config rt711_sdca_regmap = {
.max_register = 0x44ffffff,
.reg_defaults = rt711_sdca_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -133,7 +133,7 @@ static const struct regmap_config rt711_sdca_mbq_regmap = {
.max_register = 0x40800f12,
.reg_defaults = rt711_sdca_mbq_defaults,
.num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -143,9 +143,6 @@ static int rt711_sdca_update_status(struct sdw_slave *slave,
{
struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt711->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt711->hw_init = false;
@@ -168,7 +165,7 @@ static int rt711_sdca_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED)
+ if (rt711->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
index 22076f268577..11d421e8ab2b 100644
--- a/sound/soc/codecs/rt711-sdca.h
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -19,7 +19,6 @@ struct rt711_sdca_priv {
struct regmap *regmap, *mbq_regmap;
struct snd_soc_component *component;
struct sdw_slave *slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c
index 4fe68bcf2a7c..87dafcb4545d 100644
--- a/sound/soc/codecs/rt711-sdw.c
+++ b/sound/soc/codecs/rt711-sdw.c
@@ -296,7 +296,7 @@ static const struct regmap_config rt711_regmap = {
.max_register = 0x755800,
.reg_defaults = rt711_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
.reg_read = rt711_sdw_read,
@@ -319,9 +319,6 @@ static int rt711_update_status(struct sdw_slave *slave,
{
struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt711->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt711->hw_init = false;
@@ -329,7 +326,7 @@ static int rt711_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED)
+ if (rt711->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h
index b31351f11df9..491e357191f9 100644
--- a/sound/soc/codecs/rt711.h
+++ b/sound/soc/codecs/rt711.h
@@ -15,7 +15,6 @@ struct rt711_priv {
struct regmap *sdw_regmap;
struct snd_soc_component *component;
struct sdw_slave *slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index 09807b6d6353..869cc7bfd178 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -110,7 +110,7 @@ static const struct regmap_config rt712_sdca_dmic_regmap = {
.max_register = 0x40981300,
.reg_defaults = rt712_sdca_dmic_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -124,7 +124,7 @@ static const struct regmap_config rt712_sdca_dmic_mbq_regmap = {
.max_register = 0x40800f14,
.reg_defaults = rt712_sdca_dmic_mbq_defaults,
.num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -803,9 +803,6 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave,
{
struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt712->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt712->hw_init = false;
@@ -813,7 +810,7 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED)
+ if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h
index 74c29677c251..110154e74efe 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.h
+++ b/sound/soc/codecs/rt712-sdca-dmic.h
@@ -16,7 +16,6 @@ struct rt712_sdca_dmic_priv {
struct regmap *mbq_regmap;
struct snd_soc_component *component;
struct sdw_slave *slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
index 3f319459dfec..ad06267b0ea0 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.c
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -116,7 +116,7 @@ static const struct regmap_config rt712_sdca_regmap = {
.max_register = 0x44ffffff,
.reg_defaults = rt712_sdca_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -130,7 +130,7 @@ static const struct regmap_config rt712_sdca_mbq_regmap = {
.max_register = 0x41000312,
.reg_defaults = rt712_sdca_mbq_defaults,
.num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -140,9 +140,6 @@ static int rt712_sdca_update_status(struct sdw_slave *slave,
{
struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt712->status = status;
-
if (status == SDW_SLAVE_UNATTACHED)
rt712->hw_init = false;
@@ -165,7 +162,7 @@ static int rt712_sdca_update_status(struct sdw_slave *slave,
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED)
+ if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
index c6a94a23f46e..ff79e03118ce 100644
--- a/sound/soc/codecs/rt712-sdca.h
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -20,7 +20,6 @@ struct rt712_sdca_priv {
struct regmap *mbq_regmap;
struct snd_soc_component *component;
struct sdw_slave *slave;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index 38a82e4e2f95..df10916bab46 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -97,7 +97,7 @@ static const struct regmap_config rt715_sdca_regmap = {
.max_register = 0x43ffffff,
.reg_defaults = rt715_reg_defaults_sdca,
.num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -111,7 +111,7 @@ static const struct regmap_config rt715_sdca_mbq_regmap = {
.max_register = 0x43ffffff,
.reg_defaults = rt715_mbq_reg_defaults_sdca,
.num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -121,14 +121,11 @@ static int rt715_sdca_update_status(struct sdw_slave *slave,
{
struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt715->status = status;
-
/*
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED)
+ if (rt715->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h
index 7577f3151934..e5d6928ecaba 100644
--- a/sound/soc/codecs/rt715-sdca.h
+++ b/sound/soc/codecs/rt715-sdca.h
@@ -24,7 +24,6 @@ struct rt715_sdca_priv {
int dbg_nid;
int dbg_vid;
int dbg_payload;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
index 4e61e16470ed..6db87442b783 100644
--- a/sound/soc/codecs/rt715-sdw.c
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -354,7 +354,7 @@ static const struct regmap_config rt715_regmap = {
.max_register = 0x752039, /* Maximum number of register */
.reg_defaults = rt715_reg_defaults, /* Defaults */
.num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
.reg_read = rt715_sdw_read,
@@ -417,13 +417,11 @@ static int rt715_update_status(struct sdw_slave *slave,
{
struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
- /* Update the status */
- rt715->status = status;
/*
* Perform initialization only if slave status is present and
* hw_init flag is false
*/
- if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED)
+ if (rt715->hw_init || status != SDW_SLAVE_ATTACHED)
return 0;
/* perform I/O transfers required for Slave initialization */
diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h
index 17a8d041c1c3..12a0ae656d09 100644
--- a/sound/soc/codecs/rt715.h
+++ b/sound/soc/codecs/rt715.h
@@ -18,7 +18,6 @@ struct rt715_priv {
int dbg_nid;
int dbg_vid;
int dbg_payload;
- enum sdw_slave_status status;
struct sdw_bus_params params;
bool hw_init;
bool first_hw_init;
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
new file mode 100644
index 000000000000..cc57e4e27805
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -0,0 +1,507 @@
+// 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 bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ 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_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 RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+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_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ 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 RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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 0x5300000 ... 0x5300002:
+ case 0x5400002:
+ case 0x5600000 ... 0x5600007:
+ case 0x5700000 ... 0x5700004:
+ case 0x5800000 ... 0x5800004:
+ case 0x5b00003:
+ case 0x5c00011:
+ case 0x5d00006:
+ case 0x5f00000 ... 0x5f0000d:
+ case 0x5f00030:
+ case 0x6100000 ... 0x6100051:
+ case 0x6100055 ... 0x6100057:
+ case 0x6100062:
+ case 0x6100064 ... 0x6100065:
+ case 0x6100067:
+ case 0x6100070 ... 0x610007c:
+ case 0x6100080:
+ 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 true;
+ default:
+ return false;
+ }
+}
+
+static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt722_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .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 const struct regmap_config rt722_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt722_sdca_mbq_readable_register,
+ .volatile_reg = rt722_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt722_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_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_SCP_SDCA_INTMASK_SDCA_6);
+ 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;
+
+ 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 = 200;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ 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;
+ } else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) {
+ ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6);
+ 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(30));
+
+ 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 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, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt722_sdca_init(&slave->dev, regmap, mbq_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 __maybe_unused 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);
+ regcache_cache_only(rt722->mbq_regmap, true);
+
+ return 0;
+}
+
+static int __maybe_unused 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 | SDW_SCP_SDCA_INTMASK_SDCA_6, 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 __maybe_unused 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)
+ 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);
+ regcache_cache_only(rt722->mbq_regmap, false);
+ regcache_sync(rt722->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt722_sdca_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume)
+ SET_RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt722_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt722-sdca",
+ .owner = THIS_MODULE,
+ .pm = &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..5b43e86f75d1
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.h
@@ -0,0 +1,124 @@
+/* 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 },
+ { 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_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { 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_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_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_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { 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_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { 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_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ 0x09 },
+ { 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_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+static const struct reg_default rt722_sdca_mbq_defaults[] = {
+ { 0x200003c, 0xc214 },
+ { 0x2000046, 0x8004 },
+ { 0x6100006, 0x0005 },
+ { 0x6100010, 0x2630 },
+ { 0x6100011, 0x152f },
+ { 0x6100013, 0x0102 },
+ { 0x6100015, 0x2200 },
+ { 0x6100017, 0x0102 },
+ { 0x6100025, 0x2a29 },
+ { 0x6100026, 0x2a00 },
+ { 0x6100028, 0x2a2a },
+ { 0x6100029, 0x4141 },
+ { 0x6100055, 0x0000 },
+ { 0x5810000, 0x702d },
+ { 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_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_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_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_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_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 },
+};
+
+#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..9c0d34366c9e
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -0,0 +1,1555 @@
+// 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"
+
+int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ struct regmap *regmap = rt722->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "Failed to set private value: %06x <= %04x ret=%d\n",
+ 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->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "Failed to get private value: %06x => %04x ret=%d\n",
+ 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_6 is used for jack detection */
+ if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6 ||
+ 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 | SDW_SCP_SDCA_INTMASK_SDCA_6);
+ /* 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);
+ /* 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_mark_last_busy(component->dev);
+ 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->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt722->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)
+ changed = 1;
+ else
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt722->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt722->mbq_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->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt722->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 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->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else { /* ADC gain */
+ if (adc_vol_flag)
+ ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+ else
+ ctl = p->max - (((0 - 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->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(rt722->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt722->slave->dev, "%#08x can't be set\n", 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 int rt722_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 rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+ mask_sft = 12;
+ 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;
+
+ rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+ return 0;
+}
+
+static int rt722_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 rt722_sdca_priv *rt722 = 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 24 Mux"))
+ mask_sft = 4;
+ else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 0;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = (0x7 << mask_sft) & val2;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_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 adc22_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+};
+
+static const char * const adc07_10_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt722_sdca_adc22_mux =
+ SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum,
+ rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put);
+
+static const struct snd_kcontrol_new rt722_sdca_adc24_mux =
+ SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum,
+ rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put);
+
+static const struct snd_kcontrol_new rt722_sdca_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum,
+ rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put);
+
+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);
+ 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 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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, "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 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, "Rate %d is not supported\n",
+ 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 = 2,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ }
+};
+
+int rt722_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_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;
+ rt722->mbq_regmap = mbq_regmap;
+
+ 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)
+{
+ /* 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);
+}
+
+static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
+{
+ /* 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);
+}
+
+static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
+{
+ int loop_check, chk_cnt = 100, ret;
+ unsigned int calib_status = 0;
+
+ /* Read eFuse */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL,
+ 0x4808);
+ /* 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++) {
+ ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI,
+ RT722_DAC_DC_CALI_CTL3, &calib_status);
+ if (ret < 0 || loop_check == chk_cnt)
+ dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret);
+ if ((calib_status & 0x0040) == 0x0)
+ break;
+ }
+ /* 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);
+ /* 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);
+ /* Fine tune PDE40 latency */
+ regmap_write(rt722->regmap, 0x2f58, 0x07);
+}
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+
+ rt722->disable_irq = false;
+
+ if (rt722->hw_init)
+ return 0;
+
+ if (rt722->first_hw_init) {
+ regcache_cache_only(rt722->regmap, false);
+ regcache_cache_bypass(rt722->regmap, true);
+ regcache_cache_only(rt722->mbq_regmap, false);
+ regcache_cache_bypass(rt722->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);
+
+ 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);
+ regcache_cache_bypass(rt722->mbq_regmap, false);
+ regcache_mark_dirty(rt722->mbq_regmap);
+ } else
+ rt722->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt722->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+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..44af8901352e
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -0,0 +1,237 @@
+/* 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 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 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_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
+
+/* 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
+
+/* 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
+
+/* 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
+
+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,
+};
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt722_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_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
index fcf4fbaed3c7..733a7d130a95 100644
--- a/sound/soc/codecs/rt9120.c
+++ b/sound/soc/codecs/rt9120.c
@@ -633,7 +633,7 @@ static struct i2c_driver rt9120_driver = {
.of_match_table = rt9120_device_table,
.pm = &rt9120_pm_ops,
},
- .probe_new = rt9120_probe,
+ .probe = rt9120_probe,
.remove = rt9120_remove,
};
module_i2c_driver(rt9120_driver);
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index a916f4619ea3..b22ba95bd0c0 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1826,7 +1826,7 @@ static struct i2c_driver sgtl5000_i2c_driver = {
.name = "sgtl5000",
.of_match_table = sgtl5000_dt_ids,
},
- .probe_new = sgtl5000_i2c_probe,
+ .probe = sgtl5000_i2c_probe,
.remove = sgtl5000_i2c_remove,
.shutdown = sgtl5000_i2c_shutdown,
.id_table = sgtl5000_id,
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
index b6c132edf3bd..7b9abbc1bd94 100644
--- a/sound/soc/codecs/sma1303.c
+++ b/sound/soc/codecs/sma1303.c
@@ -1807,7 +1807,7 @@ static struct i2c_driver sma1303_i2c_driver = {
.name = "sma1303",
.of_match_table = sma1303_of_match,
},
- .probe_new = sma1303_i2c_probe,
+ .probe = sma1303_i2c_probe,
.remove = sma1303_i2c_remove,
.id_table = sma1303_i2c_id,
};
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
index a40fd20df984..93af8e209b05 100644
--- a/sound/soc/codecs/src4xxx-i2c.c
+++ b/sound/soc/codecs/src4xxx-i2c.c
@@ -36,7 +36,7 @@ static struct i2c_driver src4xxx_i2c_driver = {
.name = "src4xxx",
.of_match_table = of_match_ptr(src4xxx_of_match),
},
- .probe_new = src4xxx_i2c_probe,
+ .probe = src4xxx_i2c_probe,
.id_table = src4xxx_i2c_ids,
};
module_i2c_driver(src4xxx_i2c_driver);
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 22cb3b7c8283..d20d897407eb 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -803,7 +803,7 @@ static struct i2c_driver ssm2518_driver = {
.name = "ssm2518",
.of_match_table = of_match_ptr(ssm2518_dt_ids),
},
- .probe_new = ssm2518_i2c_probe,
+ .probe = ssm2518_i2c_probe,
.id_table = ssm2518_i2c_ids,
};
module_i2c_driver(ssm2518_driver);
diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c
index 3c85772901f5..596096466cd4 100644
--- a/sound/soc/codecs/ssm2602-i2c.c
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -49,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = {
.name = "ssm2602",
.of_match_table = ssm2602_of_match,
},
- .probe_new = ssm2602_i2c_probe,
+ .probe = ssm2602_i2c_probe,
.id_table = ssm2602_i2c_id,
};
module_i2c_driver(ssm2602_i2c_driver);
diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c
new file mode 100644
index 000000000000..008cb3eb5758
--- /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 < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, SSM3515_SAI2,
+ SSM3515_SAI2_TDM_SLOT,
+ FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ssm3515_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ /*
+ * We don't get live notification of faults, so at least at
+ * this time, when playback is over, check if we have tripped
+ * over anything and if so, log it.
+ */
+ ssm3515_read_faults(dai->component);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ssm3515_dai_ops = {
+ .mute_stream = ssm3515_mute,
+ .hw_params = ssm3515_hw_params,
+ .set_fmt = ssm3515_set_fmt,
+ .set_tdm_slot = ssm3515_set_tdm_slot,
+ .hw_free = ssm3515_hw_free,
+};
+
+static struct snd_soc_dai_driver ssm3515_dai_driver = {
+ .name = "SSM3515 SAI",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ssm3515_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = {
+ {"OUT", NULL, "DAC"},
+ {"DAC", NULL, "Playback"},
+};
+
+static const struct snd_soc_component_driver ssm3515_asoc_component = {
+ .probe = ssm3515_probe,
+ .controls = ssm3515_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm3515_snd_controls),
+ .dapm_widgets = ssm3515_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets),
+ .dapm_routes = ssm3515_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes),
+ .endianness = 1,
+};
+
+static int ssm3515_i2c_probe(struct i2c_client *client)
+{
+ struct ssm3515_data *data;
+ int ret;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &client->dev;
+ i2c_set_clientdata(client, data);
+
+ data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(data->dev, PTR_ERR(data->regmap),
+ "initializing register map\n");
+
+ /* Perform a reset */
+ ret = regmap_update_bits(data->regmap, SSM3515_PWR,
+ SSM3515_PWR_S_RST, SSM3515_PWR_S_RST);
+ if (ret < 0)
+ return dev_err_probe(data->dev, ret,
+ "performing software reset\n");
+ regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap);
+
+ return devm_snd_soc_register_component(data->dev,
+ &ssm3515_asoc_component,
+ &ssm3515_dai_driver, 1);
+}
+
+static const struct of_device_id ssm3515_of_match[] = {
+ { .compatible = "adi,ssm3515" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ssm3515_of_match);
+
+static struct i2c_driver ssm3515_i2c_driver = {
+ .driver = {
+ .name = "ssm3515",
+ .of_match_table = of_match_ptr(ssm3515_of_match),
+ },
+ .probe = ssm3515_i2c_probe,
+};
+module_i2c_driver(ssm3515_i2c_driver);
+
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 4b0265617c7b..0a6f04d8f636 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -500,7 +500,7 @@ static struct i2c_driver ssm4567_driver = {
.of_match_table = of_match_ptr(ssm4567_of_match),
.acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
},
- .probe_new = ssm4567_i2c_probe,
+ .probe = ssm4567_i2c_probe,
.id_table = ssm4567_i2c_ids,
};
module_i2c_driver(ssm4567_driver);
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 29af9595dac1..4a694d0bfd68 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -1167,7 +1167,7 @@ static struct i2c_driver sta32x_i2c_driver = {
.name = "sta32x",
.of_match_table = of_match_ptr(st32x_dt_ids),
},
- .probe_new = sta32x_i2c_probe,
+ .probe = sta32x_i2c_probe,
.id_table = sta32x_i2c_id,
};
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index b033a5fcd6c0..d05f3fd57661 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1249,7 +1249,7 @@ static struct i2c_driver sta350_i2c_driver = {
.name = "sta350",
.of_match_table = of_match_ptr(st350_dt_ids),
},
- .probe_new = sta350_i2c_probe,
+ .probe = sta350_i2c_probe,
.remove = sta350_i2c_remove,
.id_table = sta350_i2c_id,
};
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 313957099145..0ac08478ddac 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -379,7 +379,7 @@ static struct i2c_driver sta529_i2c_driver = {
.name = "sta529",
.of_match_table = sta529_of_match,
},
- .probe_new = sta529_i2c_probe,
+ .probe = sta529_i2c_probe,
.id_table = sta529_i2c_id,
};
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index 59a4ea5f6e30..8c9dc318b0e8 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -761,7 +761,7 @@ static struct i2c_driver tas2552_i2c_driver = {
.of_match_table = of_match_ptr(tas2552_of_match),
.pm = &tas2552_pm,
},
- .probe_new = tas2552_probe,
+ .probe = tas2552_probe,
.remove = tas2552_i2c_remove,
.id_table = tas2552_id,
};
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index b486d0bd86c9..962c2cdfa017 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -8,7 +8,6 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/i2c.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
@@ -784,7 +783,7 @@ static struct i2c_driver tas2562_i2c_driver = {
.name = "tas2562",
.of_match_table = of_match_ptr(tas2562_of_match),
},
- .probe_new = tas2562_probe,
+ .probe = tas2562_probe,
.id_table = tas2562_id,
};
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 2e0ed3e68fa5..a9838e0738cc 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -756,7 +756,7 @@ static struct i2c_driver tas2764_i2c_driver = {
.name = "tas2764",
.of_match_table = of_match_ptr(tas2764_of_match),
},
- .probe_new = tas2764_i2c_probe,
+ .probe = tas2764_i2c_probe,
.id_table = tas2764_i2c_id,
};
module_i2c_driver(tas2764_i2c_driver);
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index 8557759acb1f..99bf402eb566 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -720,7 +720,7 @@ static struct i2c_driver tas2770_i2c_driver = {
.name = "tas2770",
.of_match_table = of_match_ptr(tas2770_of_match),
},
- .probe_new = tas2770_i2c_probe,
+ .probe = tas2770_i2c_probe,
.id_table = tas2770_i2c_id,
};
module_i2c_driver(tas2770_i2c_driver);
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
index 09e7ada1bca4..86bd6c18a944 100644
--- a/sound/soc/codecs/tas2780.c
+++ b/sound/soc/codecs/tas2780.c
@@ -645,7 +645,7 @@ static struct i2c_driver tas2780_i2c_driver = {
.name = "tas2780",
.of_match_table = of_match_ptr(tas2780_of_match),
},
- .probe_new = tas2780_i2c_probe,
+ .probe = tas2780_i2c_probe,
.id_table = tas2780_i2c_id,
};
module_i2c_driver(tas2780_i2c_driver);
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
new file mode 100644
index 000000000000..a88c6c28a394
--- /dev/null
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
+//
+// Copyright 2023 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+
+#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
+ unsigned short chn, int book)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+ struct regmap *map = tas_priv->regmap;
+
+ if (client->addr != tasdev->dev_addr) {
+ client->addr = tasdev->dev_addr;
+ if (tasdev->cur_book == book) {
+ ret = regmap_write(map,
+ TASDEVICE_PAGE_SELECT, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ }
+ goto out;
+ }
+
+ if (tasdev->cur_book != book) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ tasdev->cur_book = book;
+ }
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+
+int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int *val)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tasdevice_change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_read);
+
+int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int value)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tasdevice_change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(map, TASDEVICE_PGRG(reg),
+ value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_write);
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tas_priv, unsigned short chn,
+ unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tasdevice_change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg),
+ data, len);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write);
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tasdevice_change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tas_priv, unsigned short chn,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tasdevice_change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
+ mask, value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ ret = -EINVAL;
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
+
+struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
+{
+ struct tasdevice_priv *tas_priv;
+
+ tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ return NULL;
+ tas_priv->dev = &i2c->dev;
+ tas_priv->client = (void *)i2c;
+
+ return tas_priv;
+}
+EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
+
+void tas2781_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret, i;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ TAS2781_REG_SWRESET,
+ TAS2781_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "dev %d swreset fail, %d\n",
+ i, ret);
+ }
+ }
+ usleep_range(1000, 1050);
+}
+EXPORT_SYMBOL_GPL(tas2781_reset);
+
+int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret = 0;
+
+ /* Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ mutex_lock(&tas_priv->codec_lock);
+
+ scnprintf(tas_priv->rca_binaryname, 64, "%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(THIS_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);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_priv->codec_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tascodec_init);
+
+int tasdevice_init(struct tasdevice_priv *tas_priv)
+{
+ int ret = 0;
+ int i;
+
+ tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
+ &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto out;
+ }
+
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tas_priv->tasdevice[i].cur_book = -1;
+ tas_priv->tasdevice[i].cur_prog = -1;
+ tas_priv->tasdevice[i].cur_conf = -1;
+ }
+
+ dev_set_drvdata(tas_priv->dev, tas_priv);
+
+ mutex_init(&tas_priv->codec_lock);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_init);
+
+static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (!prog)
+ return;
+
+ tas_dt = &(prog->dev_data);
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &(tas_dt->dev_blks[i]);
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+}
+
+static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_prog_blk_remove(&prog[i]);
+ kfree(prog);
+}
+
+static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (cfg) {
+ tas_dt = &(cfg->dev_data);
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &(tas_dt->dev_blks[i]);
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+ }
+}
+
+static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_cfg_blk_remove(&config[i]);
+ kfree(config);
+}
+
+void tasdevice_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+
+ if (!tas_dev->fmw)
+ return;
+
+ if (tas_fmw->programs)
+ tasdev_dsp_prog_remove(tas_fmw->programs,
+ tas_fmw->nr_programs);
+ if (tas_fmw->configs)
+ tasdev_dsp_cfg_remove(tas_fmw->configs,
+ tas_fmw->nr_configurations);
+ kfree(tas_fmw);
+ tas_dev->fmw = NULL;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dsp_remove);
+
+void tasdevice_remove(struct tasdevice_priv *tas_priv)
+{
+ if (gpio_is_valid(tas_priv->irq_info.irq_gpio))
+ gpio_free(tas_priv->irq_info.irq_gpio);
+ kfree(tas_priv->acpi_subsystem_id);
+ mutex_destroy(&tas_priv->codec_lock);
+}
+EXPORT_SYMBOL_GPL(tasdevice_remove);
+
+static int tasdevice_clamp(int val, int max, unsigned int invert)
+{
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+int tasdevice_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 err_cnt = 0;
+ int val, i, ret;
+
+ mask = (1 << fls(max)) - 1;
+ mask <<= mc->shift;
+ val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+ for (i = 0; i < tas_priv->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_priv, i,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (!ret)
+ continue;
+ err_cnt++;
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
+ }
+
+ /* All the devices set error, return 0 */
+ return (err_cnt == tas_priv->ndev) ? 0 : 1;
+}
+EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
+
+int tasdevice_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 = 0;
+ int val;
+
+ /* Read the primary device */
+ ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ mask = (1 << fls(max)) - 1;
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = tasdevice_clamp(val, max, invert);
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
+
+int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int err_cnt = 0;
+ int ret;
+ int val, i;
+
+ val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ ret = tasdevice_dev_write(tas_priv, i, mc->reg,
+ (unsigned int)val);
+ if (!ret)
+ continue;
+ err_cnt++;
+ dev_err(tas_priv->dev,
+ "set digital vol err in dev %d\n", i);
+ }
+
+ /* All the devices set error, return 0 */
+ return (err_cnt == tas_priv->ndev) ? 0 : 1;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
+
+int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get digital vol error\n",
+ __func__);
+ goto out;
+ }
+
+ val = tasdevice_clamp(val, max, invert);
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
+
+MODULE_DESCRIPTION("TAS2781 common library");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
new file mode 100644
index 000000000000..eb55abae0d7b
--- /dev/null
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -0,0 +1,2428 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// tasdevice-fmw.c -- TASDEVICE firmware support
+//
+// Copyright 2023 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas2781.h>
+
+
+#define ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define PPC_DRIVER_CRCCHK 0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1 140
+#define TAS2781_YRAM1_PAGE 42
+#define TAS2781_YRAM1_START_REG 88
+
+#define TAS2781_YRAM2_START_PAGE 43
+#define TAS2781_YRAM2_END_PAGE 49
+#define TAS2781_YRAM2_START_REG 8
+#define TAS2781_YRAM2_END_REG 127
+
+#define TAS2781_YRAM3_PAGE 50
+#define TAS2781_YRAM3_START_REG 8
+#define TAS2781_YRAM3_END_REG 27
+
+/*should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2 0
+#define TAS2781_YRAM4_START_PAGE 50
+#define TAS2781_YRAM4_END_PAGE 60
+
+#define TAS2781_YRAM5_PAGE 61
+#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG
+#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
+#define MAIN_ALL_DEVICES_1X 0x01
+#define MAIN_DEVICE_A_1X 0x02
+#define MAIN_DEVICE_B_1X 0x03
+#define MAIN_DEVICE_C_1X 0x04
+#define MAIN_DEVICE_D_1X 0x05
+#define COEFF_DEVICE_A_1X 0x12
+#define COEFF_DEVICE_B_1X 0x13
+#define COEFF_DEVICE_C_1X 0x14
+#define COEFF_DEVICE_D_1X 0x15
+#define PRE_DEVICE_A_1X 0x22
+#define PRE_DEVICE_B_1X 0x23
+#define PRE_DEVICE_C_1X 0x24
+#define PRE_DEVICE_D_1X 0x25
+#define PRE_SOFTWARE_RESET_DEVICE_A 0x41
+#define PRE_SOFTWARE_RESET_DEVICE_B 0x42
+#define PRE_SOFTWARE_RESET_DEVICE_C 0x43
+#define PRE_SOFTWARE_RESET_DEVICE_D 0x44
+#define POST_SOFTWARE_RESET_DEVICE_A 0x45
+#define POST_SOFTWARE_RESET_DEVICE_B 0x46
+#define POST_SOFTWARE_RESET_DEVICE_C 0x47
+#define POST_SOFTWARE_RESET_DEVICE_D 0x48
+
+struct tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4
+};
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ struct tasdevice_priv *tas_priv, unsigned char *config_data,
+ unsigned int config_size, int *status)
+{
+ struct tasdevice_config_info *cfg_info;
+ struct tasdev_blk_data **bk_da;
+ unsigned int config_offset = 0;
+ unsigned int i;
+
+ /* In most projects are many audio cases, such as music, handfree,
+ * receiver, games, audio-to-haptics, PMIC record, bypass mode,
+ * portrait, landscape, etc. Even in multiple audios, one or
+ * two of the chips will work for the special case, such as
+ * ultrasonic application. In order to support these variable-numbers
+ * of audio cases, flexible configs have been introduced in the
+ * dsp firmware.
+ */
+ cfg_info = kzalloc(sizeof(struct tasdevice_config_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ goto out;
+ }
+
+ if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add conf: Out of boundary\n");
+ goto out;
+ }
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add config: Out of boundary\n");
+ goto out;
+ }
+
+ /* convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ cfg_info->nblocks =
+ be32_to_cpup((__be32 *)&config_data[config_offset]);
+ config_offset += 4;
+
+ /* Several kinds of dsp/algorithm firmwares can run on tas2781,
+ * the number and size of blk are not fixed and different among
+ * these firmwares.
+ */
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(struct tasdev_blk_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(tas_priv->dev,
+ "%s: Out of boundary: i = %d nblocks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ bk_da[i] = kzalloc(sizeof(struct tasdev_blk_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 == TASDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (bk_da[i]->dev_idx == 0)
+ cfg_info->active_dev =
+ (1 << tas_priv->ndev) - 1;
+ else
+ cfg_info->active_dev |= 1 <<
+ (bk_da[i]->dev_idx - 1);
+
+ }
+ bk_da[i]->yram_checksum =
+ be16_to_cpup((__be16 *)&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ be32_to_cpup((__be32 *)&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ be32_to_cpup((__be32 *)&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d blks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ /* instead of kzalloc+memcpy */
+ 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;
+}
+
+int tasdevice_rca_parser(void *context, const struct firmware *fmw)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_config_info **cfg_info;
+ struct tasdevice_rca_hdr *fw_hdr;
+ struct tasdevice_rca *rca;
+ unsigned int total_config_sz = 0;
+ unsigned char *buf;
+ int offset = 0;
+ int ret = 0;
+ int i;
+
+ rca = &(tas_priv->rcabin);
+ fw_hdr = &(rca->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "Failed to read %s\n",
+ tas_priv->rca_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(tas_priv->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_priv->dev, "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = be32_to_cpup((__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 != tas_priv->ndev) {
+ dev_err(tas_priv->dev,
+ "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n",
+ fw_hdr->ndev, tas_priv->ndev);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ ret = -EINVAL;
+ goto out;
+ }
+ if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+ dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n");
+ ret = -EINVAL;
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = be32_to_cpup((__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(tas_priv->dev, "Bin file error!\n");
+ ret = -EINVAL;
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ ret = -ENOMEM;
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ rca->ncfgs += 1;
+ cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ }
+out:
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, SND_SOC_TAS2781_FMWLIB);
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 16 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ /* convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ block->type = be32_to_cpup((__be32 *)&data[offset]);
+ offset += 4;
+
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ block->pchksum = data[offset];
+ offset++;
+
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ block->ychksum = data[offset];
+ offset++;
+
+ block->blk_size = be32_to_cpup((__be32 *)&data[offset]);
+ offset += 4;
+
+ block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]);
+ offset += 4;
+
+ if (offset + block->blk_size > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL);
+ if (!block->data) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ offset += block->blk_size;
+
+out:
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]);
+ offset += 4;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(struct tasdev_blk), GFP_KERNEL);
+ if (!img_data->dev_blks) {
+ offset = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &(img_data->dev_blks[i]);
+ offset = fw_parse_block_data_kernel(tas_fmw, blk, fmw, offset);
+ if (offset < 0) {
+ offset = -EINVAL;
+ break;
+ }
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ if (offset + 72 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ /*skip 72 unused byts*/
+ offset += 72;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data_kernel(
+ struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 80 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ /*skip extra 16 bytes*/
+ offset += 80;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_variable_header_kernel(
+ struct tasdevice_priv *tas_priv, const struct firmware *fmw,
+ int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_prog *program;
+ struct tasdevice_config *config;
+ const unsigned char *buf = fmw->data;
+ unsigned short max_confs;
+ unsigned int i;
+
+ if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s:not TAS device\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+ if (fw_hdr->ndev != tas_priv->ndev) {
+ dev_err(tas_priv->dev,
+ "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_priv->ndev);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+
+ if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs >
+ TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_priv->dev, "mnPrograms is invalid\n");
+ offset = -EINVAL;
+ goto out;
+ }
+
+ tas_fmw->programs = kcalloc(tas_fmw->nr_programs,
+ sizeof(struct tasdevice_prog), GFP_KERNEL);
+ if (!tas_fmw->programs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ program->prog_size = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused prog_size */
+ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs);
+
+ tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+
+ /* The max number of config in firmware greater than 4 pieces of
+ * tas2781s is different from the one lower than 4 pieces of
+ * tas2781s.
+ */
+ max_confs = (fw_hdr->ndev >= 4) ?
+ TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS :
+ TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->nr_configurations == 0 ||
+ tas_fmw->nr_configurations > max_confs) {
+ dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (!tas_fmw->configs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ config = &(tas_fmw->configs[i]);
+ config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused configs */
+ offset += 4 * (max_confs - tas_fmw->nr_programs);
+
+out:
+ return offset;
+}
+
+static int tasdevice_process_block(void *context, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+ int subblk_offset, chn, chnend, rc;
+ unsigned char subblk_typ = data[1];
+ int blktyp = dev_idx & 0xC0;
+ int idx = dev_idx & 0x3F;
+ bool is_err = false;
+
+ if (idx) {
+ chn = idx - 1;
+ chnend = idx;
+ } else {
+ chn = 0;
+ chnend = tas_priv->ndev;
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_priv->tasdevice[chn].is_loading == false)
+ continue;
+
+ is_err = false;
+ subblk_offset = 2;
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W: {
+ int i;
+ unsigned short len = be16_to_cpup((__be16 *)&data[2]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_priv->dev,
+ "process_block: Out of boundary\n");
+ is_err = true;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_priv->dev,
+ "process_block: single write error\n");
+ }
+ subblk_offset += 4;
+ }
+ }
+ break;
+ case TASDEVICE_CMD_BURST: {
+ unsigned short len = be16_to_cpup((__be16 *)&data[2]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_priv->dev,
+ "%s: BST Out of boundary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ if (len % 4) {
+ dev_err(tas_priv->dev,
+ "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ break;
+ }
+
+ rc = tasdevice_dev_bulk_write(tas_priv, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ &(data[subblk_offset + 4]), len);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_priv->dev,
+ "%s: bulk_write error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += (len + 4);
+ }
+ break;
+ case TASDEVICE_CMD_DELAY: {
+ unsigned int sleep_time = 0;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_priv->dev,
+ "%s: delay Out of boundary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000;
+ usleep_range(sleep_time, sleep_time + 50);
+ subblk_offset += 2;
+ }
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_priv->dev,
+ "%s: bit write Out of boundary\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ rc = tasdevice_dev_update_bits(tas_priv, chn,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1],
+ data[subblk_offset + 5]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_priv->dev,
+ "%s: update_bits error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ }
+ if (is_err == true && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_priv->tasdevice[chn].cur_prog = -1;
+ tas_priv->tasdevice[chn].cur_conf = -1;
+ } else
+ tas_priv->tasdevice[chn].cur_conf = -1;
+ }
+ }
+
+ return subblk_offset;
+}
+
+void tasdevice_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdev_blk_data **blk_data;
+ int j, k, chn, chnend;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) {
+ dev_err(tas_priv->dev, "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ return;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(tas_priv->dev,
+ "block_type should be in range from 2 to 5\n");
+ break;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < (int)blk_data[j]->n_subblks; k++) {
+ if (blk_data[j]->dev_idx) {
+ chn = blk_data[j]->dev_idx - 1;
+ chnend = blk_data[j]->dev_idx;
+ } else {
+ chn = 0;
+ chnend = tas_priv->ndev;
+ }
+ for (; chn < chnend; chn++)
+ tas_priv->tasdevice[chn].is_loading = true;
+
+ rc = tasdevice_process_block(tas_priv,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += rc;
+ if (blk_data[j]->block_size < length) {
+ dev_err(tas_priv->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(tas_priv->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, SND_SOC_TAS2781_FMWLIB);
+
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ const unsigned int blk_size = block->blk_size;
+ unsigned int i, length;
+ unsigned char *data = block->data;
+ unsigned char dev_idx = 0;
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES_1X:
+ dev_idx = 0x80;
+ break;
+ case MAIN_DEVICE_A_1X:
+ dev_idx = 0x81;
+ break;
+ case COEFF_DEVICE_A_1X:
+ case PRE_DEVICE_A_1X:
+ case PRE_SOFTWARE_RESET_DEVICE_A:
+ case POST_SOFTWARE_RESET_DEVICE_A:
+ dev_idx = 0xC1;
+ break;
+ case MAIN_DEVICE_B_1X:
+ dev_idx = 0x82;
+ break;
+ case COEFF_DEVICE_B_1X:
+ case PRE_DEVICE_B_1X:
+ case PRE_SOFTWARE_RESET_DEVICE_B:
+ case POST_SOFTWARE_RESET_DEVICE_B:
+ dev_idx = 0xC2;
+ break;
+ case MAIN_DEVICE_C_1X:
+ dev_idx = 0x83;
+ break;
+ case COEFF_DEVICE_C_1X:
+ case PRE_DEVICE_C_1X:
+ case PRE_SOFTWARE_RESET_DEVICE_C:
+ case POST_SOFTWARE_RESET_DEVICE_C:
+ dev_idx = 0xC3;
+ break;
+ case MAIN_DEVICE_D_1X:
+ dev_idx = 0x84;
+ break;
+ case COEFF_DEVICE_D_1X:
+ case PRE_DEVICE_D_1X:
+ case PRE_SOFTWARE_RESET_DEVICE_D:
+ case POST_SOFTWARE_RESET_DEVICE_D:
+ dev_idx = 0xC4;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ } else if (fw_fixed_hdr->ppcver >=
+ PPC3_VERSION) {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES_1X:
+ dev_idx = 0x80;
+ break;
+ case MAIN_DEVICE_A_1X:
+ dev_idx = 0x81;
+ break;
+ case COEFF_DEVICE_A_1X:
+ case PRE_DEVICE_A_1X:
+ dev_idx = 0xC1;
+ break;
+ case MAIN_DEVICE_B_1X:
+ dev_idx = 0x82;
+ break;
+ case COEFF_DEVICE_B_1X:
+ case PRE_DEVICE_B_1X:
+ dev_idx = 0xC2;
+ break;
+ case MAIN_DEVICE_C_1X:
+ dev_idx = 0x83;
+ break;
+ case COEFF_DEVICE_C_1X:
+ case PRE_DEVICE_C_1X:
+ dev_idx = 0xC3;
+ break;
+ case MAIN_DEVICE_D_1X:
+ dev_idx = 0x84;
+ break;
+ case COEFF_DEVICE_D_1X:
+ case PRE_DEVICE_D_1X:
+ dev_idx = 0xC4;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ } else {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A:
+ dev_idx = 0x81;
+ break;
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ dev_idx = 0xC1;
+ break;
+ case MAIN_DEVICE_B:
+ dev_idx = 0x82;
+ break;
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ dev_idx = 0xC2;
+ break;
+ case MAIN_DEVICE_C:
+ dev_idx = 0x83;
+ break;
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ dev_idx = 0xC3;
+ break;
+ case MAIN_DEVICE_D:
+ dev_idx = 0x84;
+ break;
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ dev_idx = 0xC4;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ }
+
+ for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: %u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += (unsigned int)rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev, "%s: %u %u out of boundary\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int fw_parse_variable_hdr(struct tasdevice_priv
+ *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr,
+ const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ int len = strlen((char *)&buf[offset]);
+
+ len++;
+
+ if (offset + len + 8 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+
+ offset += len;
+
+ fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s: not TAS device\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+
+ fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+out:
+ return offset;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv
+ *tas_priv, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+ offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+ if (offset < 0)
+ goto out;
+ if (fw_hdr->ndev != tas_priv->ndev) {
+ dev_err(tas_priv->dev,
+ "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_priv->ndev);
+ offset = -EINVAL;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Type error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ block->type = be32_to_cpup((__be32 *)&data[offset]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) {
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "PChkSumPresent error\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ block->pchksum = data[offset];
+ offset++;
+
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+
+ block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]);
+ offset += 4;
+
+ n = block->nr_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ offset = -EINVAL;
+ goto out;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (!block->data) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ offset += n;
+
+out:
+ return offset;
+}
+
+/* When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i;
+ int n;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Name error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += n;
+ img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]);
+ offset += 2;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(struct tasdev_blk), GFP_KERNEL);
+ if (!img_data->dev_blks) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &(img_data->dev_blks[i]);
+ offset = fw_parse_block_data(tas_fmw, blk, fmw, offset);
+ if (offset < 0) {
+ offset = -EINVAL;
+ goto out;
+ }
+ }
+
+out:
+ return offset;
+}
+
+/* When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *buf = (unsigned char *)fmw->data;
+ struct tasdevice_prog *program;
+ int i;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_programs == 0) {
+ /*Not error in calibration Data file, return directly*/
+ dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n",
+ __func__);
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog),
+ GFP_KERNEL);
+ if (!tas_fmw->programs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ int n = 0;
+
+ program = &(tas_fmw->programs[i]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ /* skip '\0' and 5 unused bytes */
+ n += 6;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+/* When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_configuration_data(
+ struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+ int n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_configurations == 0) {
+ dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__);
+ /*Not error for calibration Data file, return directly*/
+ goto out;
+ }
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (!tas_fmw->configs) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "File Size err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n += 15;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+static bool check_inpage_yram_rg(struct tas_crc *cd,
+ unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+
+ if (reg <= TAS2781_YRAM5_END_REG &&
+ reg >= TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_END_REG)
+ cd->len = TAS2781_YRAM5_END_REG - reg + 1;
+ else
+ cd->len = len;
+ cd->offset = reg;
+ in = true;
+ } else if (reg < TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_START_REG) {
+ cd->offset = TAS2781_YRAM5_START_REG;
+ cd->len = len - TAS2781_YRAM5_START_REG + reg;
+ in = true;
+ }
+ }
+
+ return in;
+}
+
+static bool check_inpage_yram_bk1(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg + len > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len = len - TAS2781_YRAM1_START_REG + reg;
+ in = true;
+ }
+ } else if (page == TAS2781_YRAM3_PAGE)
+ in = check_inpage_yram_rg(cd, reg, len);
+
+ return in;
+}
+
+/* Return Code:
+ * true -- the registers are in the inpage yram
+ * false -- the registers are NOT in the inpage yram
+ */
+static bool check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ in = check_inpage_yram_bk1(cd, page, reg, len);
+ goto end;
+ }
+ if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE)
+ in = check_inpage_yram_rg(cd, reg, len);
+
+end:
+ return in;
+}
+
+static bool check_inblock_yram_bk(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if ((page >= TAS2781_YRAM4_START_PAGE &&
+ page <= TAS2781_YRAM4_END_PAGE) ||
+ (page >= TAS2781_YRAM2_START_PAGE &&
+ page <= TAS2781_YRAM2_END_PAGE)) {
+ if (reg <= TAS2781_YRAM2_END_REG &&
+ reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg < TAS2781_YRAM2_START_REG) {
+ if (reg + len - 1 >= TAS2781_YRAM2_START_REG) {
+ cd->offset = TAS2781_YRAM2_START_REG;
+ cd->len = reg + len - TAS2781_YRAM2_START_REG;
+ in = true;
+ }
+ }
+ }
+
+ return in;
+}
+
+/* Return Code:
+ * true -- the registers are in the inblock yram
+ * false -- the registers are NOT in the inblock yram
+ */
+static bool check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2)
+ in = check_inblock_yram_bk(cd, page, reg, len);
+
+ return in;
+}
+
+static bool check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in;
+
+ in = check_inpage_yram(cd, book, page, reg, len);
+ if (in)
+ goto end;
+ in = check_inblock_yram(cd, book, page, reg, len);
+
+end:
+ return in;
+}
+
+static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
+ unsigned short chn, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ struct tas_crc crc_data;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128];
+ int ret = 0;
+ int i;
+ bool in;
+
+ if ((reg + len - 1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ in = check_yram(&crc_data, book, page, reg, len);
+ if (!in)
+ goto end;
+
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = tasdevice_dev_bulk_read(tasdevice, chn,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ <= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)
+ + 4)))
+ /*DSP swap command, bypass */
+ continue;
+ else
+ crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i],
+ 1, 0);
+ }
+
+ ret = crc_chksum;
+
+end:
+ return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ unsigned short chl, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ struct tas_crc crc_data;
+ unsigned int nData1;
+ int ret = 0;
+ bool in;
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ in = check_yram(&crc_data, book, page, reg, 1);
+ if (!in)
+ goto end;
+ ret = tasdevice_dev_read(tasdevice, chl,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ goto end;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg, val, nData1);
+ tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK;
+ ret = -EAGAIN;
+ goto end;
+ }
+
+ ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0);
+
+end:
+ return ret;
+}
+
+static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev)
+{
+ if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A)
+ || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C)
+ || (type == MAIN_DEVICE_D))
+ dev->cur_prog = -1;
+ else
+ dev->cur_conf = -1;
+}
+
+static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned int len,
+ unsigned char val, unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1)
+ ret = tasdev_multibytes_chksum(tas_priv, chn, book, page, reg,
+ len);
+ else
+ ret = do_singlereg_checksum(tas_priv, chn, book, page, reg,
+ val);
+
+ if (ret > 0) {
+ *crc_chksum += (unsigned char)ret;
+ goto end;
+ }
+
+ if (ret != -EAGAIN)
+ goto end;
+
+ block->nr_retry--;
+ if (block->nr_retry > 0)
+ goto end;
+
+ set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]);
+
+end:
+ return ret;
+}
+
+static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char *data,
+ unsigned int len, unsigned int *nr_cmds,
+ unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_write(tas_priv, chn,
+ TASDEVICE_REG(book, page, reg), data + 3, len);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block, chn,
+ book, page, reg, len, 0, crc_chksum);
+ } else {
+ ret = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_REG(book, page, reg), data[3]);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block, chn, book,
+ page, reg, 1, data[3], crc_chksum);
+ }
+
+ if (!block->is_ychksum_present || ret >= 0) {
+ *nr_cmds += 1;
+ if (len >= 2)
+ *nr_cmds += ((len - 2) / 4) + 1;
+ }
+
+end:
+ return ret;
+}
+
+static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn)
+{
+ unsigned int nr_value;
+ int ret;
+
+ ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum,
+ &nr_value);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn);
+ set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]);
+ goto end;
+ }
+
+ if ((nr_value & 0xff) != block->pchksum) {
+ dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__,
+ chn);
+ dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n",
+ block->pchksum, (nr_value & 0xff));
+ tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK;
+ ret = -EAGAIN;
+ block->nr_retry--;
+
+ if (block->nr_retry <= 0)
+ set_err_prg_cfg(block->type,
+ &tas_priv->tasdevice[chn]);
+ } else
+ tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK;
+
+end:
+ return ret;
+}
+
+static int tasdev_load_blk(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, int chn)
+{
+ unsigned int sleep_time;
+ unsigned int len;
+ unsigned int nr_cmds;
+ unsigned char *data = block->data;
+ unsigned char crc_chksum = 0;
+ unsigned char offset;
+ unsigned char book;
+ unsigned char page;
+ unsigned char val;
+ int ret = 0;
+
+ while (block->nr_retry > 0) {
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_I2CChecksum, 0);
+ if (ret < 0)
+ break;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ nr_cmds = 0;
+
+ while (nr_cmds < block->nr_cmds) {
+ data = block->data + nr_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ nr_cmds++;
+ /*Single byte write*/
+ if (offset <= 0x7F) {
+ ret = tasdevice_dev_write(tas_priv, chn,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present) {
+ ret = tasdev_bytes_chksum(tas_priv,
+ block, chn, book, page, offset,
+ 1, val, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ continue;
+ }
+ /*sleep command*/
+ if (offset == 0x81) {
+ /*book -- data[0] page -- data[1]*/
+ sleep_time = ((book << 8) + page)*1000;
+ usleep_range(sleep_time, sleep_time + 50);
+ continue;
+ }
+ /*Multiple bytes write*/
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ ret = tasdev_multibytes_wr(tas_priv,
+ block, chn, book, page, offset, data,
+ len, &nr_cmds, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ }
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) /*err in current device, skip it*/
+ break;
+
+ if (block->is_pchksum_present) {
+ ret = tasdev_block_chksum(tas_priv, block, chn);
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) /*err in current device, skip it*/
+ break;
+ }
+
+ if (block->is_ychksum_present) {
+ /* TBD, open it when FW ready */
+ dev_err(tas_priv->dev,
+ "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_priv->tasdevice[chn].err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ }
+ /*skip current blk*/
+ break;
+ }
+
+end:
+ return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ int chnend = 0;
+ int ret = 0;
+ int chn = 0;
+ int rc = 0;
+
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ chn = 0;
+ chnend = tas_priv->ndev;
+ break;
+ case MAIN_DEVICE_A:
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ chn = 0;
+ chnend = 1;
+ break;
+ case MAIN_DEVICE_B:
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ chn = 1;
+ chnend = 2;
+ break;
+ case MAIN_DEVICE_C:
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ chn = 2;
+ chnend = 3;
+ break;
+ case MAIN_DEVICE_D:
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ chn = 3;
+ chnend = 4;
+ break;
+ default:
+ dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n",
+ block->type);
+ break;
+ }
+
+ for (; chn < chnend; chn++) {
+ block->nr_retry = 6;
+ if (tas_priv->tasdevice[chn].is_loading == false)
+ continue;
+ ret = tasdev_load_blk(tas_priv, block, chn);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n",
+ chn, block->type);
+ rc |= ret;
+ }
+
+ return rc;
+}
+
+static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
+ unsigned int drv_ver, unsigned int ppcver)
+{
+ int rc = 0;
+
+ if (drv_ver == 0x100) {
+ if (ppcver >= PPC3_VERSION) {
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ } else {
+ switch (ppcver) {
+ case 0x00:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_priv->dev,
+ "%s: PPCVer must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION);
+ dev_err(tas_priv->dev, " Current:0x%02x\n",
+ ppcver);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_priv->dev,
+ "DrvVer must be 0x0, 0x230 or above 0x230 ");
+ dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int load_calib_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &(dev_data->dev_blks[i]);
+ ret = tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 };
+ const unsigned char *buf = (unsigned char *)fmw->data;
+
+ if (offset + 92 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+
+ /* Convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 4;
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_priv->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 8;
+ fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]);
+ offset += 72;
+
+ out:
+ return offset;
+}
+
+static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+ offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+ if (offset < 0)
+ goto out;
+ if (fw_hdr->ndev != 1) {
+ dev_err(tas_priv->dev,
+ "%s: calbin must be 1, but currently ndev(%u)\n",
+ __func__, fw_hdr->ndev);
+ offset = -EINVAL;
+ }
+
+out:
+ return offset;
+}
+
+/* When calibrated data parsing error occurs, DSP can still work with default
+ * calibrated data, memory resource related to calibrated data will be
+ * released in the tasdevice_codec_remove.
+ */
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_calibration *calibration;
+ unsigned char *data = (unsigned char *)fmw->data;
+ unsigned int i, n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_calibrations != 1) {
+ dev_err(tas_priv->dev,
+ "%s: only supports one calibration (%d)!\n",
+ __func__, tas_fmw->nr_calibrations);
+ goto out;
+ }
+
+ tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations,
+ sizeof(struct tasdevice_calibration), GFP_KERNEL);
+ if (!tas_fmw->calibrations) {
+ offset = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "Calibrations error\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ calibration = &(tas_fmw->calibrations[i]);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ /* skip '\0' and 2 unused bytes */
+ n += 3;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+ return offset;
+}
+
+int tas2781_load_calibration(void *context, char *file_name,
+ unsigned short i)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+ struct tasdevice *tasdev = &(tas_priv->tasdevice[i]);
+ const struct firmware *fw_entry;
+ struct tasdevice_fw *tas_fmw;
+ struct firmware fmw;
+ int offset = 0;
+ int ret;
+
+ ret = request_firmware(&fw_entry, file_name, tas_priv->dev);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: Request firmware %s failed\n",
+ __func__, file_name);
+ goto out;
+ }
+
+ if (!fw_entry->size) {
+ dev_err(tas_priv->dev, "%s: file read error: size = %lu\n",
+ __func__, (unsigned long)fw_entry->size);
+ ret = -EINVAL;
+ goto out;
+ }
+ fmw.size = fw_entry->size;
+ fmw.data = fw_entry->data;
+
+ tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw),
+ GFP_KERNEL);
+ if (!tasdev->cali_data_fmw) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset);
+ if (offset == -EINVAL) {
+ dev_err(tas_priv->dev, "fw_parse_header EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset);
+ if (offset == -EINVAL) {
+ dev_err(tas_priv->dev,
+ "%s: fw_parse_variable_header_cal EXIT!\n", __func__);
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset);
+ if (offset < 0) {
+ dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset);
+ if (offset < 0) {
+ dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+ offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset);
+ if (offset < 0) {
+ dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n");
+ ret = offset;
+ goto out;
+ }
+
+out:
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, SND_SOC_TAS2781_FMWLIB);
+
+static int tasdevice_dspfw_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr;
+ struct tasdevice_fw *tas_fmw;
+ int offset = 0;
+ int ret = 0;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_priv->coef_binaryname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (!tas_priv->fmw) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tas_fmw = tas_priv->fmw;
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset);
+
+ if (offset == -EINVAL) {
+ ret = -EINVAL;
+ goto out;
+ }
+ fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+ /* Support different versions of firmware */
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ ret = dspfw_default_callback(tas_priv,
+ fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver);
+ if (ret)
+ goto out;
+ break;
+ }
+
+ offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset);
+ if (offset < 0) {
+ ret = offset;
+ goto out;
+ }
+ offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw,
+ offset);
+ if (offset < 0) {
+ ret = offset;
+ goto out;
+ }
+ offset = tas_priv->fw_parse_configuration_data(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ ret = offset;
+
+out:
+ return ret;
+}
+
+int tasdevice_dsp_parser(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+ const struct firmware *fw_entry;
+ int ret;
+
+ ret = request_firmware(&fw_entry, tas_priv->coef_binaryname,
+ tas_priv->dev);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: load %s error\n", __func__,
+ tas_priv->coef_binaryname);
+ goto out;
+ }
+
+ ret = tasdevice_dspfw_ready(fw_entry, tas_priv);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, SND_SOC_TAS2781_FMWLIB);
+
+static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw)
+{
+ struct tasdevice_calibration *calibration;
+ struct tasdev_blk *block;
+ struct tasdevice_data *im;
+ unsigned int blks;
+ int i;
+
+ if (!tas_fmw->calibrations)
+ goto out;
+
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ calibration = &(tas_fmw->calibrations[i]);
+ if (!calibration)
+ continue;
+
+ im = &(calibration->dev_data);
+
+ if (!im->dev_blks)
+ continue;
+
+ for (blks = 0; blks < im->nr_blk; blks++) {
+ block = &(im->dev_blks[blks]);
+ if (!block)
+ continue;
+ kfree(block->data);
+ }
+ kfree(im->dev_blks);
+ }
+ kfree(tas_fmw->calibrations);
+out:
+ kfree(tas_fmw);
+}
+
+void tasdevice_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev;
+ int i;
+
+ if (!tas_priv)
+ return;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tasdev = &(tas_priv->tasdevice[i]);
+ if (!tasdev->cali_data_fmw)
+ continue;
+ tas2781_clear_calfirmware(tasdev->cali_data_fmw);
+ tasdev->cali_data_fmw = NULL;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, SND_SOC_TAS2781_FMWLIB);
+
+void tasdevice_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **ci = rca->cfg_info;
+ int i, j;
+
+ if (!ci)
+ return;
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (!ci[i])
+ continue;
+ if (ci[i]->blk_data) {
+ for (j = 0; j < (int)ci[i]->real_nblocks; j++) {
+ if (!ci[i]->blk_data[j])
+ continue;
+ kfree(ci[i]->blk_data[j]->regdata);
+ kfree(ci[i]->blk_data[j]);
+ }
+ kfree(ci[i]->blk_data);
+ }
+ kfree(ci[i]);
+ }
+ kfree(ci);
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, SND_SOC_TAS2781_FMWLIB);
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &(dev_data->dev_blks[i]);
+ ret = tas_priv->tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *conf;
+ int prog_status = 0;
+ int status, i;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (cfg_no >= tas_fmw->nr_configurations) {
+ dev_err(tas_priv->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->nr_configurations);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ goto out;
+ }
+
+ if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 ||
+ !cfg_info) {
+ dev_err(tas_priv->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ goto out;
+ }
+
+ conf = &(tas_fmw->configs[cfg_no]);
+ for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) {
+ if (cfg_info[rca_conf_no]->active_dev & (1 << i)) {
+ if (tas_priv->tasdevice[i].cur_prog != prm_no
+ || tas_priv->force_fwload_status) {
+ tas_priv->tasdevice[i].cur_conf = -1;
+ tas_priv->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ } else
+ tas_priv->tasdevice[i].is_loading = false;
+ tas_priv->tasdevice[i].is_loaderr = false;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_priv->tasdevice[i].is_loaderr == false
+ && tas_priv->tasdevice[i].is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_priv->tasdevice[i].cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration
+ *cal = cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(tas_priv,
+ &(cal->dev_data));
+ }
+ tas_priv->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+ }
+
+ for (i = 0, status = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].cur_conf != cfg_no
+ && (cfg_info[rca_conf_no]->active_dev & (1 << i))
+ && (tas_priv->tasdevice[i].is_loaderr == false)) {
+ status++;
+ tas_priv->tasdevice[i].is_loading = true;
+ } else
+ tas_priv->tasdevice[i].is_loading = false;
+ }
+
+ if (status) {
+ status = 0;
+ tasdevice_load_data(tas_priv, &(conf->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true) {
+ status |= 1 << (i + 4);
+ continue;
+ } else if (tas_priv->tasdevice[i].is_loaderr == false
+ && tas_priv->tasdevice[i].is_loading == true)
+ tas_priv->tasdevice[i].cur_conf = cfg_no;
+ }
+ } else
+ dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
+ __func__, cfg_no);
+
+ status |= cfg_info[rca_conf_no]->active_dev;
+
+out:
+ return prog_status;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg,
+ SND_SOC_TAS2781_FMWLIB);
+
+int tasdevice_prmg_load(void *context, int prm_no)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ int prog_status = 0;
+ int i;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ goto out;
+ }
+
+ for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].cur_prog != prm_no) {
+ tas_priv->tasdevice[i].cur_conf = -1;
+ tas_priv->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_priv->tasdevice[i].is_loaderr == false
+ && tas_priv->tasdevice[i].is_loading == true)
+ tas_priv->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+
+out:
+ return prog_status;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, SND_SOC_TAS2781_FMWLIB);
+
+int tasdevice_prmg_calibdata_load(void *context, int prm_no)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ int prog_status = 0;
+ int i;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ goto out;
+ }
+
+ for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].cur_prog != prm_no) {
+ tas_priv->tasdevice[i].cur_conf = -1;
+ tas_priv->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ tas_priv->tasdevice[i].is_loaderr = false;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_priv->tasdevice[i].is_loaderr == false
+ && tas_priv->tasdevice[i].is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_priv->tasdevice[i].cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration *cal =
+ cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(tas_priv,
+ &(cal->dev_data));
+ }
+ tas_priv->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+ }
+
+out:
+ return prog_status;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_calibdata_load,
+ SND_SOC_TAS2781_FMWLIB);
+
+void tasdevice_tuning_switch(void *context, int state)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ int profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
+ dev_err(tas_priv->dev, "DSP bin file not loaded\n");
+ return;
+ }
+
+ if (state == 0) {
+ if (tas_priv->cur_prog < tas_fmw->nr_programs) {
+ /*dsp mode or tuning mode*/
+ profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+ tasdevice_select_tuningprm_cfg(tas_priv,
+ tas_priv->cur_prog, tas_priv->cur_conf,
+ profile_cfg_id);
+ }
+
+ tasdevice_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+ } else
+ tasdevice_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch,
+ SND_SOC_TAS2781_FMWLIB);
+
+MODULE_DESCRIPTION("Texas Firmware Support");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000000..4c59429a42b7
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,763 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2781", TAS2781 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2781" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tasdevice_of_match);
+#endif
+
+/**
+ * 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.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ 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 snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ tas_priv->force_fwload_status ? "ON" : "OFF");
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(component);
+ 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__,
+ tas_priv->force_fwload_status ? "ON" : "OFF");
+
+ return change;
+}
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+ SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+
+ if (tas_priv->rcabin.profile_cfg_id !=
+ ucontrol->value.integer.value[0]) {
+ tas_priv->rcabin.profile_cfg_id =
+ ucontrol->value.integer.value[0];
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ 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 = (int)tas_fw->nr_programs;
+
+ return 0;
+}
+
+static int tasdevice_info_configurations(
+ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ 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 = (int)tas_fw->nr_configurations - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = 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 = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ return 0;
+}
+
+static int tasdevice_create_control(struct tasdevice_priv *tas_priv)
+{
+ struct snd_kcontrol_new *prof_ctrls;
+ int nr_controls = 1;
+ int mix_index = 0;
+ int ret;
+ char *name;
+
+ prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls,
+ sizeof(prof_ctrls[0]), GFP_KERNEL);
+ if (!prof_ctrls) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Speaker Profile Id");
+ prof_ctrls[mix_index].name = name;
+ prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ prof_ctrls[mix_index].info = tasdevice_info_profile;
+ prof_ctrls[mix_index].get = tasdevice_get_profile_id;
+ prof_ctrls[mix_index].put = tasdevice_set_profile_id;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_priv->codec,
+ prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index);
+
+out:
+ return ret;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ unsigned int nr_program = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (tas_priv->cur_prog != nr_program) {
+ tas_priv->cur_prog = nr_program;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_configuration_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ return 0;
+}
+
+static int tasdevice_configuration_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ unsigned int nr_configuration = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (tas_priv->cur_conf != nr_configuration) {
+ tas_priv->cur_conf = nr_configuration;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_dsp_create_ctrls(
+ struct tasdevice_priv *tas_priv)
+{
+ struct snd_kcontrol_new *dsp_ctrls;
+ char *prog_name, *conf_name;
+ int nr_controls = 2;
+ int mix_index = 0;
+ int ret;
+
+ /* Alloc kcontrol via devm_kzalloc, which don't manually
+ * free the kcontrol
+ */
+ dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls,
+ sizeof(dsp_ctrls[0]), GFP_KERNEL);
+ if (!dsp_ctrls) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ prog_name = devm_kzalloc(tas_priv->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ GFP_KERNEL);
+ if (!prog_name || !conf_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ scnprintf(prog_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "Speaker Program Id");
+ dsp_ctrls[mix_index].name = prog_name;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_programs;
+ dsp_ctrls[mix_index].get = tasdevice_program_get;
+ dsp_ctrls[mix_index].put = tasdevice_program_put;
+ mix_index++;
+
+ scnprintf(conf_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "Speaker Config Id");
+ dsp_ctrls[mix_index].name = conf_name;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_configurations;
+ dsp_ctrls[mix_index].get = tasdevice_configuration_get;
+ dsp_ctrls[mix_index].put = tasdevice_configuration_put;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+
+out:
+ return ret;
+}
+
+static void tasdevice_fw_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+ tasdevice_create_control(tas_priv);
+
+ tasdevice_dsp_remove(tas_priv);
+ tasdevice_calbin_remove(tas_priv);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin",
+ tas_priv->dev_name);
+ 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;
+ }
+ tasdevice_dsp_create_ctrls(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+
+ /* If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ for (i = 0; i < tas_priv->ndev; i++) {
+ scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_priv,
+ tas_priv->cal_binaryname[i], i);
+ if (ret != 0)
+ dev_err(tas_priv->dev,
+ "%s: load %s error, default will effect\n",
+ __func__, tas_priv->cal_binaryname[i]);
+ }
+
+ tasdevice_prmg_calibdata_load(tas_priv, 0);
+ tas_priv->cur_prog = 0;
+out:
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
+ /*If DSP FW fail, kcontrol won't be created */
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+ }
+ mutex_unlock(&tas_priv->codec_lock);
+ if (fmw)
+ release_firmware(fmw);
+}
+
+static int tasdevice_dapm_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 tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ int state = 0;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_priv->codec_lock);
+ if (event == SND_SOC_DAPM_PRE_PMD)
+ state = 1;
+ tasdevice_tuning_switch(tas_priv, state);
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_priv->codec_lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0, tasdevice_dapm_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC")
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"SPK", NULL, "ASI"},
+ {"OUT", NULL, "SPK"},
+ {"ASI OUT", NULL, "DMIC"}
+};
+
+static int tasdevice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+
+ if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) {
+ dev_err(tas_priv->dev, "DSP bin file not loaded\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int tasdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int slot_width;
+ unsigned int fsrate;
+ int bclk_rate;
+ int rc = 0;
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ case 44100:
+ break;
+ default:
+ dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ case 20:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ rc = bclk_rate;
+ goto out;
+ }
+
+out:
+ return rc;
+}
+
+static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(codec_dai);
+
+ tas_priv->sysclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tasdevice_dai_ops = {
+ .startup = tasdevice_startup,
+ .hw_params = tasdevice_hw_params,
+ .set_sysclk = tasdevice_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
+ {
+ .name = "tas2781_codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .ops = &tasdevice_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int tasdevice_codec_probe(struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ return tascodec_init(tas_priv, codec, tasdevice_fw_ready);
+}
+
+static void tasdevice_deinit(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+ tasdevice_calbin_remove(tas_priv);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static void tasdevice_codec_remove(
+ struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+ tasdevice_deinit(tas_priv);
+}
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_tasdevice = {
+ .probe = tasdevice_codec_probe,
+ .remove = tasdevice_codec_remove,
+ .controls = tas2781_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2781_snd_controls),
+ .dapm_widgets = tasdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets),
+ .dapm_routes = tasdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS];
+ int rc, i, ndev = 0;
+
+ if (tas_priv->isacpi) {
+ ndev = device_property_read_u32_array(&client->dev,
+ "ti,audio-slots", NULL, 0);
+ if (ndev <= 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ } else {
+ ndev = (ndev < ARRAY_SIZE(dev_addrs))
+ ? ndev : ARRAY_SIZE(dev_addrs);
+ ndev = device_property_read_u32_array(&client->dev,
+ "ti,audio-slots", dev_addrs, ndev);
+ }
+
+ tas_priv->irq_info.irq_gpio =
+ acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0);
+ } else {
+ struct device_node *np = tas_priv->dev->of_node;
+#ifdef CONFIG_OF
+ const __be32 *reg, *reg_end;
+ int len, sw, aw;
+
+ aw = of_n_addr_cells(np);
+ sw = of_n_size_cells(np);
+ if (sw == 0) {
+ reg = (const __be32 *)of_get_property(np,
+ "reg", &len);
+ reg_end = reg + len/sizeof(*reg);
+ ndev = 0;
+ do {
+ dev_addrs[ndev] = of_read_number(reg, aw);
+ reg += aw;
+ ndev++;
+ } while (reg < reg_end);
+ } else {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+#else
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+#endif
+ tas_priv->irq_info.irq_gpio = of_irq_get(np, 0);
+ }
+ tas_priv->ndev = ndev;
+ for (i = 0; i < ndev; i++)
+ tas_priv->tasdevice[i].dev_addr = dev_addrs[i];
+
+ tas_priv->reset = devm_gpiod_get_optional(&client->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas_priv->reset))
+ dev_err(tas_priv->dev, "%s Can't get reset GPIO\n",
+ __func__);
+
+ strcpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name);
+
+ if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) {
+ rc = gpio_request(tas_priv->irq_info.irq_gpio,
+ "AUDEV-IRQ");
+ if (!rc) {
+ gpio_direction_input(
+ tas_priv->irq_info.irq_gpio);
+
+ tas_priv->irq_info.irq =
+ gpio_to_irq(tas_priv->irq_info.irq_gpio);
+ } else
+ dev_err(tas_priv->dev, "%s: GPIO %d request error\n",
+ __func__, tas_priv->irq_info.irq_gpio);
+ } else
+ dev_err(tas_priv->dev,
+ "Looking up irq-gpio property failed %d\n",
+ tas_priv->irq_info.irq_gpio);
+}
+
+static int tasdevice_i2c_probe(struct i2c_client *i2c)
+{
+ const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c);
+ const struct acpi_device_id *acpi_id;
+ struct tasdevice_priv *tas_priv;
+ int ret;
+
+ tas_priv = tasdevice_kzalloc(i2c);
+ if (!tas_priv)
+ 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");
+ ret = -EINVAL;
+ goto err;
+ }
+ tas_priv->chip_id = acpi_id->driver_data;
+ tas_priv->isacpi = true;
+ } else {
+ tas_priv->chip_id = id ? id->driver_data : 0;
+ tas_priv->isacpi = false;
+ }
+
+ tasdevice_parse_dt(tas_priv);
+
+ ret = tasdevice_init(tas_priv);
+ if (ret)
+ goto err;
+
+ ret = devm_snd_soc_register_component(tas_priv->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: codec register error:0x%08x\n",
+ __func__, ret);
+ goto err;
+ }
+err:
+ if (ret < 0)
+ tasdevice_remove(tas_priv);
+ return ret;
+}
+
+static void tasdevice_i2c_remove(struct i2c_client *client)
+{
+ struct tasdevice_priv *tas_priv = i2c_get_clientdata(client);
+
+ tasdevice_remove(tas_priv);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id tasdevice_acpi_match[] = {
+ { "TAS2781", TAS2781 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match);
+#endif
+
+static struct i2c_driver tasdevice_i2c_driver = {
+ .driver = {
+ .name = "tas2781-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tasdevice_of_match),
+#ifdef CONFIG_ACPI
+ .acpi_match_table = ACPI_PTR(tasdevice_acpi_match),
+#endif
+ },
+ .probe = tasdevice_i2c_probe,
+ .remove = tasdevice_i2c_remove,
+ .id_table = tasdevice_id,
+};
+
+module_i2c_driver(tasdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>");
+MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>");
+MODULE_DESCRIPTION("ASoC TAS2781 Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB);
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index f9e7122894bd..60e59e993ba6 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -990,7 +990,7 @@ static struct i2c_driver tas5086_i2c_driver = {
.of_match_table = of_match_ptr(tas5086_dt_ids),
},
.id_table = tas5086_i2c_id,
- .probe_new = tas5086_i2c_probe,
+ .probe = tas5086_i2c_probe,
.remove = tas5086_i2c_remove,
};
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index f39c3273b2fd..1756edb35961 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -975,7 +975,7 @@ static struct i2c_driver tas571x_i2c_driver = {
.name = "tas571x",
.of_match_table = of_match_ptr(tas571x_of_match),
},
- .probe_new = tas571x_i2c_probe,
+ .probe = tas571x_i2c_probe,
.remove = tas571x_i2c_remove,
.id_table = tas571x_i2c_id,
};
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index 4d27b60bd804..6dd6c0896eff 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -11,7 +11,6 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/i2c.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
@@ -821,7 +820,7 @@ static struct i2c_driver tas5720_i2c_driver = {
.name = "tas5720",
.of_match_table = of_match_ptr(tas5720_of_match),
},
- .probe_new = tas5720_probe,
+ .probe = tas5720_probe,
.id_table = tas5720_id,
};
diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c
index 4e38eb7acea1..aca3756ffab6 100644
--- a/sound/soc/codecs/tas5805m.c
+++ b/sound/soc/codecs/tas5805m.c
@@ -597,7 +597,7 @@ MODULE_DEVICE_TABLE(of, tas5805m_of_match);
#endif
static struct i2c_driver tas5805m_i2c_driver = {
- .probe_new = tas5805m_i2c_probe,
+ .probe = tas5805m_i2c_probe,
.remove = tas5805m_i2c_remove,
.id_table = tas5805m_i2c_id,
.driver = {
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
index f8ff69fa2549..da89e8c681dd 100644
--- a/sound/soc/codecs/tas6424.c
+++ b/sound/soc/codecs/tas6424.c
@@ -11,7 +11,6 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/i2c.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
@@ -803,7 +802,7 @@ static struct i2c_driver tas6424_i2c_driver = {
.name = "tas6424",
.of_match_table = of_match_ptr(tas6424_of_ids),
},
- .probe_new = tas6424_i2c_probe,
+ .probe = tas6424_i2c_probe,
.remove = tas6424_i2c_remove,
.id_table = tas6424_i2c_ids,
};
diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c
index d964e5207569..e187d74a1737 100644
--- a/sound/soc/codecs/tda7419.c
+++ b/sound/soc/codecs/tda7419.c
@@ -629,7 +629,7 @@ static struct i2c_driver tda7419_driver = {
.name = "tda7419",
.of_match_table = tda7419_of_match,
},
- .probe_new = tda7419_probe,
+ .probe = tda7419_probe,
.id_table = tda7419_i2c_id,
};
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
index 9f7902ec40db..8cca2ceadd9e 100644
--- a/sound/soc/codecs/tfa9879.c
+++ b/sound/soc/codecs/tfa9879.c
@@ -312,7 +312,7 @@ static struct i2c_driver tfa9879_i2c_driver = {
.name = "tfa9879",
.of_match_table = tfa9879_of_match,
},
- .probe_new = tfa9879_i2c_probe,
+ .probe = tfa9879_i2c_probe,
.id_table = tfa9879_i2c_id,
};
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c
index b853507e65a8..79847a90ac46 100644
--- a/sound/soc/codecs/tfa989x.c
+++ b/sound/soc/codecs/tfa989x.c
@@ -416,7 +416,7 @@ static struct i2c_driver tfa989x_i2c_driver = {
.name = "tfa989x",
.of_match_table = tfa989x_of_match,
},
- .probe_new = tfa989x_i2c_probe,
+ .probe = tfa989x_i2c_probe,
};
module_i2c_driver(tfa989x_i2c_driver);
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
index 52bb55724724..b976c1946286 100644
--- a/sound/soc/codecs/tlv320adc3xxx.c
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -1451,7 +1451,7 @@ static struct i2c_driver adc3xxx_i2c_driver = {
.name = "tlv320adc3xxx-codec",
.of_match_table = tlv320adc3xxx_of_match,
},
- .probe_new = adc3xxx_i2c_probe,
+ .probe = adc3xxx_i2c_probe,
.remove = __exit_p(adc3xxx_i2c_remove),
.id_table = adc3xxx_i2c_id,
};
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 530f321d08e9..41342b340680 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -1208,7 +1208,7 @@ static struct i2c_driver adcx140_i2c_driver = {
.name = "tlv320adcx140-codec",
.of_match_table = of_match_ptr(tlv320adcx140_of_match),
},
- .probe_new = adcx140_i2c_probe,
+ .probe = adcx140_i2c_probe,
.id_table = adcx140_i2c_id,
};
module_i2c_driver(adcx140_i2c_driver);
diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c
index 1f97673a1cc0..9692ae007c91 100644
--- a/sound/soc/codecs/tlv320aic23-i2c.c
+++ b/sound/soc/codecs/tlv320aic23-i2c.c
@@ -47,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = {
.name = "tlv320aic23-codec",
.of_match_table = of_match_ptr(tlv320aic23_of_match),
},
- .probe_new = tlv320aic23_i2c_probe,
+ .probe = tlv320aic23_i2c_probe,
.id_table = tlv320aic23_id,
};
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 0847302121f6..9611aa8acb0d 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1746,7 +1746,7 @@ static struct i2c_driver aic31xx_i2c_driver = {
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
.acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
},
- .probe_new = aic31xx_i2c_probe,
+ .probe = aic31xx_i2c_probe,
.id_table = aic31xx_i2c_id,
};
module_i2c_driver(aic31xx_i2c_driver);
diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c
index d1e543ca3521..49b33a256cd7 100644
--- a/sound/soc/codecs/tlv320aic32x4-i2c.c
+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c
@@ -71,7 +71,7 @@ static struct i2c_driver aic32x4_i2c_driver = {
.name = "tlv320aic32x4",
.of_match_table = aic32x4_of_id,
},
- .probe_new = aic32x4_i2c_probe,
+ .probe = aic32x4_i2c_probe,
.remove = aic32x4_i2c_remove,
.id_table = aic32x4_i2c_id,
};
diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c
index d7e94d564dbf..bb33fd3dfb4f 100644
--- a/sound/soc/codecs/tlv320aic3x-i2c.c
+++ b/sound/soc/codecs/tlv320aic3x-i2c.c
@@ -61,7 +61,7 @@ static struct i2c_driver aic3x_i2c_driver = {
.name = "tlv320aic3x",
.of_match_table = aic3x_of_id,
},
- .probe_new = aic3x_i2c_probe,
+ .probe = aic3x_i2c_probe,
.remove = aic3x_i2c_remove,
.id_table = aic3x_i2c_id,
};
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 16ce3ef1134b..fa46f51d4341 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1560,7 +1560,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = {
.driver = {
.name = "tlv320dac33-codec",
},
- .probe_new = dac33_i2c_probe,
+ .probe = dac33_i2c_probe,
.remove = dac33_i2c_remove,
.id_table = tlv320dac33_i2c_id,
};
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 8c00db32996b..5bc486283fde 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -319,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
.name = "tpa6130a2",
.of_match_table = of_match_ptr(tpa6130a2_of_match),
},
- .probe_new = tpa6130a2_probe,
+ .probe = tpa6130a2_probe,
.id_table = tpa6130a2_id,
};
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 5282112c7d8d..6d9166d9116a 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -455,7 +455,7 @@ static struct i2c_driver ts3a227e_driver = {
.of_match_table = of_match_ptr(ts3a227e_of_match),
.acpi_match_table = ACPI_PTR(ts3a227e_acpi_match),
},
- .probe_new = ts3a227e_i2c_probe,
+ .probe = ts3a227e_i2c_probe,
.id_table = ts3a227e_i2c_ids,
};
module_i2c_driver(ts3a227e_driver);
diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c
index fa0c525189c2..1eefc1fe6ea8 100644
--- a/sound/soc/codecs/tscs42xx.c
+++ b/sound/soc/codecs/tscs42xx.c
@@ -1503,7 +1503,7 @@ static struct i2c_driver tscs42xx_i2c_driver = {
.name = "tscs42xx",
.of_match_table = tscs42xx_of_match,
},
- .probe_new = tscs42xx_i2c_probe,
+ .probe = tscs42xx_i2c_probe,
.id_table = tscs42xx_i2c_id,
};
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
index 38622bc247fc..744aef32a21f 100644
--- a/sound/soc/codecs/tscs454.c
+++ b/sound/soc/codecs/tscs454.c
@@ -3473,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = {
.name = "tscs454",
.of_match_table = tscs454_of_match,
},
- .probe_new = tscs454_i2c_probe,
+ .probe = tscs454_i2c_probe,
.id_table = tscs454_i2c_id,
};
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index fdaaee845176..5c5751dc14e5 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -798,7 +798,7 @@ static struct i2c_driver uda1380_i2c_driver = {
.name = "uda1380-codec",
.of_match_table = uda1380_of_match,
},
- .probe_new = uda1380_i2c_probe,
+ .probe = uda1380_i2c_probe,
.id_table = uda1380_i2c_id,
};
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index 9c10200ff34b..bd0e9fbc12eb 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -1270,7 +1270,7 @@ static int wcd9380_probe(struct sdw_slave *pdev,
/* Start in cache-only until device is enumerated */
regcache_cache_only(wcd->regmap, true);
- };
+ }
pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(dev);
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 034a4e858c7e..1d4259433f47 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -994,3 +994,6 @@ module_spi_driver(wm0010_spi_driver);
MODULE_DESCRIPTION("ASoC WM0010 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("wm0010.dfw");
+MODULE_FIRMWARE("wm0010_stage2.bin");
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index 0064a607ec68..d7eeb41ba60f 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -243,7 +243,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = {
.driver = {
.name = "wm1250-ev1",
},
- .probe_new = wm1250_ev1_probe,
+ .probe = wm1250_ev1_probe,
.remove = wm1250_ev1_remove,
.id_table = wm1250_ev1_i2c_id,
};
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 14b4fd97488c..9571ea53cb3f 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -938,7 +938,7 @@ static struct i2c_driver wm2000_i2c_driver = {
.driver = {
.name = "wm2000",
},
- .probe_new = wm2000_i2c_probe,
+ .probe = wm2000_i2c_probe,
.id_table = wm2000_i2c_id,
};
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 0a65afa44a59..277b8c468c78 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2485,7 +2485,7 @@ static struct i2c_driver wm2200_i2c_driver = {
.name = "wm2200",
.pm = &wm2200_pm,
},
- .probe_new = wm2200_i2c_probe,
+ .probe = wm2200_i2c_probe,
.remove = wm2200_i2c_remove,
.id_table = wm2200_i2c_id,
};
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 3b09d4a1684f..a86eacb2a9bb 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2709,7 +2709,7 @@ static struct i2c_driver wm5100_i2c_driver = {
.name = "wm5100",
.pm = &wm5100_pm,
},
- .probe_new = wm5100_i2c_probe,
+ .probe = wm5100_i2c_probe,
.remove = wm5100_i2c_remove,
.id_table = wm5100_i2c_id,
};
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index e13f9780a111..c0ed76d5b65f 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -678,7 +678,7 @@ static struct i2c_driver wm8510_i2c_driver = {
.name = "wm8510",
.of_match_table = wm8510_of_match,
},
- .probe_new = wm8510_i2c_probe,
+ .probe = wm8510_i2c_probe,
.id_table = wm8510_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 66f6371d8acf..55c72c5ac845 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -527,7 +527,7 @@ static struct i2c_driver wm8523_i2c_driver = {
.name = "wm8523",
.of_match_table = wm8523_of_match,
},
- .probe_new = wm8523_i2c_probe,
+ .probe = wm8523_i2c_probe,
.id_table = wm8523_i2c_id,
};
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index ca796aa0aeb7..34ae7fe05398 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -1049,7 +1049,7 @@ static struct i2c_driver wm8580_i2c_driver = {
.name = "wm8580",
.of_match_table = wm8580_of_match,
},
- .probe_new = wm8580_i2c_probe,
+ .probe = wm8580_i2c_probe,
.id_table = wm8580_i2c_id,
};
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 383c6796e8a3..903a0147d584 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -464,7 +464,7 @@ static struct i2c_driver wm8711_i2c_driver = {
.name = "wm8711",
.of_match_table = wm8711_of_match,
},
- .probe_new = wm8711_i2c_probe,
+ .probe = wm8711_i2c_probe,
.id_table = wm8711_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index a3dbdbf40723..5ea6d8fd10f6 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -305,7 +305,7 @@ static struct i2c_driver wm8728_i2c_driver = {
.name = "wm8728",
.of_match_table = wm8728_of_match,
},
- .probe_new = wm8728_i2c_probe,
+ .probe = wm8728_i2c_probe,
.id_table = wm8728_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c
index fdf03bf91606..c39e637d813d 100644
--- a/sound/soc/codecs/wm8731-i2c.c
+++ b/sound/soc/codecs/wm8731-i2c.c
@@ -57,7 +57,7 @@ static struct i2c_driver wm8731_i2c_driver = {
.name = "wm8731",
.of_match_table = wm8731_of_match,
},
- .probe_new = wm8731_i2c_probe,
+ .probe = wm8731_i2c_probe,
.id_table = wm8731_i2c_id,
};
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 90b54343370c..9f4e372e90ea 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -649,7 +649,7 @@ static struct i2c_driver wm8737_i2c_driver = {
.name = "wm8737",
.of_match_table = wm8737_of_match,
},
- .probe_new = wm8737_i2c_probe,
+ .probe = wm8737_i2c_probe,
.id_table = wm8737_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index c7afa4f2795d..787156b980a1 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -616,7 +616,7 @@ static struct i2c_driver wm8741_i2c_driver = {
.name = "wm8741",
.of_match_table = wm8741_of_match,
},
- .probe_new = wm8741_i2c_probe,
+ .probe = wm8741_i2c_probe,
.id_table = wm8741_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 2f6ee8d6639f..20dc9ff9fea9 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -813,7 +813,7 @@ static struct i2c_driver wm8750_i2c_driver = {
.name = "wm8750",
.of_match_table = wm8750_of_match,
},
- .probe_new = wm8750_i2c_probe,
+ .probe = wm8750_i2c_probe,
.id_table = wm8750_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index bb18f58dc670..5e8a8eb41b2b 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1590,7 +1590,7 @@ static struct i2c_driver wm8753_i2c_driver = {
.name = "wm8753",
.of_match_table = wm8753_of_match,
},
- .probe_new = wm8753_i2c_probe,
+ .probe = wm8753_i2c_probe,
.id_table = wm8753_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 936ea24621b0..212224a68006 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -523,7 +523,7 @@ static struct i2c_driver wm8776_i2c_driver = {
.name = "wm8776",
.of_match_table = wm8776_of_match,
},
- .probe_new = wm8776_i2c_probe,
+ .probe = wm8776_i2c_probe,
.id_table = wm8776_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c
index 3ce1a39d76eb..7062a8b2f8b5 100644
--- a/sound/soc/codecs/wm8804-i2c.c
+++ b/sound/soc/codecs/wm8804-i2c.c
@@ -60,7 +60,7 @@ static struct i2c_driver wm8804_i2c_driver = {
.of_match_table = of_match_ptr(wm8804_of_match),
.acpi_match_table = ACPI_PTR(wm8804_acpi_match),
},
- .probe_new = wm8804_i2c_probe,
+ .probe = wm8804_i2c_probe,
.remove = wm8804_i2c_remove,
.id_table = wm8804_i2c_id
};
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 03bbd85ebdf4..320ccd92f318 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1295,7 +1295,7 @@ static struct i2c_driver wm8900_i2c_driver = {
.driver = {
.name = "wm8900",
},
- .probe_new = wm8900_i2c_probe,
+ .probe = wm8900_i2c_probe,
.remove = wm8900_i2c_remove,
.id_table = wm8900_i2c_id,
};
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 1dc8e20bdace..901b65ef8de5 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -2209,7 +2209,7 @@ static struct i2c_driver wm8903_i2c_driver = {
.name = "wm8903",
.of_match_table = wm8903_of_match,
},
- .probe_new = wm8903_i2c_probe,
+ .probe = wm8903_i2c_probe,
.remove = wm8903_i2c_remove,
.id_table = wm8903_i2c_id,
};
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 791d8738d1c0..068e610b1b4c 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2337,7 +2337,7 @@ static struct i2c_driver wm8904_i2c_driver = {
.name = "wm8904",
.of_match_table = of_match_ptr(wm8904_of_match),
},
- .probe_new = wm8904_i2c_probe,
+ .probe = wm8904_i2c_probe,
.id_table = wm8904_i2c_id,
};
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 8eb4782c9232..53c27986d216 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -860,7 +860,7 @@ static struct i2c_driver wm8940_i2c_driver = {
.name = "wm8940",
.of_match_table = wm8940_of_match,
},
- .probe_new = wm8940_i2c_probe,
+ .probe = wm8940_i2c_probe,
.id_table = wm8940_i2c_id,
};
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 05ef45672ebc..78044f580a67 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -1003,7 +1003,7 @@ static struct i2c_driver wm8955_i2c_driver = {
.driver = {
.name = "wm8955",
},
- .probe_new = wm8955_i2c_probe,
+ .probe = wm8955_i2c_probe,
.id_table = wm8955_i2c_id,
};
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 0d167238a369..366f5d769d6d 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -1518,7 +1518,7 @@ static struct i2c_driver wm8960_i2c_driver = {
.of_match_table = of_match_ptr(wm8960_of_match),
.acpi_match_table = ACPI_PTR(wm8960_acpi_match),
},
- .probe_new = wm8960_i2c_probe,
+ .probe = wm8960_i2c_probe,
.remove = wm8960_i2c_remove,
.id_table = wm8960_i2c_id,
};
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index a4857024711d..c076f78d04ce 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -982,7 +982,7 @@ static struct i2c_driver wm8961_i2c_driver = {
.name = "wm8961",
.of_match_table = of_match_ptr(wm8961_of_match),
},
- .probe_new = wm8961_i2c_probe,
+ .probe = wm8961_i2c_probe,
.id_table = wm8961_i2c_id,
};
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index b901e4c65e8a..68ea15be7330 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3946,7 +3946,7 @@ static struct i2c_driver wm8962_i2c_driver = {
.of_match_table = wm8962_of_match,
.pm = &wm8962_pm,
},
- .probe_new = wm8962_i2c_probe,
+ .probe = wm8962_i2c_probe,
.remove = wm8962_i2c_remove,
.id_table = wm8962_i2c_id,
};
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 4db9248de54b..b22d8f0b59be 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -700,7 +700,7 @@ static struct i2c_driver wm8971_i2c_driver = {
.driver = {
.name = "wm8971",
},
- .probe_new = wm8971_i2c_probe,
+ .probe = wm8971_i2c_probe,
.id_table = wm8971_i2c_id,
};
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 010a394c705c..044b6f604c09 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -723,7 +723,7 @@ static struct i2c_driver wm8974_i2c_driver = {
.name = "wm8974",
.of_match_table = wm8974_of_match,
},
- .probe_new = wm8974_i2c_probe,
+ .probe = wm8974_i2c_probe,
.id_table = wm8974_i2c_id,
};
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index aa2f55401a88..5c829301cf4c 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1072,7 +1072,7 @@ static struct i2c_driver wm8978_i2c_driver = {
.name = "wm8978",
.of_match_table = wm8978_of_match,
},
- .probe_new = wm8978_i2c_probe,
+ .probe = wm8978_i2c_probe,
.id_table = wm8978_i2c_id,
};
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index 50e6ac6ccbe0..2bd26e2478d9 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -1068,7 +1068,7 @@ static struct i2c_driver wm8983_i2c_driver = {
.driver = {
.name = "wm8983",
},
- .probe_new = wm8983_i2c_probe,
+ .probe = wm8983_i2c_probe,
.id_table = wm8983_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 751aa6730833..c0816bcfa294 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1206,7 +1206,7 @@ static struct i2c_driver wm8985_i2c_driver = {
.driver = {
.name = "wm8985",
},
- .probe_new = wm8985_i2c_probe,
+ .probe = wm8985_i2c_probe,
.id_table = wm8985_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 5dbdf647cd97..b440719cca7d 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -905,7 +905,7 @@ static struct i2c_driver wm8988_i2c_driver = {
.driver = {
.name = "wm8988",
},
- .probe_new = wm8988_i2c_probe,
+ .probe = wm8988_i2c_probe,
.id_table = wm8988_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 589af286f133..5a8e765090af 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1247,7 +1247,7 @@ static struct i2c_driver wm8990_i2c_driver = {
.driver = {
.name = "wm8990",
},
- .probe_new = wm8990_i2c_probe,
+ .probe = wm8990_i2c_probe,
.id_table = wm8990_i2c_id,
};
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 30121993b7b4..8cb2ae829699 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1323,7 +1323,7 @@ static struct i2c_driver wm8991_i2c_driver = {
.driver = {
.name = "wm8991",
},
- .probe_new = wm8991_i2c_probe,
+ .probe = wm8991_i2c_probe,
.id_table = wm8991_i2c_id,
};
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 22a47acbc6d1..feb997c698e2 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1741,7 +1741,7 @@ static struct i2c_driver wm8993_i2c_driver = {
.driver = {
.name = "wm8993",
},
- .probe_new = wm8993_i2c_probe,
+ .probe = wm8993_i2c_probe,
.remove = wm8993_i2c_remove,
.id_table = wm8993_i2c_id,
};
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index eed48bf339f2..90588614edcc 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2268,7 +2268,7 @@ static struct i2c_driver wm8995_i2c_driver = {
.driver = {
.name = "wm8995",
},
- .probe_new = wm8995_i2c_probe,
+ .probe = wm8995_i2c_probe,
.id_table = wm8995_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index b52ed89d631a..5d0eb0ae0475 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -3086,7 +3086,7 @@ static struct i2c_driver wm8996_i2c_driver = {
.driver = {
.name = "wm8996",
},
- .probe_new = wm8996_i2c_probe,
+ .probe = wm8996_i2c_probe,
.remove = wm8996_i2c_remove,
.id_table = wm8996_i2c_id,
};
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 513ec0ba81bb..34a07db7342a 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1369,7 +1369,7 @@ static struct i2c_driver wm9081_i2c_driver = {
.driver = {
.name = "wm9081",
},
- .probe_new = wm9081_i2c_probe,
+ .probe = wm9081_i2c_probe,
.remove = wm9081_i2c_remove,
.id_table = wm9081_i2c_id,
};
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index ef3524c3f07f..432729c753dd 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -616,7 +616,7 @@ static struct i2c_driver wm9090_i2c_driver = {
.driver = {
.name = "wm9090",
},
- .probe_new = wm9090_i2c_probe,
+ .probe = wm9090_i2c_probe,
.id_table = wm9090_id,
};
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 216120b68b64..5a89abfe8784 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -15,7 +15,6 @@
#include <linux/firmware.h>
#include <linux/list.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -686,8 +685,6 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
{
struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
struct wm_coeff_ctl *ctl;
- struct snd_kcontrol *kcontrol;
- char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int ret;
ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
@@ -699,23 +696,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
ctl = cs_ctl->priv;
- if (dsp->component->name_prefix)
- snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s",
- dsp->component->name_prefix, ctl->name);
- else
- snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s",
- ctl->name);
-
- kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name);
- if (!kcontrol) {
- adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name);
- return -EINVAL;
- }
-
- snd_ctl_notify(dsp->component->card->snd_card,
- SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id);
-
- return 0;
+ return snd_soc_component_notify_control(dsp->component, ctl->name);
}
EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index e80b53143569..e40d583a1ce6 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -1333,7 +1333,8 @@ static int wsa883x_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
WSA883X_DRE_GAIN_EN_MASK,
WSA883X_DRE_GAIN_FROM_CSR);
snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL,
- WSA883X_GLOBAL_PA_EN_MASK, 1);
+ WSA883X_GLOBAL_PA_EN_MASK,
+ WSA883X_GLOBAL_PA_ENABLE);
}
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
new file mode 100644
index 000000000000..993d76b18b53
--- /dev/null
+++ b/sound/soc/codecs/wsa884x.c
@@ -0,0 +1,1936 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023, Linaro Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define WSA884X_BASE 0x3000
+#define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001)
+#define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00)
+#define WSA884X_ADC_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x01)
+#define WSA884X_BOP1_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x02)
+#define WSA884X_BOP2_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x03)
+#define WSA884X_BOP2_PROG_BOP2_VTH_MASK 0xf0
+#define WSA884X_BOP2_PROG_BOP2_VTH_SHIFT 4
+#define WSA884X_BOP2_PROG_BOP2_HYST_MASK 0x0f
+#define WSA884X_BOP2_PROG_BOP2_HYST_SHIFT 0
+#define WSA884X_UVLO_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x04)
+#define WSA884X_UVLO_PROG1 (WSA884X_ANA_BG_TSADC_BASE + 0x05)
+#define WSA884X_SPARE_CTRL_0 (WSA884X_ANA_BG_TSADC_BASE + 0x06)
+#define WSA884X_SPARE_CTRL_1 (WSA884X_ANA_BG_TSADC_BASE + 0x07)
+#define WSA884X_SPARE_CTRL_2 (WSA884X_ANA_BG_TSADC_BASE + 0x08)
+#define WSA884X_SPARE_CTRL_3 (WSA884X_ANA_BG_TSADC_BASE + 0x09)
+#define WSA884X_REF_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x0a)
+#define WSA884X_REF_CTRL_BG_RDY_SEL_MASK 0x03
+#define WSA884X_REF_CTRL_BG_RDY_SEL_SHIFT 0
+#define WSA884X_BG_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0b)
+#define WSA884X_BG_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x0c)
+#define WSA884X_ADC_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x0d)
+#define WSA884X_ADC_IREF_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0e)
+#define WSA884X_ADC_ISENS_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0f)
+#define WSA884X_ADC_CLK_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x10)
+#define WSA884X_ADC_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x11)
+#define WSA884X_ADC_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x12)
+#define WSA884X_VBAT_SNS (WSA884X_ANA_BG_TSADC_BASE + 0x13)
+#define WSA884X_DOUT_MSB (WSA884X_ANA_BG_TSADC_BASE + 0x14)
+#define WSA884X_DOUT_LSB (WSA884X_ANA_BG_TSADC_BASE + 0x15)
+#define WSA884X_BOP_ATEST_SEL (WSA884X_ANA_BG_TSADC_BASE + 0x16)
+#define WSA884X_MISC0 (WSA884X_ANA_BG_TSADC_BASE + 0x17)
+#define WSA884X_MISC1 (WSA884X_ANA_BG_TSADC_BASE + 0x18)
+#define WSA884X_MISC2 (WSA884X_ANA_BG_TSADC_BASE + 0x19)
+#define WSA884X_MISC3 (WSA884X_ANA_BG_TSADC_BASE + 0x1a)
+#define WSA884X_SPARE_TSBG_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1b)
+#define WSA884X_SPARE_TUNE_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1c)
+#define WSA884X_SPARE_TUNE_1 (WSA884X_ANA_BG_TSADC_BASE + 0x1d)
+
+#define WSA884X_ANA_IVSENSE_BASE (WSA884X_BASE + 0x0020)
+#define WSA884X_VSENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x00)
+#define WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK 0xe0
+#define WSA884X_VSENSE1_GAIN_VSENSE_FE_SHIFT 5
+#define WSA884X_ISENSE2 (WSA884X_ANA_IVSENSE_BASE + 0x01)
+#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK 0xe0
+#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_SHIFT 5
+
+#define WSA884X_SPARE_CTL_1 (WSA884X_ANA_IVSENSE_BASE + 0x02)
+#define WSA884X_SPARE_CTL_2 (WSA884X_ANA_IVSENSE_BASE + 0x03)
+#define WSA884X_SPARE_CTL_3 (WSA884X_ANA_IVSENSE_BASE + 0x04)
+#define WSA884X_SPARE_CTL_4 (WSA884X_ANA_IVSENSE_BASE + 0x05)
+#define WSA884X_EN (WSA884X_ANA_IVSENSE_BASE + 0x06)
+#define WSA884X_OVERRIDE1 (WSA884X_ANA_IVSENSE_BASE + 0x07)
+#define WSA884X_OVERRIDE2 (WSA884X_ANA_IVSENSE_BASE + 0x08)
+#define WSA884X_ISENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x09)
+#define WSA884X_ISENSE_CAL (WSA884X_ANA_IVSENSE_BASE + 0x0a)
+#define WSA884X_MISC (WSA884X_ANA_IVSENSE_BASE + 0x0b)
+#define WSA884X_ADC_0 (WSA884X_ANA_IVSENSE_BASE + 0x0c)
+#define WSA884X_ADC_1 (WSA884X_ANA_IVSENSE_BASE + 0x0d)
+#define WSA884X_ADC_2 (WSA884X_ANA_IVSENSE_BASE + 0x0e)
+#define WSA884X_ADC_3 (WSA884X_ANA_IVSENSE_BASE + 0x0f)
+#define WSA884X_ADC_4 (WSA884X_ANA_IVSENSE_BASE + 0x10)
+#define WSA884X_ADC_5 (WSA884X_ANA_IVSENSE_BASE + 0x11)
+#define WSA884X_ADC_6 (WSA884X_ANA_IVSENSE_BASE + 0x12)
+#define WSA884X_ADC_7 (WSA884X_ANA_IVSENSE_BASE + 0x13)
+#define WSA884X_STATUS (WSA884X_ANA_IVSENSE_BASE + 0x14)
+#define WSA884X_IVSENSE_SPARE_TUNE_1 (WSA884X_ANA_IVSENSE_BASE + 0x15)
+#define WSA884X_SPARE_TUNE_2 (WSA884X_ANA_IVSENSE_BASE + 0x16)
+#define WSA884X_SPARE_TUNE_3 (WSA884X_ANA_IVSENSE_BASE + 0x17)
+#define WSA884X_SPARE_TUNE_4 (WSA884X_ANA_IVSENSE_BASE + 0x18)
+
+#define WSA884X_ANA_SPK_TOP_BASE (WSA884X_BASE + 0x0040)
+#define WSA884X_TOP_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x00)
+#define WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK 0x01
+#define WSA884X_CLIP_DET_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x01)
+#define WSA884X_CLIP_DET_CTRL2 (WSA884X_ANA_SPK_TOP_BASE + 0x02)
+#define WSA884X_DAC_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x03)
+#define WSA884X_DAC_VCM_CTRL_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x04)
+#define WSA884X_DAC_VCM_CTRL_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x05)
+#define WSA884X_DAC_VCM_CTRL_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x06)
+#define WSA884X_DAC_VCM_CTRL_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x07)
+#define WSA884X_DAC_VCM_CTRL_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x08)
+#define WSA884X_DAC_VCM_CTRL_REG6 (WSA884X_ANA_SPK_TOP_BASE + 0x09)
+#define WSA884X_PWM_CLK_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0a)
+#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_MASK 0x80
+#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_SHIFT 7
+#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_MASK 0x40
+#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_SHIFT 6
+#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_MASK 0x30
+#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_SHIFT 4
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK 0x08
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_SHIFT 3
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_MASK 0x06
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_SHIFT 1
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_MASK 0x01
+#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_SHIFT 0
+#define WSA884X_DRV_LF_LDO_SEL (WSA884X_ANA_SPK_TOP_BASE + 0x0b)
+#define WSA884X_OCP_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0c)
+#define WSA884X_PDRV_HS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0d)
+#define WSA884X_PDRV_LS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0e)
+#define WSA884X_SPK_TOP_SPARE_CTL_1 (WSA884X_ANA_SPK_TOP_BASE + 0x0f)
+#define WSA884X_SPK_TOP_SPARE_CTL_2 (WSA884X_ANA_SPK_TOP_BASE + 0x10)
+#define WSA884X_SPK_TOP_SPARE_CTL_3 (WSA884X_ANA_SPK_TOP_BASE + 0x11)
+#define WSA884X_SPK_TOP_SPARE_CTL_4 (WSA884X_ANA_SPK_TOP_BASE + 0x12)
+#define WSA884X_SPARE_CTL_5 (WSA884X_ANA_SPK_TOP_BASE + 0x13)
+#define WSA884X_DAC_EN_DEBUG_REG (WSA884X_ANA_SPK_TOP_BASE + 0x14)
+#define WSA884X_DAC_OPAMP_BIAS1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x15)
+#define WSA884X_DAC_OPAMP_BIAS2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x16)
+#define WSA884X_DAC_TUNE1 (WSA884X_ANA_SPK_TOP_BASE + 0x17)
+#define WSA884X_DAC_VOLTAGE_CTRL_REG (WSA884X_ANA_SPK_TOP_BASE + 0x18)
+#define WSA884X_ATEST1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x19)
+#define WSA884X_ATEST2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x1a)
+#define WSA884X_TOP_BIAS_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x1b)
+#define WSA884X_TOP_BIAS_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1c)
+#define WSA884X_TOP_BIAS_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x1d)
+#define WSA884X_TOP_BIAS_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x1e)
+#define WSA884X_PWRSTG_DBG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1f)
+#define WSA884X_DRV_LF_BLK_EN (WSA884X_ANA_SPK_TOP_BASE + 0x20)
+#define WSA884X_DRV_LF_EN (WSA884X_ANA_SPK_TOP_BASE + 0x21)
+#define WSA884X_DRV_LF_MASK_DCC_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x22)
+#define WSA884X_DRV_LF_MISC_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x23)
+#define WSA884X_DRV_LF_REG_GAIN (WSA884X_ANA_SPK_TOP_BASE + 0x24)
+#define WSA884X_DRV_OS_CAL_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x25)
+#define WSA884X_DRV_OS_CAL_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x26)
+#define WSA884X_PWRSTG_DBG (WSA884X_ANA_SPK_TOP_BASE + 0x27)
+#define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28)
+#define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29)
+#define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a)
+#define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b)
+#define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c)
+#define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d)
+#define WSA884X_SPK_TOP_SPARE_TUNE_3 (WSA884X_ANA_SPK_TOP_BASE + 0x2e)
+#define WSA884X_SPK_TOP_SPARE_TUNE_4 (WSA884X_ANA_SPK_TOP_BASE + 0x2f)
+#define WSA884X_SPARE_TUNE_5 (WSA884X_ANA_SPK_TOP_BASE + 0x30)
+#define WSA884X_SPARE_TUNE_6 (WSA884X_ANA_SPK_TOP_BASE + 0x31)
+#define WSA884X_SPARE_TUNE_7 (WSA884X_ANA_SPK_TOP_BASE + 0x32)
+#define WSA884X_SPARE_TUNE_8 (WSA884X_ANA_SPK_TOP_BASE + 0x33)
+#define WSA884X_SPARE_TUNE_9 (WSA884X_ANA_SPK_TOP_BASE + 0x34)
+#define WSA884X_SPARE_TUNE_10 (WSA884X_ANA_SPK_TOP_BASE + 0x35)
+#define WSA884X_PA_STATUS0 (WSA884X_ANA_SPK_TOP_BASE + 0x36)
+#define WSA884X_PA_STATUS1 (WSA884X_ANA_SPK_TOP_BASE + 0x37)
+#define WSA884X_PA_STATUS2 (WSA884X_ANA_SPK_TOP_BASE + 0x38)
+#define WSA884X_PA_STATUS3 (WSA884X_ANA_SPK_TOP_BASE + 0x39)
+#define WSA884X_PA_STATUS4 (WSA884X_ANA_SPK_TOP_BASE + 0x3a)
+#define WSA884X_PA_STATUS5 (WSA884X_ANA_SPK_TOP_BASE + 0x3b)
+#define WSA884X_SPARE_RO_1 (WSA884X_ANA_SPK_TOP_BASE + 0x3c)
+#define WSA884X_SPARE_RO_2 (WSA884X_ANA_SPK_TOP_BASE + 0x3d)
+#define WSA884X_SPARE_RO_3 (WSA884X_ANA_SPK_TOP_BASE + 0x3e)
+
+#define WSA884X_ANA_BOOST_BASE (WSA884X_BASE + 0x0090)
+#define WSA884X_STB_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x00)
+#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK 0xf8
+#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_SHIFT 3
+#define WSA884X_STB_CTRL1_VOUT_FS_MASK 0x07
+#define WSA884X_STB_CTRL1_VOUT_FS_SHIFT 0
+#define WSA884X_CURRENT_LIMIT (WSA884X_ANA_BOOST_BASE + 0x01)
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK 0x80
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_SHIFT 7
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK 0x7c
+#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_SHIFT 2
+#define WSA884X_CURRENT_LIMIT_CLK_PHASE_SHIFT 0
+#define WSA884X_BYP_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x02)
+#define WSA884X_SPARE_CTL_0 (WSA884X_ANA_BOOST_BASE + 0x03)
+#define WSA884X_BOOST_SPARE_CTL_1 (WSA884X_ANA_BOOST_BASE + 0x04)
+#define WSA884X_SPARE_RO_0 (WSA884X_ANA_BOOST_BASE + 0x05)
+#define WSA884X_BOOST_SPARE_RO_1 (WSA884X_ANA_BOOST_BASE + 0x06)
+#define WSA884X_IBIAS1 (WSA884X_ANA_BOOST_BASE + 0x07)
+#define WSA884X_IBIAS2 (WSA884X_ANA_BOOST_BASE + 0x08)
+#define WSA884X_IBIAS3 (WSA884X_ANA_BOOST_BASE + 0x09)
+#define WSA884X_EN_CTRL (WSA884X_ANA_BOOST_BASE + 0x0a)
+#define WSA884X_STB_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0b)
+#define WSA884X_STB_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0c)
+#define WSA884X_STB_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x0d)
+#define WSA884X_BYP_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0e)
+#define WSA884X_BYP_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0f)
+#define WSA884X_ZX_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x10)
+#define WSA884X_ZX_CTRL1_ZX_DET_EN_MASK 0x80
+#define WSA884X_ZX_CTRL1_ZX_DET_EN_SHIFT 7
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_MASK 0x40
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_SHIFT 6
+#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_MASK 0x20
+#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_SHIFT 5
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK 0x18
+#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_SHIFT 3
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_MASK 0x04
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_SHIFT 2
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_MASK 0x02
+#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_SHIFT 1
+#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_MASK 0x01
+#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_SHIFT 0
+#define WSA884X_ZX_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x11)
+#define WSA884X_BLEEDER_CTRL (WSA884X_ANA_BOOST_BASE + 0x12)
+#define WSA884X_BOOST_MISC (WSA884X_ANA_BOOST_BASE + 0x13)
+#define WSA884X_PWRSTAGE_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x14)
+#define WSA884X_PWRSTAGE_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x15)
+#define WSA884X_PWRSTAGE_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x16)
+#define WSA884X_PWRSTAGE_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x17)
+#define WSA884X_MAXD_REG1 (WSA884X_ANA_BOOST_BASE + 0x18)
+#define WSA884X_MAXD_REG2 (WSA884X_ANA_BOOST_BASE + 0x19)
+#define WSA884X_ILIM_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1a)
+#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_MASK 0x80
+#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_SHIFT 0x07
+#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_MASK 0x40
+#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_SHIFT 0x06
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_MASK 0x38
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_SHIFT 0x03
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK 0x07
+#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_SHIFT 0x00
+#define WSA884X_ILIM_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1b)
+#define WSA884X_TEST_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1c)
+#define WSA884X_TEST_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1d)
+#define WSA884X_SPARE1 (WSA884X_ANA_BOOST_BASE + 0x1e)
+#define WSA884X_BOOT_CAP_CHECK (WSA884X_ANA_BOOST_BASE + 0x1f)
+
+#define WSA884X_ANA_PON_LDOL_BASE (WSA884X_BASE + 0x00b0)
+#define WSA884X_PON_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x00)
+#define WSA884X_PWRSAV_CTL (WSA884X_ANA_PON_LDOL_BASE + 0x01)
+#define WSA884X_PON_LDOL_SPARE_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x02)
+#define WSA884X_PON_LDOL_SPARE_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x03)
+#define WSA884X_PON_LDOL_SPARE_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x04)
+#define WSA884X_PON_LDOL_SPARE_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x05)
+#define WSA884X_PON_CLT_1 (WSA884X_ANA_PON_LDOL_BASE + 0x06)
+#define WSA884X_PON_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x07)
+#define WSA884X_PON_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x08)
+#define WSA884X_CKWD_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x09)
+#define WSA884X_CKWD_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0a)
+#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK 0x20
+#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_SHIFT 5
+#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK 0x1f
+#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_SHIFT 0
+#define WSA884X_CKWD_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x0b)
+#define WSA884X_CKSK_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0c)
+#define WSA884X_PADSW_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0d)
+#define WSA884X_TEST_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0e)
+#define WSA884X_TEST_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0f)
+#define WSA884X_STATUS_0 (WSA884X_ANA_PON_LDOL_BASE + 0x10)
+#define WSA884X_STATUS_1 (WSA884X_ANA_PON_LDOL_BASE + 0x11)
+#define WSA884X_PON_LDOL_SPARE_TUNE_0 (WSA884X_ANA_PON_LDOL_BASE + 0x12)
+#define WSA884X_PON_LDOL_SPARE_TUNE_1 (WSA884X_ANA_PON_LDOL_BASE + 0x13)
+#define WSA884X_PON_LDOL_SPARE_TUNE_2 (WSA884X_ANA_PON_LDOL_BASE + 0x14)
+#define WSA884X_PON_LDOL_SPARE_TUNE_3 (WSA884X_ANA_PON_LDOL_BASE + 0x15)
+#define WSA884X_PON_LDOL_SPARE_TUNE_4 (WSA884X_ANA_PON_LDOL_BASE + 0x16)
+
+#define WSA884X_DIG_CTRL0_BASE (WSA884X_BASE + 0x0400)
+#define WSA884X_DIG_CTRL0_PAGE (WSA884X_DIG_CTRL0_BASE + 0x00)
+#define WSA884X_CHIP_ID0 (WSA884X_DIG_CTRL0_BASE + 0x01)
+#define WSA884X_CHIP_ID1 (WSA884X_DIG_CTRL0_BASE + 0x02)
+#define WSA884X_CHIP_ID2 (WSA884X_DIG_CTRL0_BASE + 0x03)
+#define WSA884X_CHIP_ID3 (WSA884X_DIG_CTRL0_BASE + 0x04)
+#define WSA884X_BUS_ID (WSA884X_DIG_CTRL0_BASE + 0x05)
+#define WSA884X_CDC_RST_CTL (WSA884X_DIG_CTRL0_BASE + 0x10)
+#define WSA884X_SWR_RESET_EN (WSA884X_DIG_CTRL0_BASE + 0x14)
+#define WSA884X_TOP_CLK_CFG (WSA884X_DIG_CTRL0_BASE + 0x18)
+#define WSA884X_SWR_CLK_RATE (WSA884X_DIG_CTRL0_BASE + 0x19)
+#define WSA884X_CDC_PATH_MODE (WSA884X_DIG_CTRL0_BASE + 0x1a)
+#define WSA884X_CDC_PATH_MODE_RXD_MODE_MASK 0x02
+#define WSA884X_CDC_PATH_MODE_RXD_MODE_SHIFT 0
+#define WSA884X_CDC_PATH_MODE_TXD_MODE_MASK 0x01
+#define WSA884X_CDC_PATH_MODE_TXD_MODE_SHIFT 0
+#define WSA884X_CDC_CLK_CTL (WSA884X_DIG_CTRL0_BASE + 0x1c)
+#define WSA884X_PA_FSM_EN (WSA884X_DIG_CTRL0_BASE + 0x30)
+#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK 0x01
+#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_SHIFT 0
+#define WSA884X_PA_FSM_CTL0 (WSA884X_DIG_CTRL0_BASE + 0x31)
+#define WSA884X_PA_FSM_CTL1 (WSA884X_DIG_CTRL0_BASE + 0x32)
+#define WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK 0x38
+#define WSA884X_PA_FSM_TIMER0 (WSA884X_DIG_CTRL0_BASE + 0x33)
+#define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34)
+#define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35)
+#define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36)
+#define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37)
+#define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38)
+#define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39)
+#define WSA884X_PA_FSM_MSK0 (WSA884X_DIG_CTRL0_BASE + 0x3a)
+#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b)
+#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c)
+#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d)
+#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e)
+#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50)
+#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51)
+#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52)
+#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53)
+#define WSA884X_TEMP_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x54)
+#define WSA884X_TEMP_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x55)
+#define WSA884X_TEMP_CONFIG0 (WSA884X_DIG_CTRL0_BASE + 0x56)
+#define WSA884X_TEMP_CONFIG1 (WSA884X_DIG_CTRL0_BASE + 0x57)
+#define WSA884X_VBAT_THRM_FLT_CTL (WSA884X_DIG_CTRL0_BASE + 0x58)
+#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_MASK 0xe0
+#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_SHIFT 5
+#define WSA884X_VBAT_THRM_FLT_CTL_THRM_FLT_EN_SHIFT 4
+#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK 0x0e
+#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_SHIFT 1
+#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_FLT_EN_SHIFT 0
+#define WSA884X_VBAT_CAL_CTL (WSA884X_DIG_CTRL0_BASE + 0x59)
+#define WSA884X_VBAT_CAL_CTL_RESERVE_MASK 0x0e
+#define WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK 0x01
+#define WSA884X_VBAT_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x5a)
+#define WSA884X_VBAT_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x5b)
+#define WSA884X_VBAT_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x5c)
+#define WSA884X_VBAT_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x5d)
+#define WSA884X_VBAT_CAL_MSB (WSA884X_DIG_CTRL0_BASE + 0x5e)
+#define WSA884X_VBAT_CAL_LSB (WSA884X_DIG_CTRL0_BASE + 0x5f)
+#define WSA884X_UVLO_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x60)
+#define WSA884X_BOP_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x61)
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK 0x1e
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_SHIFT 1
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK 0x1
+#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_SHIFT 0
+#define WSA884X_VBAT_ZONE_DETC_CTL (WSA884X_DIG_CTRL0_BASE + 0x64)
+#define WSA884X_CPS_CTL (WSA884X_DIG_CTRL0_BASE + 0x68)
+#define WSA884X_CDC_RX_CTL (WSA884X_DIG_CTRL0_BASE + 0x70)
+#define WSA884X_CDC_SPK_DSM_A1_0 (WSA884X_DIG_CTRL0_BASE + 0x71)
+#define WSA884X_CDC_SPK_DSM_A1_1 (WSA884X_DIG_CTRL0_BASE + 0x72)
+#define WSA884X_CDC_SPK_DSM_A2_0 (WSA884X_DIG_CTRL0_BASE + 0x73)
+#define WSA884X_CDC_SPK_DSM_A2_1 (WSA884X_DIG_CTRL0_BASE + 0x74)
+#define WSA884X_CDC_SPK_DSM_A3_0 (WSA884X_DIG_CTRL0_BASE + 0x75)
+#define WSA884X_CDC_SPK_DSM_A3_1 (WSA884X_DIG_CTRL0_BASE + 0x76)
+#define WSA884X_CDC_SPK_DSM_A4_0 (WSA884X_DIG_CTRL0_BASE + 0x77)
+#define WSA884X_CDC_SPK_DSM_A4_1 (WSA884X_DIG_CTRL0_BASE + 0x78)
+#define WSA884X_CDC_SPK_DSM_A5_0 (WSA884X_DIG_CTRL0_BASE + 0x79)
+#define WSA884X_CDC_SPK_DSM_A5_1 (WSA884X_DIG_CTRL0_BASE + 0x7a)
+#define WSA884X_CDC_SPK_DSM_A6_0 (WSA884X_DIG_CTRL0_BASE + 0x7b)
+#define WSA884X_CDC_SPK_DSM_A7_0 (WSA884X_DIG_CTRL0_BASE + 0x7c)
+#define WSA884X_CDC_SPK_DSM_C_0 (WSA884X_DIG_CTRL0_BASE + 0x7d)
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK 0xf0
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_SHIFT 4
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK 0x0f
+#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_SHIFT 0
+#define WSA884X_CDC_SPK_DSM_C_1 (WSA884X_DIG_CTRL0_BASE + 0x7e)
+#define WSA884X_CDC_SPK_DSM_C_2 (WSA884X_DIG_CTRL0_BASE + 0x7f)
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK 0xf0
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_SHIFT 4
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_MASK 0x0f
+#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_SHIFT 0
+#define WSA884X_CDC_SPK_DSM_C_3 (WSA884X_DIG_CTRL0_BASE + 0x80)
+#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK 0x3f
+#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_SHIFT 0
+#define WSA884X_CDC_SPK_DSM_R1 (WSA884X_DIG_CTRL0_BASE + 0x81)
+#define WSA884X_CDC_SPK_DSM_R2 (WSA884X_DIG_CTRL0_BASE + 0x82)
+#define WSA884X_CDC_SPK_DSM_R3 (WSA884X_DIG_CTRL0_BASE + 0x83)
+#define WSA884X_CDC_SPK_DSM_R4 (WSA884X_DIG_CTRL0_BASE + 0x84)
+#define WSA884X_CDC_SPK_DSM_R5 (WSA884X_DIG_CTRL0_BASE + 0x85)
+#define WSA884X_CDC_SPK_DSM_R6 (WSA884X_DIG_CTRL0_BASE + 0x86)
+#define WSA884X_CDC_SPK_DSM_R7 (WSA884X_DIG_CTRL0_BASE + 0x87)
+#define WSA884X_CDC_SPK_GAIN_PDM_0 (WSA884X_DIG_CTRL0_BASE + 0x88)
+#define WSA884X_CDC_SPK_GAIN_PDM_1 (WSA884X_DIG_CTRL0_BASE + 0x89)
+#define WSA884X_CDC_SPK_GAIN_PDM_2 (WSA884X_DIG_CTRL0_BASE + 0x8a)
+#define WSA884X_PDM_WD_CTL (WSA884X_DIG_CTRL0_BASE + 0x8b)
+#define WSA884X_PDM_WD_CTL_HOLD_OFF_MASK 0x04
+#define WSA884X_PDM_WD_CTL_HOLD_OFF_SHIFT 2
+#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_MASK 0x02
+#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_SHIFT 1
+#define WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK 0x01
+#define WSA884X_PDM_WD_CTL_PDM_WD_EN_SHIFT 0
+#define WSA884X_DEM_BYPASS_DATA0 (WSA884X_DIG_CTRL0_BASE + 0x90)
+#define WSA884X_DEM_BYPASS_DATA1 (WSA884X_DIG_CTRL0_BASE + 0x91)
+#define WSA884X_DEM_BYPASS_DATA2 (WSA884X_DIG_CTRL0_BASE + 0x92)
+#define WSA884X_DEM_BYPASS_DATA3 (WSA884X_DIG_CTRL0_BASE + 0x93)
+#define WSA884X_DRE_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xb0)
+#define WSA884X_DRE_CTL_0_PROG_DELAY_MASK 0xf0
+#define WSA884X_DRE_CTL_0_PROG_DELAY_SHIFT 4
+#define WSA884X_DRE_CTL_0_OFFSET_MASK 0x07
+#define WSA884X_DRE_CTL_0_OFFSET_SHIFT 0
+#define WSA884X_DRE_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xb1)
+#define WSA884X_DRE_CTL_1_CSR_GAIN_MASK 0x3e
+#define WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT 1
+#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK 0x01
+#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_SHIFT 0
+#define WSA884X_DRE_IDLE_DET_CTL (WSA884X_DIG_CTRL0_BASE + 0xb2)
+#define WSA884X_GAIN_RAMPING_CTL (WSA884X_DIG_CTRL0_BASE + 0xb8)
+#define WSA884X_GAIN_RAMPING_MIN (WSA884X_DIG_CTRL0_BASE + 0xb9)
+#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK 0x1f
+#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_SHIFT 0
+#define WSA884X_TAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc0)
+#define WSA884X_TAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc1)
+#define WSA884X_TAGC_FORCE_VAL (WSA884X_DIG_CTRL0_BASE + 0xc2)
+#define WSA884X_VAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc8)
+#define WSA884X_VAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc9)
+#define WSA884X_VAGC_ATTN_LVL_1 (WSA884X_DIG_CTRL0_BASE + 0xca)
+#define WSA884X_VAGC_ATTN_LVL_2 (WSA884X_DIG_CTRL0_BASE + 0xcb)
+#define WSA884X_VAGC_ATTN_LVL_3 (WSA884X_DIG_CTRL0_BASE + 0xcc)
+#define WSA884X_CLSH_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xd0)
+#define WSA884X_CLSH_CTL_0_CSR_GAIN_EN_SHIFT 7
+#define WSA884X_CLSH_CTL_0_DLY_CODE_MASK 0x70
+#define WSA884X_CLSH_CTL_0_DLY_CODE_SHIFT 4
+#define WSA884X_CLSH_CTL_0_DLY_RST_SHIFT 3
+#define WSA884X_CLSH_CTL_0_DLY_EN_SHIFT 2
+#define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1
+#define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0
+#define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1)
+#define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2)
+#define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3)
+#define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4)
+#define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5)
+#define WSA884X_CLSH_SOFT_MAX (WSA884X_DIG_CTRL0_BASE + 0xd6)
+#define WSA884X_CLSH_SIG_DP (WSA884X_DIG_CTRL0_BASE + 0xd7)
+#define WSA884X_PBR_DELAY_CTL (WSA884X_DIG_CTRL0_BASE + 0xd8)
+#define WSA884X_CLSH_SRL_MAX_PBR (WSA884X_DIG_CTRL0_BASE + 0xe0)
+#define WSA884X_PBR_MAX_VOLTAGE 20
+#define WSA884X_PBR_MAX_CODE 255
+#define WSA884X_VTH_TO_REG(vth) \
+ ((vth) != 0 ? (((vth) - 150) * WSA884X_PBR_MAX_CODE / (WSA884X_PBR_MAX_VOLTAGE * 100) + 1) : 0)
+#define WSA884X_CLSH_VTH1 (WSA884X_DIG_CTRL0_BASE + 0xe1)
+#define WSA884X_CLSH_VTH2 (WSA884X_DIG_CTRL0_BASE + 0xe2)
+#define WSA884X_CLSH_VTH3 (WSA884X_DIG_CTRL0_BASE + 0xe3)
+#define WSA884X_CLSH_VTH4 (WSA884X_DIG_CTRL0_BASE + 0xe4)
+#define WSA884X_CLSH_VTH5 (WSA884X_DIG_CTRL0_BASE + 0xe5)
+#define WSA884X_CLSH_VTH6 (WSA884X_DIG_CTRL0_BASE + 0xe6)
+#define WSA884X_CLSH_VTH7 (WSA884X_DIG_CTRL0_BASE + 0xe7)
+#define WSA884X_CLSH_VTH8 (WSA884X_DIG_CTRL0_BASE + 0xe8)
+#define WSA884X_CLSH_VTH9 (WSA884X_DIG_CTRL0_BASE + 0xe9)
+#define WSA884X_CLSH_VTH10 (WSA884X_DIG_CTRL0_BASE + 0xea)
+#define WSA884X_CLSH_VTH11 (WSA884X_DIG_CTRL0_BASE + 0xeb)
+#define WSA884X_CLSH_VTH12 (WSA884X_DIG_CTRL0_BASE + 0xec)
+#define WSA884X_CLSH_VTH13 (WSA884X_DIG_CTRL0_BASE + 0xed)
+#define WSA884X_CLSH_VTH14 (WSA884X_DIG_CTRL0_BASE + 0xee)
+#define WSA884X_CLSH_VTH15 (WSA884X_DIG_CTRL0_BASE + 0xef)
+
+#define WSA884X_DIG_CTRL1_BASE (WSA884X_BASE + 0x0500)
+#define WSA884X_DIG_CTRL1_PAGE (WSA884X_DIG_CTRL1_BASE + 0x00)
+#define WSA884X_VPHX_SYS_EN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x01)
+#define WSA884X_ANA_WO_CTL_0 (WSA884X_DIG_CTRL1_BASE + 0x04)
+#define WSA884X_ANA_WO_CTL_0_MODE_SHIFT 0
+#define WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK 0xc0
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_DISABLE 0x0
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB 0xa
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB 0x7
+#define WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK 0x3c
+#define WSA884X_ANA_WO_CTL_0_PA_MIN_GAIN_BYP_MASK 0x02
+#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER 0x1
+#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK 0x01
+#define WSA884X_ANA_WO_CTL_1 (WSA884X_DIG_CTRL1_BASE + 0x05)
+#define WSA884X_PIN_CTL (WSA884X_DIG_CTRL1_BASE + 0x10)
+#define WSA884X_PIN_CTL_OE (WSA884X_DIG_CTRL1_BASE + 0x11)
+#define WSA884X_PIN_WDATA_IOPAD (WSA884X_DIG_CTRL1_BASE + 0x12)
+#define WSA884X_PIN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x13)
+#define WSA884X_I2C_SLAVE_CTL (WSA884X_DIG_CTRL1_BASE + 0x14)
+#define WSA884X_SPMI_PAD_CTL0 (WSA884X_DIG_CTRL1_BASE + 0x15)
+#define WSA884X_SPMI_PAD_CTL1 (WSA884X_DIG_CTRL1_BASE + 0x16)
+#define WSA884X_SPMI_PAD_CTL2 (WSA884X_DIG_CTRL1_BASE + 0x17)
+#define WSA884X_MEM_CTL (WSA884X_DIG_CTRL1_BASE + 0x18)
+#define WSA884X_SWR_HM_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x19)
+#define WSA884X_SWR_HM_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x1a)
+#define WSA884X_OTP_CTRL0 (WSA884X_DIG_CTRL1_BASE + 0x30)
+#define WSA884X_OTP_CTRL1 (WSA884X_DIG_CTRL1_BASE + 0x31)
+#define WSA884X_OTP_CTRL2 (WSA884X_DIG_CTRL1_BASE + 0x32)
+#define WSA884X_OTP_STAT (WSA884X_DIG_CTRL1_BASE + 0x33)
+#define WSA884X_OTP_PRG_TCSP0 (WSA884X_DIG_CTRL1_BASE + 0x34)
+#define WSA884X_OTP_PRG_TCSP1 (WSA884X_DIG_CTRL1_BASE + 0x35)
+#define WSA884X_OTP_PRG_TPPS (WSA884X_DIG_CTRL1_BASE + 0x36)
+#define WSA884X_OTP_PRG_TVPS (WSA884X_DIG_CTRL1_BASE + 0x37)
+#define WSA884X_OTP_PRG_TVPH (WSA884X_DIG_CTRL1_BASE + 0x38)
+#define WSA884X_OTP_PRG_TPPR0 (WSA884X_DIG_CTRL1_BASE + 0x39)
+#define WSA884X_OTP_PRG_TPPR1 (WSA884X_DIG_CTRL1_BASE + 0x3a)
+#define WSA884X_OTP_PRG_TPPH (WSA884X_DIG_CTRL1_BASE + 0x3b)
+#define WSA884X_OTP_PRG_END (WSA884X_DIG_CTRL1_BASE + 0x3c)
+#define WSA884X_WAVG_PLAY (WSA884X_DIG_CTRL1_BASE + 0x40)
+#define WSA884X_WAVG_CTL (WSA884X_DIG_CTRL1_BASE + 0x41)
+#define WSA884X_WAVG_LRA_PER_0 (WSA884X_DIG_CTRL1_BASE + 0x43)
+#define WSA884X_WAVG_LRA_PER_1 (WSA884X_DIG_CTRL1_BASE + 0x44)
+#define WSA884X_WAVG_DELTA_THETA_0 (WSA884X_DIG_CTRL1_BASE + 0x45)
+#define WSA884X_WAVG_DELTA_THETA_1 (WSA884X_DIG_CTRL1_BASE + 0x46)
+#define WSA884X_WAVG_DIRECT_AMP_0 (WSA884X_DIG_CTRL1_BASE + 0x47)
+#define WSA884X_WAVG_DIRECT_AMP_1 (WSA884X_DIG_CTRL1_BASE + 0x48)
+#define WSA884X_WAVG_PTRN_AMP0_0 (WSA884X_DIG_CTRL1_BASE + 0x49)
+#define WSA884X_WAVG_PTRN_AMP0_1 (WSA884X_DIG_CTRL1_BASE + 0x4a)
+#define WSA884X_WAVG_PTRN_AMP1_0 (WSA884X_DIG_CTRL1_BASE + 0x4b)
+#define WSA884X_WAVG_PTRN_AMP1_1 (WSA884X_DIG_CTRL1_BASE + 0x4c)
+#define WSA884X_WAVG_PTRN_AMP2_0 (WSA884X_DIG_CTRL1_BASE + 0x4d)
+#define WSA884X_WAVG_PTRN_AMP2_1 (WSA884X_DIG_CTRL1_BASE + 0x4e)
+#define WSA884X_WAVG_PTRN_AMP3_0 (WSA884X_DIG_CTRL1_BASE + 0x4f)
+#define WSA884X_WAVG_PTRN_AMP3_1 (WSA884X_DIG_CTRL1_BASE + 0x50)
+#define WSA884X_WAVG_PTRN_AMP4_0 (WSA884X_DIG_CTRL1_BASE + 0x51)
+#define WSA884X_WAVG_PTRN_AMP4_1 (WSA884X_DIG_CTRL1_BASE + 0x52)
+#define WSA884X_WAVG_PTRN_AMP5_0 (WSA884X_DIG_CTRL1_BASE + 0x53)
+#define WSA884X_WAVG_PTRN_AMP5_1 (WSA884X_DIG_CTRL1_BASE + 0x54)
+#define WSA884X_WAVG_PTRN_AMP6_0 (WSA884X_DIG_CTRL1_BASE + 0x55)
+#define WSA884X_WAVG_PTRN_AMP6_1 (WSA884X_DIG_CTRL1_BASE + 0x56)
+#define WSA884X_WAVG_PTRN_AMP7_0 (WSA884X_DIG_CTRL1_BASE + 0x57)
+#define WSA884X_WAVG_PTRN_AMP7_1 (WSA884X_DIG_CTRL1_BASE + 0x58)
+#define WSA884X_WAVG_PER_0_1 (WSA884X_DIG_CTRL1_BASE + 0x59)
+#define WSA884X_WAVG_PER_2_3 (WSA884X_DIG_CTRL1_BASE + 0x5a)
+#define WSA884X_WAVG_PER_4_5 (WSA884X_DIG_CTRL1_BASE + 0x5b)
+#define WSA884X_WAVG_PER_6_7 (WSA884X_DIG_CTRL1_BASE + 0x5c)
+#define WSA884X_WAVG_STA (WSA884X_DIG_CTRL1_BASE + 0x5d)
+#define WSA884X_INTR_MODE (WSA884X_DIG_CTRL1_BASE + 0x80)
+#define WSA884X_INTR_MASK0 (WSA884X_DIG_CTRL1_BASE + 0x81)
+#define WSA884X_INTR_MASK1 (WSA884X_DIG_CTRL1_BASE + 0x82)
+#define WSA884X_INTR_STATUS0 (WSA884X_DIG_CTRL1_BASE + 0x83)
+#define WSA884X_INTR_STATUS1 (WSA884X_DIG_CTRL1_BASE + 0x84)
+#define WSA884X_INTR_CLEAR0 (WSA884X_DIG_CTRL1_BASE + 0x85)
+#define WSA884X_INTR_CLEAR1 (WSA884X_DIG_CTRL1_BASE + 0x86)
+#define WSA884X_INTR_LEVEL0 (WSA884X_DIG_CTRL1_BASE + 0x87)
+#define WSA884X_INTR_LEVEL1 (WSA884X_DIG_CTRL1_BASE + 0x88)
+#define WSA884X_INTR_SET0 (WSA884X_DIG_CTRL1_BASE + 0x89)
+#define WSA884X_INTR_SET1 (WSA884X_DIG_CTRL1_BASE + 0x8a)
+#define WSA884X_INTR_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x8b)
+#define WSA884X_INTR_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x8c)
+#define WSA884X_PDM_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc0)
+#define WSA884X_ATE_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc1)
+#define WSA884X_PA_FSM_DBG (WSA884X_DIG_CTRL1_BASE + 0xc2)
+#define WSA884X_DIG_DEBUG_MODE (WSA884X_DIG_CTRL1_BASE + 0xc3)
+#define WSA884X_DIG_DEBUG_SEL (WSA884X_DIG_CTRL1_BASE + 0xc4)
+#define WSA884X_DIG_DEBUG_EN (WSA884X_DIG_CTRL1_BASE + 0xc5)
+#define WSA884X_TADC_DETECT_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xc9)
+#define WSA884X_TADC_DEBUG_MSB (WSA884X_DIG_CTRL1_BASE + 0xca)
+#define WSA884X_TADC_DEBUG_LSB (WSA884X_DIG_CTRL1_BASE + 0xcb)
+#define WSA884X_SAMPLE_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcc)
+#define WSA884X_SWR_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcd)
+#define WSA884X_TEST_MODE_CTL (WSA884X_DIG_CTRL1_BASE + 0xce)
+#define WSA884X_IOPAD_CTL (WSA884X_DIG_CTRL1_BASE + 0xcf)
+#define WSA884X_ANA_CSR_DBG_ADD (WSA884X_DIG_CTRL1_BASE + 0xd0)
+#define WSA884X_ANA_CSR_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd1)
+#define WSA884X_CLK_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd2)
+#define WSA884X_SPARE_R (WSA884X_DIG_CTRL1_BASE + 0xf0)
+#define WSA884X_SPARE_0 (WSA884X_DIG_CTRL1_BASE + 0xf1)
+#define WSA884X_SPARE_1 (WSA884X_DIG_CTRL1_BASE + 0xf2)
+#define WSA884X_SPARE_2 (WSA884X_DIG_CTRL1_BASE + 0xf3)
+#define WSA884X_SCODE (WSA884X_DIG_CTRL1_BASE + 0xff)
+
+#define WSA884X_DIG_TRIM_BASE (WSA884X_BASE + 0x0800)
+#define WSA884X_DIG_TRIM_PAGE (WSA884X_DIG_TRIM_BASE + 0x00)
+#define WSA884X_OTP_REG_0 (WSA884X_DIG_TRIM_BASE + 0x80)
+#define WSA884X_OTP_ID_WSA8840 0x0
+#define WSA884X_OTP_ID_WSA8845 0x5
+#define WSA884X_OTP_ID_WSA8845H 0xc
+#define WSA884X_OTP_REG_0_ID_MASK 0x0f
+#define WSA884X_OTP_REG_1 (WSA884X_DIG_TRIM_BASE + 0x81)
+#define WSA884X_OTP_REG_2 (WSA884X_DIG_TRIM_BASE + 0x82)
+#define WSA884X_OTP_REG_3 (WSA884X_DIG_TRIM_BASE + 0x83)
+#define WSA884X_OTP_REG_4 (WSA884X_DIG_TRIM_BASE + 0x84)
+#define WSA884X_OTP_REG_5 (WSA884X_DIG_TRIM_BASE + 0x85)
+#define WSA884X_OTP_REG_6 (WSA884X_DIG_TRIM_BASE + 0x86)
+#define WSA884X_OTP_REG_7 (WSA884X_DIG_TRIM_BASE + 0x87)
+#define WSA884X_OTP_REG_8 (WSA884X_DIG_TRIM_BASE + 0x88)
+#define WSA884X_OTP_REG_9 (WSA884X_DIG_TRIM_BASE + 0x89)
+#define WSA884X_OTP_REG_10 (WSA884X_DIG_TRIM_BASE + 0x8a)
+#define WSA884X_OTP_REG_11 (WSA884X_DIG_TRIM_BASE + 0x8b)
+#define WSA884X_OTP_REG_12 (WSA884X_DIG_TRIM_BASE + 0x8c)
+#define WSA884X_OTP_REG_13 (WSA884X_DIG_TRIM_BASE + 0x8d)
+#define WSA884X_OTP_REG_14 (WSA884X_DIG_TRIM_BASE + 0x8e)
+#define WSA884X_OTP_REG_15 (WSA884X_DIG_TRIM_BASE + 0x8f)
+#define WSA884X_OTP_REG_16 (WSA884X_DIG_TRIM_BASE + 0x90)
+#define WSA884X_OTP_REG_17 (WSA884X_DIG_TRIM_BASE + 0x91)
+#define WSA884X_OTP_REG_18 (WSA884X_DIG_TRIM_BASE + 0x92)
+#define WSA884X_OTP_REG_19 (WSA884X_DIG_TRIM_BASE + 0x93)
+#define WSA884X_OTP_REG_20 (WSA884X_DIG_TRIM_BASE + 0x94)
+#define WSA884X_OTP_REG_21 (WSA884X_DIG_TRIM_BASE + 0x95)
+#define WSA884X_OTP_REG_22 (WSA884X_DIG_TRIM_BASE + 0x96)
+#define WSA884X_OTP_REG_23 (WSA884X_DIG_TRIM_BASE + 0x97)
+#define WSA884X_OTP_REG_24 (WSA884X_DIG_TRIM_BASE + 0x98)
+#define WSA884X_OTP_REG_25 (WSA884X_DIG_TRIM_BASE + 0x99)
+#define WSA884X_OTP_REG_26 (WSA884X_DIG_TRIM_BASE + 0x9a)
+#define WSA884X_OTP_REG_27 (WSA884X_DIG_TRIM_BASE + 0x9b)
+#define WSA884X_OTP_REG_28 (WSA884X_DIG_TRIM_BASE + 0x9c)
+#define WSA884X_OTP_REG_29 (WSA884X_DIG_TRIM_BASE + 0x9d)
+#define WSA884X_OTP_REG_30 (WSA884X_DIG_TRIM_BASE + 0x9e)
+#define WSA884X_OTP_REG_31 (WSA884X_DIG_TRIM_BASE + 0x9f)
+#define WSA884X_OTP_REG_32 (WSA884X_DIG_TRIM_BASE + 0xa0)
+#define WSA884X_OTP_REG_33 (WSA884X_DIG_TRIM_BASE + 0xa1)
+#define WSA884X_OTP_REG_34 (WSA884X_DIG_TRIM_BASE + 0xa2)
+#define WSA884X_OTP_REG_35 (WSA884X_DIG_TRIM_BASE + 0xa3)
+#define WSA884X_OTP_REG_36 (WSA884X_DIG_TRIM_BASE + 0xa4)
+#define WSA884X_OTP_REG_37 (WSA884X_DIG_TRIM_BASE + 0xa5)
+#define WSA884X_OTP_REG_38 (WSA884X_DIG_TRIM_BASE + 0xa6)
+#define WSA884X_OTP_REG_38_RESERVER_MASK 0xf0
+#define WSA884X_OTP_REG_38_RESERVER_SHIFT 4
+#define WSA884X_OTP_REG_38_BST_CFG_SEL_MASK 0x08
+#define WSA884X_OTP_REG_38_BST_CFG_SEL_SHIFT 3
+#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_MASK 0x07
+#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_SHIFT 0
+#define WSA884X_OTP_REG_39 (WSA884X_DIG_TRIM_BASE + 0xa7)
+#define WSA884X_OTP_REG_40 (WSA884X_DIG_TRIM_BASE + 0xa8)
+#define WSA884X_OTP_REG_40_SPARE_TYPE2_MASK 0xc0
+#define WSA884X_OTP_REG_40_SPARE_TYPE2_SHIFT 6
+#define WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK 0x3c
+#define WSA884X_OTP_REG_40_ISENSE_RESCAL_SHIFT 2
+#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_MASK 0x2
+#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_SHIFT 1
+#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_MASK 0x1
+#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_SHIFT 0
+#define WSA884X_OTP_REG_41 (WSA884X_DIG_TRIM_BASE + 0xa9)
+#define WSA884X_OTP_REG_63 (WSA884X_DIG_TRIM_BASE + 0xbf)
+
+#define WSA884X_DIG_EMEM_BASE (WSA884X_BASE + 0x08C0)
+#define WSA884X_EMEM_0 (WSA884X_DIG_EMEM_BASE + 0x00)
+#define WSA884X_EMEM_1 (WSA884X_DIG_EMEM_BASE + 0x01)
+#define WSA884X_EMEM_2 (WSA884X_DIG_EMEM_BASE + 0x02)
+#define WSA884X_EMEM_3 (WSA884X_DIG_EMEM_BASE + 0x03)
+#define WSA884X_EMEM_4 (WSA884X_DIG_EMEM_BASE + 0x04)
+#define WSA884X_EMEM_5 (WSA884X_DIG_EMEM_BASE + 0x05)
+#define WSA884X_EMEM_6 (WSA884X_DIG_EMEM_BASE + 0x06)
+#define WSA884X_EMEM_7 (WSA884X_DIG_EMEM_BASE + 0x07)
+#define WSA884X_EMEM_8 (WSA884X_DIG_EMEM_BASE + 0x08)
+#define WSA884X_EMEM_9 (WSA884X_DIG_EMEM_BASE + 0x09)
+#define WSA884X_EMEM_10 (WSA884X_DIG_EMEM_BASE + 0x0a)
+#define WSA884X_EMEM_11 (WSA884X_DIG_EMEM_BASE + 0x0b)
+#define WSA884X_EMEM_12 (WSA884X_DIG_EMEM_BASE + 0x0c)
+#define WSA884X_EMEM_13 (WSA884X_DIG_EMEM_BASE + 0x0d)
+#define WSA884X_EMEM_14 (WSA884X_DIG_EMEM_BASE + 0x0e)
+#define WSA884X_EMEM_15 (WSA884X_DIG_EMEM_BASE + 0x0f)
+#define WSA884X_EMEM_16 (WSA884X_DIG_EMEM_BASE + 0x10)
+#define WSA884X_EMEM_17 (WSA884X_DIG_EMEM_BASE + 0x11)
+#define WSA884X_EMEM_18 (WSA884X_DIG_EMEM_BASE + 0x12)
+#define WSA884X_EMEM_19 (WSA884X_DIG_EMEM_BASE + 0x13)
+#define WSA884X_EMEM_20 (WSA884X_DIG_EMEM_BASE + 0x14)
+#define WSA884X_EMEM_21 (WSA884X_DIG_EMEM_BASE + 0x15)
+#define WSA884X_EMEM_22 (WSA884X_DIG_EMEM_BASE + 0x16)
+#define WSA884X_EMEM_23 (WSA884X_DIG_EMEM_BASE + 0x17)
+#define WSA884X_EMEM_24 (WSA884X_DIG_EMEM_BASE + 0x18)
+#define WSA884X_EMEM_25 (WSA884X_DIG_EMEM_BASE + 0x19)
+#define WSA884X_EMEM_26 (WSA884X_DIG_EMEM_BASE + 0x1a)
+#define WSA884X_EMEM_27 (WSA884X_DIG_EMEM_BASE + 0x1b)
+#define WSA884X_EMEM_28 (WSA884X_DIG_EMEM_BASE + 0x1c)
+#define WSA884X_EMEM_29 (WSA884X_DIG_EMEM_BASE + 0x1d)
+#define WSA884X_EMEM_30 (WSA884X_DIG_EMEM_BASE + 0x1e)
+#define WSA884X_EMEM_31 (WSA884X_DIG_EMEM_BASE + 0x1f)
+#define WSA884X_EMEM_32 (WSA884X_DIG_EMEM_BASE + 0x20)
+#define WSA884X_EMEM_33 (WSA884X_DIG_EMEM_BASE + 0x21)
+#define WSA884X_EMEM_34 (WSA884X_DIG_EMEM_BASE + 0x22)
+#define WSA884X_EMEM_35 (WSA884X_DIG_EMEM_BASE + 0x23)
+#define WSA884X_EMEM_36 (WSA884X_DIG_EMEM_BASE + 0x24)
+#define WSA884X_EMEM_37 (WSA884X_DIG_EMEM_BASE + 0x25)
+#define WSA884X_EMEM_38 (WSA884X_DIG_EMEM_BASE + 0x26)
+#define WSA884X_EMEM_39 (WSA884X_DIG_EMEM_BASE + 0x27)
+#define WSA884X_EMEM_40 (WSA884X_DIG_EMEM_BASE + 0x28)
+#define WSA884X_EMEM_41 (WSA884X_DIG_EMEM_BASE + 0x29)
+#define WSA884X_EMEM_42 (WSA884X_DIG_EMEM_BASE + 0x2a)
+#define WSA884X_EMEM_43 (WSA884X_DIG_EMEM_BASE + 0x2b)
+#define WSA884X_EMEM_44 (WSA884X_DIG_EMEM_BASE + 0x2c)
+#define WSA884X_EMEM_45 (WSA884X_DIG_EMEM_BASE + 0x2d)
+#define WSA884X_EMEM_46 (WSA884X_DIG_EMEM_BASE + 0x2e)
+#define WSA884X_EMEM_47 (WSA884X_DIG_EMEM_BASE + 0x2f)
+#define WSA884X_EMEM_48 (WSA884X_DIG_EMEM_BASE + 0x30)
+#define WSA884X_EMEM_49 (WSA884X_DIG_EMEM_BASE + 0x31)
+#define WSA884X_EMEM_50 (WSA884X_DIG_EMEM_BASE + 0x32)
+#define WSA884X_EMEM_51 (WSA884X_DIG_EMEM_BASE + 0x33)
+#define WSA884X_EMEM_52 (WSA884X_DIG_EMEM_BASE + 0x34)
+#define WSA884X_EMEM_53 (WSA884X_DIG_EMEM_BASE + 0x35)
+#define WSA884X_EMEM_54 (WSA884X_DIG_EMEM_BASE + 0x36)
+#define WSA884X_EMEM_55 (WSA884X_DIG_EMEM_BASE + 0x37)
+#define WSA884X_EMEM_56 (WSA884X_DIG_EMEM_BASE + 0x38)
+#define WSA884X_EMEM_57 (WSA884X_DIG_EMEM_BASE + 0x39)
+#define WSA884X_EMEM_58 (WSA884X_DIG_EMEM_BASE + 0x3a)
+#define WSA884X_EMEM_59 (WSA884X_DIG_EMEM_BASE + 0x3b)
+#define WSA884X_EMEM_60 (WSA884X_DIG_EMEM_BASE + 0x3c)
+#define WSA884X_EMEM_61 (WSA884X_DIG_EMEM_BASE + 0x3d)
+#define WSA884X_EMEM_62 (WSA884X_DIG_EMEM_BASE + 0x3e)
+#define WSA884X_EMEM_63 (WSA884X_DIG_EMEM_BASE + 0x3f)
+
+#define WSA884X_NUM_REGISTERS (WSA884X_EMEM_63 + 1)
+#define WSA884X_MAX_REGISTER (WSA884X_NUM_REGISTERS - 1)
+
+#define WSA884X_SUPPLIES_NUM 2
+#define WSA884X_MAX_SWR_PORTS 6
+#define WSA884X_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 WSA884X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define WSA884X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct wsa884x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM];
+ struct sdw_slave *slave;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS];
+ struct gpio_desc *sd_n;
+ bool port_prepared[WSA884X_MAX_SWR_PORTS];
+ bool port_enable[WSA884X_MAX_SWR_PORTS];
+ unsigned int variant;
+ int active_ports;
+ int dev_mode;
+ bool hw_init;
+};
+
+enum {
+ COMP_OFFSET0,
+ COMP_OFFSET1,
+ COMP_OFFSET2,
+ COMP_OFFSET3,
+ COMP_OFFSET4,
+};
+
+enum wsa884x_gain {
+ G_21_DB = 0,
+ G_19P5_DB,
+ G_18_DB,
+ G_16P5_DB,
+ G_15_DB,
+ G_13P5_DB,
+ G_12_DB,
+ G_10P5_DB,
+ G_9_DB,
+ G_7P5_DB,
+ G_6_DB,
+ G_4P5_DB,
+ G_3_DB,
+ G_1P5_DB,
+ G_0_DB,
+ G_M1P5_DB,
+ G_M3_DB,
+ G_M4P5_DB,
+ G_M6_DB,
+ G_MAX_DB,
+};
+
+enum wsa884x_isense {
+ ISENSE_6_DB = 0,
+ ISENSE_12_DB,
+ ISENSE_15_DB,
+ ISENSE_18_DB,
+};
+
+enum wsa884x_vsense {
+ VSENSE_M12_DB = 0,
+ VSENSE_M15_DB,
+ VSENSE_M18_DB,
+ VSENSE_M21_DB,
+ VSENSE_M24_DB,
+};
+
+enum wsa884x_port_ids {
+ WSA884X_PORT_DAC,
+ WSA884X_PORT_COMP,
+ WSA884X_PORT_BOOST,
+ WSA884X_PORT_PBR,
+ WSA884X_PORT_VISENSE,
+ WSA884X_PORT_CPS,
+};
+
+static const char * const wsa884x_supply_name[] = {
+ "vdd-io",
+ "vdd-1p8",
+};
+
+static const char * const wsa884x_dev_mode_text[] = {
+ "Speaker", "Receiver"
+};
+
+enum wsa884x_mode {
+ WSA884X_SPEAKER,
+ WSA884X_RECEIVER,
+};
+
+static const struct soc_enum wsa884x_dev_mode_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text);
+
+static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
+ {
+ .num = WSA884X_PORT_DAC + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }, {
+ .num = WSA884X_PORT_COMP + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }, {
+ .num = WSA884X_PORT_BOOST + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }, {
+ .num = WSA884X_PORT_PBR + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }, {
+ .num = WSA884X_PORT_VISENSE + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }, {
+ .num = WSA884X_PORT_CPS + 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 1,
+ .simple_ch_prep_sm = true,
+ .read_only_wordlength = true,
+ }
+};
+
+static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
+ {
+ .num = WSA884X_PORT_DAC + 1,
+ .ch_mask = 0x1,
+ }, {
+ .num = WSA884X_PORT_COMP + 1,
+ .ch_mask = 0xf,
+ }, {
+ .num = WSA884X_PORT_BOOST + 1,
+ .ch_mask = 0x3,
+ }, {
+ .num = WSA884X_PORT_PBR + 1,
+ .ch_mask = 0x1,
+ }, {
+ .num = WSA884X_PORT_VISENSE + 1,
+ .ch_mask = 0x3,
+ }, {
+ .num = WSA884X_PORT_CPS + 1,
+ .ch_mask = 0x3,
+ },
+};
+
+static struct reg_default wsa884x_defaults[] = {
+ { WSA884X_BG_CTRL, 0xa5 },
+ { WSA884X_ADC_CTRL, 0x00 },
+ { WSA884X_BOP1_PROG, 0x22 },
+ { WSA884X_BOP2_PROG, 0x44 },
+ { WSA884X_UVLO_PROG, 0x99 },
+ { WSA884X_UVLO_PROG1, 0x70 },
+ { WSA884X_SPARE_CTRL_0, 0x00 },
+ { WSA884X_SPARE_CTRL_1, 0x00 },
+ { WSA884X_SPARE_CTRL_2, 0x00 },
+ { WSA884X_SPARE_CTRL_3, 0x00 },
+ { WSA884X_REF_CTRL, 0xd2 },
+ { WSA884X_BG_TEST_CTL, 0x06 },
+ { WSA884X_BG_BIAS, 0xd7 },
+ { WSA884X_ADC_PROG, 0x08 },
+ { WSA884X_ADC_IREF_CTL, 0x57 },
+ { WSA884X_ADC_ISENS_CTL, 0x47 },
+ { WSA884X_ADC_CLK_CTL, 0x87 },
+ { WSA884X_ADC_TEST_CTL, 0x00 },
+ { WSA884X_ADC_BIAS, 0x51 },
+ { WSA884X_VBAT_SNS, 0xa0 },
+ { WSA884X_BOP_ATEST_SEL, 0x00 },
+ { WSA884X_MISC0, 0x04 },
+ { WSA884X_MISC1, 0x75 },
+ { WSA884X_MISC2, 0x00 },
+ { WSA884X_MISC3, 0x10 },
+ { WSA884X_SPARE_TSBG_0, 0x00 },
+ { WSA884X_SPARE_TUNE_0, 0x00 },
+ { WSA884X_SPARE_TUNE_1, 0x00 },
+ { WSA884X_VSENSE1, 0xe7 },
+ { WSA884X_ISENSE2, 0x27 },
+ { WSA884X_SPARE_CTL_1, 0x00 },
+ { WSA884X_SPARE_CTL_2, 0x00 },
+ { WSA884X_SPARE_CTL_3, 0x00 },
+ { WSA884X_SPARE_CTL_4, 0x00 },
+ { WSA884X_EN, 0x10 },
+ { WSA884X_OVERRIDE1, 0x00 },
+ { WSA884X_OVERRIDE2, 0x08 },
+ { WSA884X_ISENSE1, 0xd4 },
+ { WSA884X_ISENSE_CAL, 0x00 },
+ { WSA884X_MISC, 0x00 },
+ { WSA884X_ADC_0, 0x00 },
+ { WSA884X_ADC_1, 0x00 },
+ { WSA884X_ADC_2, 0x40 },
+ { WSA884X_ADC_3, 0x80 },
+ { WSA884X_ADC_4, 0x25 },
+ { WSA884X_ADC_5, 0x24 },
+ { WSA884X_ADC_6, 0x0a },
+ { WSA884X_ADC_7, 0x81 },
+ { WSA884X_IVSENSE_SPARE_TUNE_1, 0x00 },
+ { WSA884X_SPARE_TUNE_2, 0x00 },
+ { WSA884X_SPARE_TUNE_3, 0x00 },
+ { WSA884X_SPARE_TUNE_4, 0x00 },
+ { WSA884X_TOP_CTRL1, 0xd3 },
+ { WSA884X_CLIP_DET_CTRL1, 0x7e },
+ { WSA884X_CLIP_DET_CTRL2, 0x4c },
+ { WSA884X_DAC_CTRL1, 0xa4 },
+ { WSA884X_DAC_VCM_CTRL_REG1, 0x02 },
+ { WSA884X_DAC_VCM_CTRL_REG2, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG3, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG4, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG5, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG6, 0x00 },
+ { WSA884X_PWM_CLK_CTL, 0x20 },
+ { WSA884X_DRV_LF_LDO_SEL, 0xaa },
+ { WSA884X_OCP_CTL, 0xc6 },
+ { WSA884X_PDRV_HS_CTL, 0x52 },
+ { WSA884X_PDRV_LS_CTL, 0x4a },
+ { WSA884X_SPK_TOP_SPARE_CTL_1, 0x00 },
+ { WSA884X_SPK_TOP_SPARE_CTL_2, 0x00 },
+ { WSA884X_SPK_TOP_SPARE_CTL_3, 0x00 },
+ { WSA884X_SPK_TOP_SPARE_CTL_4, 0x00 },
+ { WSA884X_SPARE_CTL_5, 0x00 },
+ { WSA884X_DAC_EN_DEBUG_REG, 0x00 },
+ { WSA884X_DAC_OPAMP_BIAS1_REG, 0x48 },
+ { WSA884X_DAC_OPAMP_BIAS2_REG, 0x48 },
+ { WSA884X_DAC_TUNE1, 0x02 },
+ { WSA884X_DAC_VOLTAGE_CTRL_REG, 0x05 },
+ { WSA884X_ATEST1_REG, 0x00 },
+ { WSA884X_ATEST2_REG, 0x00 },
+ { WSA884X_TOP_BIAS_REG1, 0x6a },
+ { WSA884X_TOP_BIAS_REG2, 0x65 },
+ { WSA884X_TOP_BIAS_REG3, 0x55 },
+ { WSA884X_TOP_BIAS_REG4, 0xa9 },
+ { WSA884X_PWRSTG_DBG2, 0x21 },
+ { WSA884X_DRV_LF_BLK_EN, 0x0f },
+ { WSA884X_DRV_LF_EN, 0x0a },
+ { WSA884X_DRV_LF_MASK_DCC_CTL, 0x08 },
+ { WSA884X_DRV_LF_MISC_CTL1, 0x30 },
+ { WSA884X_DRV_LF_REG_GAIN, 0x00 },
+ { WSA884X_DRV_OS_CAL_CTL, 0x00 },
+ { WSA884X_DRV_OS_CAL_CTL1, 0x90 },
+ { WSA884X_PWRSTG_DBG, 0x08 },
+ { WSA884X_BBM_CTL, 0x92 },
+ { WSA884X_TOP_MISC1, 0x00 },
+ { WSA884X_DAC_VCM_CTRL_REG7, 0x00 },
+ { WSA884X_TOP_BIAS_REG5, 0x15 },
+ { WSA884X_DRV_LF_MISC_CTL2, 0x00 },
+ { WSA884X_STB_CTRL1, 0x42 },
+ { WSA884X_CURRENT_LIMIT, 0x54 },
+ { WSA884X_BYP_CTRL1, 0x01 },
+ { WSA884X_SPARE_CTL_0, 0x00 },
+ { WSA884X_BOOST_SPARE_CTL_1, 0x00 },
+ { WSA884X_IBIAS1, 0x00 },
+ { WSA884X_IBIAS2, 0x00 },
+ { WSA884X_IBIAS3, 0x00 },
+ { WSA884X_EN_CTRL, 0x42 },
+ { WSA884X_STB_CTRL2, 0x03 },
+ { WSA884X_STB_CTRL3, 0x3c },
+ { WSA884X_STB_CTRL4, 0x30 },
+ { WSA884X_BYP_CTRL2, 0x97 },
+ { WSA884X_BYP_CTRL3, 0x11 },
+ { WSA884X_ZX_CTRL1, 0xf0 },
+ { WSA884X_ZX_CTRL2, 0x04 },
+ { WSA884X_BLEEDER_CTRL, 0x04 },
+ { WSA884X_BOOST_MISC, 0x62 },
+ { WSA884X_PWRSTAGE_CTRL1, 0x00 },
+ { WSA884X_PWRSTAGE_CTRL2, 0x31 },
+ { WSA884X_PWRSTAGE_CTRL3, 0x81 },
+ { WSA884X_PWRSTAGE_CTRL4, 0x5f },
+ { WSA884X_MAXD_REG1, 0x00 },
+ { WSA884X_MAXD_REG2, 0x5b },
+ { WSA884X_ILIM_CTRL1, 0xe2 },
+ { WSA884X_ILIM_CTRL2, 0x90 },
+ { WSA884X_TEST_CTRL1, 0x00 },
+ { WSA884X_TEST_CTRL2, 0x00 },
+ { WSA884X_SPARE1, 0x00 },
+ { WSA884X_BOOT_CAP_CHECK, 0x01 },
+ { WSA884X_PON_CTL_0, 0x12 },
+ { WSA884X_PWRSAV_CTL, 0xaa },
+ { WSA884X_PON_LDOL_SPARE_CTL_0, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_CTL_1, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_CTL_2, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_CTL_3, 0x00 },
+ { WSA884X_PON_CLT_1, 0xe1 },
+ { WSA884X_PON_CTL_2, 0x00 },
+ { WSA884X_PON_CTL_3, 0x70 },
+ { WSA884X_CKWD_CTL_0, 0x14 },
+ { WSA884X_CKWD_CTL_1, 0x3b },
+ { WSA884X_CKWD_CTL_2, 0x00 },
+ { WSA884X_CKSK_CTL_0, 0x00 },
+ { WSA884X_PADSW_CTL_0, 0x00 },
+ { WSA884X_TEST_0, 0x00 },
+ { WSA884X_TEST_1, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_0, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_1, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_2, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_3, 0x00 },
+ { WSA884X_PON_LDOL_SPARE_TUNE_4, 0x00 },
+ { WSA884X_DIG_CTRL0_PAGE, 0x00 },
+ { WSA884X_CDC_RST_CTL, 0x01 },
+ { WSA884X_SWR_RESET_EN, 0x00 },
+ { WSA884X_TOP_CLK_CFG, 0x00 },
+ { WSA884X_SWR_CLK_RATE, 0x00 },
+ { WSA884X_CDC_PATH_MODE, 0x00 },
+ { WSA884X_CDC_CLK_CTL, 0x1f },
+ { WSA884X_PA_FSM_EN, 0x00 },
+ { WSA884X_PA_FSM_CTL0, 0x00 },
+ { WSA884X_PA_FSM_CTL1, 0xfe },
+ { WSA884X_PA_FSM_TIMER0, 0x80 },
+ { WSA884X_PA_FSM_TIMER1, 0x80 },
+ { WSA884X_PA_FSM_ERR_CTL, 0x00 },
+ { WSA884X_PA_FSM_MSK0, 0x00 },
+ { WSA884X_PA_FSM_MSK1, 0x00 },
+ { WSA884X_PA_FSM_BYP_CTL, 0x00 },
+ { WSA884X_PA_FSM_BYP0, 0x00 },
+ { WSA884X_PA_FSM_BYP1, 0x00 },
+ { WSA884X_TADC_VALUE_CTL, 0x03 },
+ { WSA884X_TEMP_DETECT_CTL, 0x01 },
+ { WSA884X_TEMP_CONFIG0, 0x00 },
+ { WSA884X_TEMP_CONFIG1, 0x00 },
+ { WSA884X_VBAT_THRM_FLT_CTL, 0x7f },
+ { WSA884X_VBAT_CAL_CTL, 0x01 },
+ { WSA884X_UVLO_DEGLITCH_CTL, 0x05 },
+ { WSA884X_BOP_DEGLITCH_CTL, 0x05 },
+ { WSA884X_VBAT_ZONE_DETC_CTL, 0x31 },
+ { WSA884X_CPS_CTL, 0x00 },
+ { WSA884X_CDC_RX_CTL, 0xfe },
+ { WSA884X_CDC_SPK_DSM_A1_0, 0x00 },
+ { WSA884X_CDC_SPK_DSM_A1_1, 0x01 },
+ { WSA884X_CDC_SPK_DSM_A2_0, 0x96 },
+ { WSA884X_CDC_SPK_DSM_A2_1, 0x09 },
+ { WSA884X_CDC_SPK_DSM_A3_0, 0xab },
+ { WSA884X_CDC_SPK_DSM_A3_1, 0x05 },
+ { WSA884X_CDC_SPK_DSM_A4_0, 0x1c },
+ { WSA884X_CDC_SPK_DSM_A4_1, 0x02 },
+ { WSA884X_CDC_SPK_DSM_A5_0, 0x17 },
+ { WSA884X_CDC_SPK_DSM_A5_1, 0x02 },
+ { WSA884X_CDC_SPK_DSM_A6_0, 0xaa },
+ { WSA884X_CDC_SPK_DSM_A7_0, 0xe3 },
+ { WSA884X_CDC_SPK_DSM_C_0, 0x69 },
+ { WSA884X_CDC_SPK_DSM_C_1, 0x54 },
+ { WSA884X_CDC_SPK_DSM_C_2, 0x02 },
+ { WSA884X_CDC_SPK_DSM_C_3, 0x15 },
+ { WSA884X_CDC_SPK_DSM_R1, 0xa4 },
+ { WSA884X_CDC_SPK_DSM_R2, 0xb5 },
+ { WSA884X_CDC_SPK_DSM_R3, 0x86 },
+ { WSA884X_CDC_SPK_DSM_R4, 0x85 },
+ { WSA884X_CDC_SPK_DSM_R5, 0xaa },
+ { WSA884X_CDC_SPK_DSM_R6, 0xe2 },
+ { WSA884X_CDC_SPK_DSM_R7, 0x62 },
+ { WSA884X_CDC_SPK_GAIN_PDM_0, 0x00 },
+ { WSA884X_CDC_SPK_GAIN_PDM_1, 0xfc },
+ { WSA884X_CDC_SPK_GAIN_PDM_2, 0x05 },
+ { WSA884X_PDM_WD_CTL, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA0, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA1, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA2, 0x00 },
+ { WSA884X_DEM_BYPASS_DATA3, 0x00 },
+ { WSA884X_DRE_CTL_0, 0x70 },
+ { WSA884X_DRE_CTL_1, 0x04 },
+ { WSA884X_DRE_IDLE_DET_CTL, 0x2f },
+ { WSA884X_GAIN_RAMPING_CTL, 0x50 },
+ { WSA884X_GAIN_RAMPING_MIN, 0x12 },
+ { WSA884X_TAGC_CTL, 0x15 },
+ { WSA884X_TAGC_TIME, 0xbc },
+ { WSA884X_TAGC_FORCE_VAL, 0x00 },
+ { WSA884X_VAGC_CTL, 0x01 },
+ { WSA884X_VAGC_TIME, 0x0f },
+ { WSA884X_VAGC_ATTN_LVL_1, 0x03 },
+ { WSA884X_VAGC_ATTN_LVL_2, 0x06 },
+ { WSA884X_VAGC_ATTN_LVL_3, 0x09 },
+ { WSA884X_CLSH_CTL_0, 0x37 },
+ { WSA884X_CLSH_CTL_1, 0x81 },
+ { WSA884X_CLSH_V_HD_PA, 0x0c },
+ { WSA884X_CLSH_V_PA_MIN, 0x00 },
+ { WSA884X_CLSH_OVRD_VAL, 0x00 },
+ { WSA884X_CLSH_HARD_MAX, 0xff },
+ { WSA884X_CLSH_SOFT_MAX, 0xf5 },
+ { WSA884X_CLSH_SIG_DP, 0x00 },
+ { WSA884X_PBR_DELAY_CTL, 0x07 },
+ { WSA884X_CLSH_SRL_MAX_PBR, 0x02 },
+ { WSA884X_CLSH_VTH1, 0x00 },
+ { WSA884X_CLSH_VTH2, 0x00 },
+ { WSA884X_CLSH_VTH3, 0x00 },
+ { WSA884X_CLSH_VTH4, 0x00 },
+ { WSA884X_CLSH_VTH5, 0x00 },
+ { WSA884X_CLSH_VTH6, 0x00 },
+ { WSA884X_CLSH_VTH7, 0x00 },
+ { WSA884X_CLSH_VTH8, 0x00 },
+ { WSA884X_CLSH_VTH9, 0x00 },
+ { WSA884X_CLSH_VTH10, 0x00 },
+ { WSA884X_CLSH_VTH11, 0x00 },
+ { WSA884X_CLSH_VTH12, 0x00 },
+ { WSA884X_CLSH_VTH13, 0x00 },
+ { WSA884X_CLSH_VTH14, 0x00 },
+ { WSA884X_CLSH_VTH15, 0x00 },
+ { WSA884X_DIG_CTRL1_PAGE, 0x00 },
+ { WSA884X_PIN_CTL, 0x04 },
+ { WSA884X_PIN_CTL_OE, 0x00 },
+ { WSA884X_PIN_WDATA_IOPAD, 0x00 },
+ { WSA884X_I2C_SLAVE_CTL, 0x00 },
+ { WSA884X_SPMI_PAD_CTL0, 0x2f },
+ { WSA884X_SPMI_PAD_CTL1, 0x2f },
+ { WSA884X_SPMI_PAD_CTL2, 0x2f },
+ { WSA884X_MEM_CTL, 0x00 },
+ { WSA884X_SWR_HM_TEST0, 0x08 },
+ { WSA884X_OTP_CTRL0, 0x00 },
+ { WSA884X_OTP_CTRL2, 0x00 },
+ { WSA884X_OTP_PRG_TCSP0, 0x77 },
+ { WSA884X_OTP_PRG_TCSP1, 0x00 },
+ { WSA884X_OTP_PRG_TPPS, 0x47 },
+ { WSA884X_OTP_PRG_TVPS, 0x3b },
+ { WSA884X_OTP_PRG_TVPH, 0x47 },
+ { WSA884X_OTP_PRG_TPPR0, 0x47 },
+ { WSA884X_OTP_PRG_TPPR1, 0x00 },
+ { WSA884X_OTP_PRG_TPPH, 0x47 },
+ { WSA884X_OTP_PRG_END, 0x47 },
+ { WSA884X_WAVG_PLAY, 0x00 },
+ { WSA884X_WAVG_CTL, 0x06 },
+ { WSA884X_WAVG_LRA_PER_0, 0xd1 },
+ { WSA884X_WAVG_LRA_PER_1, 0x00 },
+ { WSA884X_WAVG_DELTA_THETA_0, 0xe6 },
+ { WSA884X_WAVG_DELTA_THETA_1, 0x04 },
+ { WSA884X_WAVG_DIRECT_AMP_0, 0x50 },
+ { WSA884X_WAVG_DIRECT_AMP_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP0_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP0_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP1_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP1_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP2_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP2_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP3_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP3_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP4_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP4_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP5_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP5_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP6_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP6_1, 0x00 },
+ { WSA884X_WAVG_PTRN_AMP7_0, 0x50 },
+ { WSA884X_WAVG_PTRN_AMP7_1, 0x00 },
+ { WSA884X_WAVG_PER_0_1, 0x88 },
+ { WSA884X_WAVG_PER_2_3, 0x88 },
+ { WSA884X_WAVG_PER_4_5, 0x88 },
+ { WSA884X_WAVG_PER_6_7, 0x88 },
+ { WSA884X_INTR_MODE, 0x00 },
+ { WSA884X_INTR_MASK0, 0x90 },
+ { WSA884X_INTR_MASK1, 0x00 },
+ { WSA884X_INTR_CLEAR0, 0x00 },
+ { WSA884X_INTR_CLEAR1, 0x00 },
+ { WSA884X_INTR_LEVEL0, 0x04 },
+ { WSA884X_INTR_LEVEL1, 0x00 },
+ { WSA884X_INTR_SET0, 0x00 },
+ { WSA884X_INTR_SET1, 0x00 },
+ { WSA884X_INTR_TEST0, 0x00 },
+ { WSA884X_INTR_TEST1, 0x00 },
+ { WSA884X_PDM_TEST_MODE, 0x00 },
+ { WSA884X_PA_FSM_DBG, 0x00 },
+ { WSA884X_DIG_DEBUG_MODE, 0x00 },
+ { WSA884X_DIG_DEBUG_SEL, 0x00 },
+ { WSA884X_DIG_DEBUG_EN, 0x00 },
+ { WSA884X_TADC_DETECT_DBG_CTL, 0x00 },
+ { WSA884X_TADC_DEBUG_MSB, 0x00 },
+ { WSA884X_TADC_DEBUG_LSB, 0x00 },
+ { WSA884X_SAMPLE_EDGE_SEL, 0x7f },
+ { WSA884X_SWR_EDGE_SEL, 0x00 },
+ { WSA884X_TEST_MODE_CTL, 0x05 },
+ { WSA884X_IOPAD_CTL, 0x00 },
+ { WSA884X_ANA_CSR_DBG_ADD, 0x00 },
+ { WSA884X_ANA_CSR_DBG_CTL, 0x12 },
+ { WSA884X_CLK_DBG_CTL, 0x00 },
+ { WSA884X_SPARE_0, 0x00 },
+ { WSA884X_SPARE_1, 0x00 },
+ { WSA884X_SPARE_2, 0x00 },
+ { WSA884X_SCODE, 0x00 },
+ { WSA884X_DIG_TRIM_PAGE, 0x00 },
+ { WSA884X_EMEM_0, 0x00 },
+ { WSA884X_EMEM_1, 0x00 },
+ { WSA884X_EMEM_2, 0x00 },
+ { WSA884X_EMEM_3, 0x00 },
+ { WSA884X_EMEM_4, 0x00 },
+ { WSA884X_EMEM_5, 0x00 },
+ { WSA884X_EMEM_6, 0x00 },
+ { WSA884X_EMEM_7, 0x00 },
+ { WSA884X_EMEM_8, 0x00 },
+ { WSA884X_EMEM_9, 0x00 },
+ { WSA884X_EMEM_10, 0x00 },
+ { WSA884X_EMEM_11, 0x00 },
+ { WSA884X_EMEM_12, 0x00 },
+ { WSA884X_EMEM_13, 0x00 },
+ { WSA884X_EMEM_14, 0x00 },
+ { WSA884X_EMEM_15, 0x00 },
+ { WSA884X_EMEM_16, 0x00 },
+ { WSA884X_EMEM_17, 0x00 },
+ { WSA884X_EMEM_18, 0x00 },
+ { WSA884X_EMEM_19, 0x00 },
+ { WSA884X_EMEM_20, 0x00 },
+ { WSA884X_EMEM_21, 0x00 },
+ { WSA884X_EMEM_22, 0x00 },
+ { WSA884X_EMEM_23, 0x00 },
+ { WSA884X_EMEM_24, 0x00 },
+ { WSA884X_EMEM_25, 0x00 },
+ { WSA884X_EMEM_26, 0x00 },
+ { WSA884X_EMEM_27, 0x00 },
+ { WSA884X_EMEM_28, 0x00 },
+ { WSA884X_EMEM_29, 0x00 },
+ { WSA884X_EMEM_30, 0x00 },
+ { WSA884X_EMEM_31, 0x00 },
+ { WSA884X_EMEM_32, 0x00 },
+ { WSA884X_EMEM_33, 0x00 },
+ { WSA884X_EMEM_34, 0x00 },
+ { WSA884X_EMEM_35, 0x00 },
+ { WSA884X_EMEM_36, 0x00 },
+ { WSA884X_EMEM_37, 0x00 },
+ { WSA884X_EMEM_38, 0x00 },
+ { WSA884X_EMEM_39, 0x00 },
+ { WSA884X_EMEM_40, 0x00 },
+ { WSA884X_EMEM_41, 0x00 },
+ { WSA884X_EMEM_42, 0x00 },
+ { WSA884X_EMEM_43, 0x00 },
+ { WSA884X_EMEM_44, 0x00 },
+ { WSA884X_EMEM_45, 0x00 },
+ { WSA884X_EMEM_46, 0x00 },
+ { WSA884X_EMEM_47, 0x00 },
+ { WSA884X_EMEM_48, 0x00 },
+ { WSA884X_EMEM_49, 0x00 },
+ { WSA884X_EMEM_50, 0x00 },
+ { WSA884X_EMEM_51, 0x00 },
+ { WSA884X_EMEM_52, 0x00 },
+ { WSA884X_EMEM_53, 0x00 },
+ { WSA884X_EMEM_54, 0x00 },
+ { WSA884X_EMEM_55, 0x00 },
+ { WSA884X_EMEM_56, 0x00 },
+ { WSA884X_EMEM_57, 0x00 },
+ { WSA884X_EMEM_58, 0x00 },
+ { WSA884X_EMEM_59, 0x00 },
+ { WSA884X_EMEM_60, 0x00 },
+ { WSA884X_EMEM_61, 0x00 },
+ { WSA884X_EMEM_62, 0x00 },
+ { WSA884X_EMEM_63, 0x00 },
+};
+
+static bool wsa884x_readonly_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA884X_DOUT_MSB:
+ case WSA884X_DOUT_LSB:
+ case WSA884X_STATUS:
+ case WSA884X_SPK_TOP_SPARE_TUNE_2:
+ case WSA884X_SPK_TOP_SPARE_TUNE_3:
+ case WSA884X_SPK_TOP_SPARE_TUNE_4:
+ case WSA884X_SPARE_TUNE_5:
+ case WSA884X_SPARE_TUNE_6:
+ case WSA884X_SPARE_TUNE_7:
+ case WSA884X_SPARE_TUNE_8:
+ case WSA884X_SPARE_TUNE_9:
+ case WSA884X_SPARE_TUNE_10:
+ case WSA884X_PA_STATUS0:
+ case WSA884X_PA_STATUS1:
+ case WSA884X_PA_STATUS2:
+ case WSA884X_PA_STATUS3:
+ case WSA884X_PA_STATUS4:
+ case WSA884X_PA_STATUS5:
+ case WSA884X_SPARE_RO_1:
+ case WSA884X_SPARE_RO_2:
+ case WSA884X_SPARE_RO_3:
+ case WSA884X_SPARE_RO_0:
+ case WSA884X_BOOST_SPARE_RO_1:
+ case WSA884X_STATUS_0:
+ case WSA884X_STATUS_1:
+ case WSA884X_CHIP_ID0:
+ case WSA884X_CHIP_ID1:
+ case WSA884X_CHIP_ID2:
+ case WSA884X_CHIP_ID3:
+ case WSA884X_BUS_ID:
+ case WSA884X_PA_FSM_STA0:
+ case WSA884X_PA_FSM_STA1:
+ case WSA884X_PA_FSM_ERR_COND0:
+ case WSA884X_PA_FSM_ERR_COND1:
+ case WSA884X_TEMP_DIN_MSB:
+ case WSA884X_TEMP_DIN_LSB:
+ case WSA884X_TEMP_DOUT_MSB:
+ case WSA884X_TEMP_DOUT_LSB:
+ case WSA884X_VBAT_DIN_MSB:
+ case WSA884X_VBAT_DIN_LSB:
+ case WSA884X_VBAT_DOUT_MSB:
+ case WSA884X_VBAT_DOUT_LSB:
+ case WSA884X_VBAT_CAL_MSB:
+ case WSA884X_VBAT_CAL_LSB:
+ case WSA884X_VPHX_SYS_EN_STATUS:
+ case WSA884X_PIN_STATUS:
+ case WSA884X_SWR_HM_TEST1:
+ case WSA884X_OTP_CTRL1:
+ case WSA884X_OTP_STAT:
+ case WSA884X_WAVG_STA:
+ case WSA884X_INTR_STATUS0:
+ case WSA884X_INTR_STATUS1:
+ case WSA884X_ATE_TEST_MODE:
+ case WSA884X_SPARE_R:
+ return true;
+ }
+ return false;
+}
+
+static bool wsa884x_writeable_register(struct device *dev, unsigned int reg)
+{
+ return !wsa884x_readonly_register(dev, reg);
+}
+
+static bool wsa884x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA884X_ANA_WO_CTL_0:
+ case WSA884X_ANA_WO_CTL_1:
+ return true;
+ }
+ return wsa884x_readonly_register(dev, reg);
+}
+
+static struct regmap_config wsa884x_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = wsa884x_defaults,
+ .max_register = WSA884X_MAX_REGISTER,
+ .num_reg_defaults = ARRAY_SIZE(wsa884x_defaults),
+ .volatile_reg = wsa884x_volatile_register,
+ .writeable_reg = wsa884x_writeable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .use_single_read = true,
+};
+
+static const struct reg_sequence wsa884x_reg_init[] = {
+ { WSA884X_BOP2_PROG, FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_VTH_MASK, 0x6) |
+ FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_HYST_MASK, 0x6) },
+ { WSA884X_REF_CTRL, (0xd2 & ~WSA884X_REF_CTRL_BG_RDY_SEL_MASK) |
+ FIELD_PREP_CONST(WSA884X_REF_CTRL_BG_RDY_SEL_MASK, 0x1) },
+ /*
+ * Downstream suggests for batteries different than 1-Stacked (1S):
+ * { WSA884X_TOP_CTRL1, 0xd3 & ~WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK },
+ */
+ { WSA884X_STB_CTRL1, (0x42 & ~WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK) |
+ FIELD_PREP_CONST(WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK, 0xd) },
+ { WSA884X_CURRENT_LIMIT, (0x54 & ~WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK) |
+ FIELD_PREP_CONST(WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, 0x9) },
+ { WSA884X_ZX_CTRL1, (0xf0 & ~WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK) |
+ FIELD_PREP_CONST(WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK, 0x3) },
+ { WSA884X_ILIM_CTRL1, (0xe2 & ~WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK) |
+ FIELD_PREP_CONST(WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK, 0x3) },
+ { WSA884X_CKWD_CTL_1, FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK, 0x0) |
+ FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK, 0x13) },
+ { WSA884X_PA_FSM_CTL1, (0xfe & ~WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK) |
+ FIELD_PREP_CONST(WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK, 0x4) }, /* == 0xfe */
+ { WSA884X_VBAT_THRM_FLT_CTL, (0x7f & ~WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK) |
+ FIELD_PREP_CONST(WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK, 0x4) },
+ { WSA884X_VBAT_CAL_CTL, FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_RESERVE_MASK, 0x2) |
+ FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK, 0x1) },
+ { WSA884X_BOP_DEGLITCH_CTL, FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK, 0x8) |
+ FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK, 0x1) },
+ { WSA884X_CDC_SPK_DSM_A2_0, 0x0a },
+ { WSA884X_CDC_SPK_DSM_A2_1, 0x08 },
+ { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 },
+ { WSA884X_CDC_SPK_DSM_A3_1, 0x07 },
+ { WSA884X_CDC_SPK_DSM_A4_0, 0x79 },
+ { WSA884X_CDC_SPK_DSM_A5_0, 0x0b },
+ { WSA884X_CDC_SPK_DSM_A6_0, 0x8a },
+ { WSA884X_CDC_SPK_DSM_A7_0, 0x9b },
+ { WSA884X_CDC_SPK_DSM_C_0, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK, 0x6) |
+ FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK, 0x8) },
+ { WSA884X_CDC_SPK_DSM_C_2, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK, 0xf) },
+ { WSA884X_CDC_SPK_DSM_C_3, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK, 0x20) },
+ { WSA884X_CDC_SPK_DSM_R1, 0x83 },
+ { WSA884X_CDC_SPK_DSM_R2, 0x7f },
+ { WSA884X_CDC_SPK_DSM_R3, 0x9d },
+ { WSA884X_CDC_SPK_DSM_R4, 0x82 },
+ { WSA884X_CDC_SPK_DSM_R5, 0x8b },
+ { WSA884X_CDC_SPK_DSM_R6, 0x9b },
+ { WSA884X_CDC_SPK_DSM_R7, 0x3f },
+ /* Speaker mode by default */
+ { WSA884X_DRE_CTL_0, FIELD_PREP_CONST(WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x7) },
+ { WSA884X_CLSH_CTL_0, (0x37 & ~WSA884X_CLSH_CTL_0_DLY_CODE_MASK) |
+ FIELD_PREP_CONST(WSA884X_CLSH_CTL_0_DLY_CODE_MASK, 0x6) },
+ /*
+ * WSA884X_CLSH_VTH values for speaker mode with G_21_DB system gain,
+ * battery 1S and rload 8 Ohms.
+ */
+ { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(863), },
+ { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(918), },
+ { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(980), },
+ { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(1043), },
+ { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(1098), },
+ { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(1137), },
+ { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1184), },
+ { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1239), },
+ { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1278), },
+ { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1380), },
+ { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1482), },
+ { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1584), },
+ { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1663), },
+ { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1780), },
+ { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000), },
+ { WSA884X_ANA_WO_CTL_1, 0x00 },
+ { WSA884X_OTP_REG_38, 0x00 },
+ { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) },
+};
+
+static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x)
+{
+ struct regmap *regmap = wsa884x->regmap;
+ unsigned int min_gain, igain, vgain, comp_offset;
+
+ /*
+ * Downstream sets gain parameters customized per boards per use-case.
+ * Choose here some sane values matching knowon users, like QRD8550
+ * board:.
+ *
+ * Values match here downstream:
+ * For WSA884X_RECEIVER - G_7P5_DB system gain
+ * For WSA884X_SPEAKER - G_21_DB system gain
+ */
+ if (wsa884x->dev_mode == WSA884X_RECEIVER) {
+ comp_offset = COMP_OFFSET4;
+ min_gain = G_M6_DB;
+ igain = ISENSE_18_DB;
+ vgain = VSENSE_M12_DB;
+ } else {
+ /* WSA884X_SPEAKER */
+ comp_offset = COMP_OFFSET0;
+ min_gain = G_0_DB;
+ igain = ISENSE_12_DB;
+ vgain = VSENSE_M24_DB;
+ }
+
+ regmap_update_bits(regmap, WSA884X_ISENSE2,
+ WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK,
+ FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain));
+ regmap_update_bits(regmap, WSA884X_VSENSE1,
+ WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK,
+ FIELD_PREP(WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, vgain));
+ regmap_update_bits(regmap, WSA884X_GAIN_RAMPING_MIN,
+ WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK,
+ FIELD_PREP(WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, min_gain));
+
+ if (wsa884x->port_enable[WSA884X_PORT_COMP]) {
+ regmap_update_bits(regmap, WSA884X_DRE_CTL_0,
+ WSA884X_DRE_CTL_0_OFFSET_MASK,
+ FIELD_PREP(WSA884X_DRE_CTL_0_OFFSET_MASK, comp_offset));
+
+ regmap_update_bits(regmap, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0));
+ } else {
+ regmap_update_bits(regmap, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x1));
+ }
+}
+
+static void wsa884x_init(struct wsa884x_priv *wsa884x)
+{
+ unsigned int wo_ctl_0;
+ unsigned int variant = 0;
+
+ if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant))
+ wsa884x->variant = variant & WSA884X_OTP_REG_0_ID_MASK;
+
+ regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init,
+ ARRAY_SIZE(wsa884x_reg_init));
+
+ wo_ctl_0 = 0xc;
+ wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK,
+ WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER);
+ /* Assume that compander is enabled by default unless it is haptics sku */
+ if (wsa884x->variant == WSA884X_OTP_ID_WSA8845H)
+ wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK,
+ WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB);
+ else
+ wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK,
+ WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB);
+ regmap_write(wsa884x->regmap, WSA884X_ANA_WO_CTL_0, wo_ctl_0);
+
+ wsa884x_set_gain_parameters(wsa884x);
+
+ wsa884x->hw_init = false;
+}
+
+static int wsa884x_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ if (status == SDW_SLAVE_UNATTACHED) {
+ wsa884x->hw_init = false;
+ regcache_cache_only(wsa884x->regmap, true);
+ regcache_mark_dirty(wsa884x->regmap);
+ return 0;
+ }
+
+ if (wsa884x->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ regcache_cache_only(wsa884x->regmap, false);
+ ret = regcache_sync(wsa884x->regmap);
+ if (ret < 0) {
+ dev_err(&slave->dev, "Cannot sync regmap cache\n");
+ return ret;
+ }
+
+ wsa884x_init(wsa884x);
+
+ return 0;
+}
+
+static int wsa884x_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev);
+
+ if (state == SDW_OPS_PORT_POST_PREP)
+ wsa884x->port_prepared[prepare_ch->num - 1] = true;
+ else
+ wsa884x->port_prepared[prepare_ch->num - 1] = false;
+
+ return 0;
+}
+
+static const struct sdw_slave_ops wsa884x_slave_ops = {
+ .update_status = wsa884x_update_status,
+ .port_prep = wsa884x_port_prep,
+};
+
+static int wsa884x_dev_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = wsa884x->dev_mode;
+
+ return 0;
+}
+
+static int wsa884x_dev_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component);
+
+ if (wsa884x->dev_mode == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wsa884x->dev_mode = ucontrol->value.enumerated.item[0];
+
+ return 1;
+}
+
+static int wsa884x_get_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ ucontrol->value.integer.value[0] = wsa884x->port_enable[portidx];
+
+ return 0;
+}
+
+static int wsa884x_set_swr_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ int portidx = mixer->reg;
+
+ if (ucontrol->value.integer.value[0]) {
+ if (wsa884x->port_enable[portidx])
+ return 0;
+
+ wsa884x->port_enable[portidx] = true;
+ } else {
+ if (!wsa884x->port_enable[portidx])
+ return 0;
+
+ wsa884x->port_enable[portidx] = false;
+ }
+
+ return 1;
+}
+
+static int wsa884x_codec_probe(struct snd_soc_component *comp)
+{
+ struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, wsa884x->regmap);
+
+ return 0;
+}
+
+static void wsa884x_spkr_post_pmu(struct snd_soc_component *component,
+ struct wsa884x_priv *wsa884x)
+{
+ unsigned int curr_limit, curr_ovrd_en;
+
+ wsa884x_set_gain_parameters(wsa884x);
+ if (wsa884x->dev_mode == WSA884X_RECEIVER) {
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_0,
+ WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x3);
+ snd_soc_component_write_field(component, WSA884X_CDC_PATH_MODE,
+ WSA884X_CDC_PATH_MODE_RXD_MODE_MASK,
+ 0x1);
+ snd_soc_component_write_field(component, WSA884X_PWM_CLK_CTL,
+ WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK,
+ 0x1);
+ } else {
+ /* WSA884X_SPEAKER */
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_0,
+ WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0xf);
+ }
+
+ if (wsa884x->port_enable[WSA884X_PORT_PBR]) {
+ curr_ovrd_en = 0x0;
+ curr_limit = 0x15;
+ } else {
+ curr_ovrd_en = 0x1;
+ if (wsa884x->dev_mode == WSA884X_RECEIVER)
+ curr_limit = 0x9;
+ else
+ curr_limit = 0x15;
+ }
+ snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT,
+ WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK,
+ curr_ovrd_en);
+ snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT,
+ WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK,
+ curr_limit);
+}
+
+static int wsa884x_spkr_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 wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wsa884x_spkr_post_pmu(component, wsa884x);
+
+ snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
+ WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
+ 0x1);
+ snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
+ WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
+ 0x1);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
+ WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
+ 0x0);
+ snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
+ WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
+ 0x0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wsa884x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_SPK("SPKR", wsa884x_spkr_event),
+};
+
+static const DECLARE_TLV_DB_SCALE(pa_gain, -900, 150, -900);
+
+static const struct snd_kcontrol_new wsa884x_snd_controls[] = {
+ SOC_SINGLE_RANGE_TLV("PA Volume", WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT,
+ 0x0, 0x1f, 1, pa_gain),
+ SOC_ENUM_EXT("WSA MODE", wsa884x_dev_mode_enum,
+ wsa884x_dev_mode_get, wsa884x_dev_mode_put),
+ SOC_SINGLE_EXT("DAC Switch", WSA884X_PORT_DAC, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("COMP Switch", WSA884X_PORT_COMP, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("BOOST Switch", WSA884X_PORT_BOOST, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("PBR Switch", WSA884X_PORT_PBR, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("VISENSE Switch", WSA884X_PORT_VISENSE, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+ SOC_SINGLE_EXT("CPS Switch", WSA884X_PORT_CPS, 0, 1, 0,
+ wsa884x_get_swr_port, wsa884x_set_swr_port),
+};
+
+static const struct snd_soc_dapm_route wsa884x_audio_map[] = {
+ {"SPKR", NULL, "IN"},
+};
+
+static const struct snd_soc_component_driver wsa884x_component_drv = {
+ .name = "WSA884x",
+ .probe = wsa884x_codec_probe,
+ .controls = wsa884x_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa884x_snd_controls),
+ .dapm_widgets = wsa884x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa884x_dapm_widgets),
+ .dapm_routes = wsa884x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map),
+};
+
+static int wsa884x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+ int i;
+
+ wsa884x->active_ports = 0;
+ for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) {
+ if (!wsa884x->port_enable[i])
+ continue;
+
+ wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i];
+ wsa884x->active_ports++;
+ }
+
+ wsa884x->sconfig.frame_rate = params_rate(params);
+
+ return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig,
+ wsa884x->port_config, wsa884x->active_ports,
+ wsa884x->sruntime);
+}
+
+static int wsa884x_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+
+ sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime);
+
+ return 0;
+}
+
+static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute) {
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ 0x0);
+ snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
+ WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
+ 0x0);
+
+ } else {
+ snd_soc_component_write_field(component, WSA884X_DRE_CTL_1,
+ WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK,
+ 0x1);
+ snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
+ WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
+ 0x1);
+ }
+
+ return 0;
+}
+
+static int wsa884x_set_stream(struct snd_soc_dai *dai,
+ void *stream, int direction)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+
+ wsa884x->sruntime = stream;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wsa884x_dai_ops = {
+ .hw_params = wsa884x_hw_params,
+ .hw_free = wsa884x_hw_free,
+ .mute_stream = wsa884x_mute_stream,
+ .set_stream = wsa884x_set_stream,
+};
+
+static struct snd_soc_dai_driver wsa884x_dais[] = {
+ {
+ .name = "SPKR",
+ .playback = {
+ .stream_name = "SPKR Playback",
+ .rates = WSA884X_RATES | WSA884X_FRAC_RATES,
+ .formats = WSA884X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &wsa884x_dai_ops,
+ },
+};
+
+static void wsa884x_gpio_powerdown(void *data)
+{
+ gpiod_direction_output(data, 1);
+}
+
+static void wsa884x_regulator_disable(void *data)
+{
+ regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data);
+}
+
+static int wsa884x_probe(struct sdw_slave *pdev,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct wsa884x_priv *wsa884x;
+ unsigned int i;
+ int ret;
+
+ wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL);
+ if (!wsa884x)
+ return -ENOMEM;
+
+ for (i = 0; i < WSA884X_SUPPLIES_NUM; i++)
+ wsa884x->supplies[i].supply = wsa884x_supply_name[i];
+
+ ret = devm_regulator_bulk_get(dev, WSA884X_SUPPLIES_NUM,
+ wsa884x->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(WSA884X_SUPPLIES_NUM, wsa884x->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(dev, wsa884x_regulator_disable,
+ wsa884x->supplies);
+ if (ret)
+ return ret;
+
+ wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wsa884x->sd_n))
+ return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n),
+ "Shutdown Control GPIO not found\n");
+
+ dev_set_drvdata(dev, wsa884x);
+ wsa884x->slave = pdev;
+ wsa884x->dev = dev;
+ wsa884x->dev_mode = WSA884X_SPEAKER;
+ wsa884x->sconfig.ch_count = 1;
+ wsa884x->sconfig.bps = 1;
+ wsa884x->sconfig.direction = SDW_DATA_DIR_RX;
+ wsa884x->sconfig.type = SDW_STREAM_PDM;
+
+ pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0);
+ pdev->prop.simple_clk_stop_capable = true;
+ pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop;
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* Bring out of reset */
+ gpiod_direction_output(wsa884x->sd_n, 0);
+ ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n);
+ if (ret)
+ return ret;
+
+ wsa884x->regmap = devm_regmap_init_sdw(pdev, &wsa884x_regmap_config);
+ if (IS_ERR(wsa884x->regmap))
+ return dev_err_probe(dev, PTR_ERR(wsa884x->regmap),
+ "regmap_init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(wsa884x->regmap, true);
+ wsa884x->hw_init = true;
+
+ 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);
+
+ return devm_snd_soc_register_component(dev,
+ &wsa884x_component_drv,
+ wsa884x_dais,
+ ARRAY_SIZE(wsa884x_dais));
+}
+
+static int __maybe_unused wsa884x_runtime_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int __maybe_unused wsa884x_runtime_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wsa884x_pm_ops = {
+ SET_RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id wsa884x_swr_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x204, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id);
+
+static struct sdw_driver wsa884x_codec_driver = {
+ .driver = {
+ .name = "wsa884x-codec",
+ .pm = &wsa884x_pm_ops,
+ },
+ .probe = wsa884x_probe,
+ .ops = &wsa884x_slave_ops,
+ .id_table = wsa884x_swr_id,
+};
+module_sdw_driver(wsa884x_codec_driver);
+
+MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
+MODULE_DESCRIPTION("WSA884x codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c
index 399a489f24f2..97d652f0e84d 100644
--- a/sound/soc/dwc/dwc-i2s.c
+++ b/sound/soc/dwc/dwc-i2s.c
@@ -17,6 +17,7 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <sound/designware_i2s.h>
@@ -149,19 +150,51 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
+static void i2s_enable_dma(struct dw_i2s_dev *dev, u32 stream)
+{
+ u32 dma_reg = i2s_read_reg(dev->i2s_base, I2S_DMACR);
+
+ /* Enable DMA handshake for stream */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_reg |= I2S_DMAEN_TXBLOCK;
+ else
+ dma_reg |= I2S_DMAEN_RXBLOCK;
+
+ i2s_write_reg(dev->i2s_base, I2S_DMACR, dma_reg);
+}
+
+static void i2s_disable_dma(struct dw_i2s_dev *dev, u32 stream)
+{
+ u32 dma_reg = i2s_read_reg(dev->i2s_base, I2S_DMACR);
+
+ /* Disable DMA handshake for stream */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_reg &= ~I2S_DMAEN_TXBLOCK;
+ i2s_write_reg(dev->i2s_base, I2S_RTXDMA, 1);
+ } else {
+ dma_reg &= ~I2S_DMAEN_RXBLOCK;
+ i2s_write_reg(dev->i2s_base, I2S_RRXDMA, 1);
+ }
+ i2s_write_reg(dev->i2s_base, I2S_DMACR, dma_reg);
+}
+
static void i2s_start(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream)
{
struct i2s_clk_config_data *config = &dev->config;
i2s_write_reg(dev->i2s_base, IER, 1);
- i2s_enable_irqs(dev, substream->stream, config->chan_nr);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
i2s_write_reg(dev->i2s_base, ITER, 1);
else
i2s_write_reg(dev->i2s_base, IRER, 1);
+ if (dev->use_pio)
+ i2s_enable_irqs(dev, substream->stream, config->chan_nr);
+ else
+ i2s_enable_dma(dev, substream->stream);
+
i2s_write_reg(dev->i2s_base, CER, 1);
}
@@ -175,7 +208,10 @@ static void i2s_stop(struct dw_i2s_dev *dev,
else
i2s_write_reg(dev->i2s_base, IRER, 0);
- i2s_disable_irqs(dev, substream->stream, 8);
+ if (dev->use_pio)
+ i2s_disable_irqs(dev, substream->stream, 8);
+ else
+ i2s_disable_dma(dev, substream->stream);
if (!dev->active) {
i2s_write_reg(dev->i2s_base, CER, 0);
@@ -448,9 +484,9 @@ static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
static const u32 formats[COMP_MAX_WORDSIZE] = {
SNDRV_PCM_FMTBIT_S16_LE,
SNDRV_PCM_FMTBIT_S16_LE,
- SNDRV_PCM_FMTBIT_S24_LE,
- SNDRV_PCM_FMTBIT_S24_LE,
- SNDRV_PCM_FMTBIT_S32_LE,
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
0,
0,
0
@@ -557,13 +593,9 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
- u32 idx = COMP1_APB_DATA_WIDTH(comp1);
u32 idx2;
int ret;
- if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
- return -EINVAL;
-
ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000);
if (ret < 0)
return ret;
@@ -573,7 +605,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
dev->capability |= DWC_I2S_PLAY;
dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
- dev->play_dma_data.dt.addr_width = bus_widths[idx];
dev->play_dma_data.dt.fifo_size = fifo_depth *
(fifo_width[idx2]) >> 8;
dev->play_dma_data.dt.maxburst = 16;
@@ -583,7 +614,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
dev->capability |= DWC_I2S_RECORD;
dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
- dev->capture_dma_data.dt.addr_width = bus_widths[idx];
dev->capture_dma_data.dt.fifo_size = fifo_depth *
(fifo_width[idx2] >> 8);
dev->capture_dma_data.dt.maxburst = 16;
@@ -625,6 +655,14 @@ static int dw_i2s_probe(struct platform_device *pdev)
if (IS_ERR(dev->i2s_base))
return PTR_ERR(dev->i2s_base);
+ dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
+ if (IS_ERR(dev->reset))
+ return PTR_ERR(dev->reset);
+
+ ret = reset_control_deassert(dev->reset);
+ if (ret)
+ return ret;
+
dev->dev = &pdev->dev;
irq = platform_get_irq_optional(pdev, 0);
@@ -633,7 +671,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
pdev->name, dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request irq\n");
- return ret;
+ goto err_assert_reset;
}
}
@@ -653,24 +691,27 @@ static int dw_i2s_probe(struct platform_device *pdev)
ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
}
if (ret < 0)
- return ret;
+ goto err_assert_reset;
if (dev->capability & DW_I2S_MASTER) {
if (pdata) {
dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
if (!dev->i2s_clk_cfg) {
dev_err(&pdev->dev, "no clock configure method\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_assert_reset;
}
}
dev->clk = devm_clk_get(&pdev->dev, clk_id);
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
+ if (IS_ERR(dev->clk)) {
+ ret = PTR_ERR(dev->clk);
+ goto err_assert_reset;
+ }
ret = clk_prepare_enable(dev->clk);
if (ret < 0)
- return ret;
+ goto err_assert_reset;
}
dev_set_drvdata(&pdev->dev, dev);
@@ -704,6 +745,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
err_clk_disable:
if (dev->capability & DW_I2S_MASTER)
clk_disable_unprepare(dev->clk);
+err_assert_reset:
+ reset_control_assert(dev->reset);
return ret;
}
@@ -714,6 +757,7 @@ static void dw_i2s_remove(struct platform_device *pdev)
if (dev->capability & DW_I2S_MASTER)
clk_disable_unprepare(dev->clk);
+ reset_control_assert(dev->reset);
pm_runtime_disable(&pdev->dev);
}
diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h
index 1c361eb6127e..ba4e397099be 100644
--- a/sound/soc/dwc/local.h
+++ b/sound/soc/dwc/local.h
@@ -53,6 +53,12 @@
#define I2S_COMP_VERSION 0x01F8
#define I2S_COMP_TYPE 0x01FC
+#define I2S_RRXDMA 0x01C4
+#define I2S_RTXDMA 0x01CC
+#define I2S_DMACR 0x0200
+#define I2S_DMAEN_RXBLOCK (1 << 16)
+#define I2S_DMAEN_TXBLOCK (1 << 17)
+
/*
* Component parameter register fields - define the I2S block's
* configuration.
@@ -89,6 +95,7 @@ union dw_i2s_snd_dma_data {
struct dw_i2s_dev {
void __iomem *i2s_base;
struct clk *clk;
+ struct reset_control *reset;
int active;
unsigned int capability;
unsigned int quirks;
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 40870668ee24..76b5bfc288fd 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -27,6 +27,7 @@
#include "../codecs/wm8960.h"
#include "../codecs/wm8994.h"
#include "../codecs/tlv320aic31xx.h"
+#include "../codecs/nau8822.h"
#define DRIVER_NAME "fsl-asoc-card"
@@ -47,6 +48,7 @@
* @pll_id: PLL id for set_pll()
*/
struct codec_priv {
+ struct clk *mclk;
unsigned long mclk_freq;
unsigned long free_freq;
u32 mclk_id;
@@ -60,6 +62,7 @@ struct codec_priv {
* @sysclk_dir: SYSCLK directions for set_sysclk()
* @sysclk_id: SYSCLK ids for set_sysclk()
* @slot_width: Slot width of each frame
+ * @slot_num: Number of slots of each frame
*
* Note: [1] for tx and [0] for rx
*/
@@ -68,6 +71,7 @@ struct cpu_priv {
u32 sysclk_dir[2];
u32 sysclk_id[2];
u32 slot_width;
+ u32 slot_num;
};
/**
@@ -189,7 +193,11 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
}
if (cpu_priv->slot_width) {
- ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2,
+ if (!cpu_priv->slot_num)
+ cpu_priv->slot_num = 2;
+
+ ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3,
+ cpu_priv->slot_num,
cpu_priv->slot_width);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set TDM slot for cpu dai\n");
@@ -524,6 +532,9 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
return ret;
}
+ if (!IS_ERR_OR_NULL(codec_priv->mclk))
+ clk_prepare_enable(codec_priv->mclk);
+
return 0;
}
@@ -686,6 +697,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->codec_priv.free_freq = priv->codec_priv.mclk_freq;
priv->card.dapm_routes = NULL;
priv->card.num_dapm_routes = 0;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) {
+ codec_dai_name = "nau8822-hifi";
+ priv->codec_priv.mclk_id = NAU8822_CLK_MCLK;
+ priv->codec_priv.fll_id = NAU8822_CLK_PLL;
+ priv->codec_priv.pll_id = NAU8822_CLK_PLL;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ if (codec_dev)
+ priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL);
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
ret = -EINVAL;
@@ -911,6 +930,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = {
{ .compatible = "fsl,imx-audio-wm8524", },
{ .compatible = "fsl,imx-audio-si476x", },
{ .compatible = "fsl,imx-audio-wm8958", },
+ { .compatible = "fsl,imx-audio-nau8822", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index e3105d48fb65..5e09f634c61b 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1407,7 +1407,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->cpu_dai_drv.symmetric_sample_bits = 0;
}
- if (of_property_read_bool(np, "fsl,sai-mclk-direction-output") &&
+ sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output");
+
+ if (sai->mclk_direction_output &&
of_device_is_compatible(np, "fsl,imx6ul-sai")) {
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
if (IS_ERR(gpr)) {
@@ -1450,7 +1452,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
dev_warn(dev, "Error reading SAI version: %d\n", ret);
/* Select MCLK direction */
- if (of_property_read_bool(np, "fsl,sai-mclk-direction-output") &&
+ if (sai->mclk_direction_output &&
sai->soc_data->max_register >= FSL_SAI_MCTL) {
regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
@@ -1569,6 +1571,17 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mm_data = {
.max_register = FSL_SAI_MCTL,
};
+static const struct fsl_sai_soc_data fsl_sai_imx8mn_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 8,
+ .flags = 0,
+ .max_register = FSL_SAI_MDIV,
+};
+
static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = {
.use_imx_pcm = true,
.use_edma = false,
@@ -1578,6 +1591,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = {
.pins = 8,
.flags = 0,
.max_register = FSL_SAI_MDIV,
+ .mclk_with_tere = true,
};
static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = {
@@ -1613,7 +1627,7 @@ static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,imx8mm-sai", .data = &fsl_sai_imx8mm_data },
{ .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data },
{ .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data },
- { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mp_data },
+ { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data },
{ .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data },
{ /* sentinel */ }
};
@@ -1678,6 +1692,10 @@ static int fsl_sai_runtime_resume(struct device *dev)
if (ret)
goto disable_rx_clk;
+ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output)
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs),
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+
return 0;
disable_rx_clk:
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index a53c4f0e25fa..8254c3547b87 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -231,6 +231,7 @@ struct fsl_sai_soc_data {
bool use_imx_pcm;
bool use_edma;
bool mclk0_is_mclk1;
+ bool mclk_with_tere;
unsigned int fifo_depth;
unsigned int pins;
unsigned int reg_offset;
@@ -288,6 +289,7 @@ struct fsl_sai {
bool synchronous[2];
struct fsl_sai_dl_cfg *dl_cfg;
unsigned int dl_cfg_cnt;
+ bool mclk_direction_output;
unsigned int mclk_id[2];
unsigned int mclk_streams;
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
index b2c5aca92c6b..0b58df56f4da 100644
--- a/sound/soc/fsl/imx-audmix.c
+++ b/sound/soc/fsl/imx-audmix.c
@@ -15,7 +15,6 @@
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <linux/pm_runtime.h>
#include "fsl_sai.h"
#include "fsl_audmix.h"
@@ -207,8 +206,8 @@ static int imx_audmix_probe(struct platform_device *pdev)
for (i = 0; i < num_dai; i++) {
struct snd_soc_dai_link_component *dlc;
- /* for CPU/Codec x 2 */
- dlc = devm_kcalloc(&pdev->dev, 4, sizeof(*dlc), GFP_KERNEL);
+ /* for CPU x 2 */
+ dlc = devm_kcalloc(&pdev->dev, 2, sizeof(*dlc), GFP_KERNEL);
if (!dlc)
return -ENOMEM;
@@ -228,6 +227,8 @@ static int imx_audmix_probe(struct platform_device *pdev)
dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
fe_name_pref, args.np->full_name + 1);
+ if (!dai_name)
+ return -ENOMEM;
dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
@@ -236,6 +237,8 @@ static int imx_audmix_probe(struct platform_device *pdev)
capture_dai_name =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, "CPU-Capture");
+ if (!capture_dai_name)
+ return -ENOMEM;
}
/*
@@ -244,7 +247,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
*/
priv->dai[i].cpus =
priv->dai[i].platforms = &dlc[0];
- priv->dai[i].codecs = &dlc[1];
+ priv->dai[i].codecs = &asoc_dummy_dlc;
priv->dai[i].num_cpus = 1;
priv->dai[i].num_codecs = 1;
@@ -252,8 +255,6 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->dai[i].name = dai_name;
priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
- priv->dai[i].codecs->dai_name = "snd-soc-dummy-dai";
- priv->dai[i].codecs->name = "snd-soc-dummy";
priv->dai[i].cpus->of_node = args.np;
priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev);
priv->dai[i].dynamic = 1;
@@ -269,16 +270,16 @@ static int imx_audmix_probe(struct platform_device *pdev)
"AUDMIX-Playback-%d", i);
be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"AUDMIX-Capture-%d", i);
+ if (!be_name || !be_pb || !be_cp)
+ return -ENOMEM;
- priv->dai[num_dai + i].cpus = &dlc[2];
- priv->dai[num_dai + i].codecs = &dlc[3];
+ priv->dai[num_dai + i].cpus = &dlc[1];
+ priv->dai[num_dai + i].codecs = &asoc_dummy_dlc;
priv->dai[num_dai + i].num_cpus = 1;
priv->dai[num_dai + i].num_codecs = 1;
priv->dai[num_dai + i].name = be_name;
- priv->dai[num_dai + i].codecs->dai_name = "snd-soc-dummy-dai";
- priv->dai[num_dai + i].codecs->name = "snd-soc-dummy";
priv->dai[num_dai + i].cpus->of_node = audmix_np;
priv->dai[num_dai + i].cpus->dai_name = be_name;
priv->dai[num_dai + i].no_pcm = 1;
@@ -293,6 +294,9 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->dapm_routes[i].source =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, "CPU-Playback");
+ if (!priv->dapm_routes[i].source)
+ return -ENOMEM;
+
priv->dapm_routes[i].sink = be_pb;
priv->dapm_routes[num_dai + i].source = be_pb;
priv->dapm_routes[num_dai + i].sink = be_cp;
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
index 64a4d7e9db60..356a0bc3b126 100644
--- a/sound/soc/fsl/imx-card.c
+++ b/sound/soc/fsl/imx-card.c
@@ -551,10 +551,10 @@ static int imx_card_parse_of(struct imx_card_data *data)
goto err;
}
- ret = of_parse_phandle_with_args(cpu, "sound-dai",
- "#sound-dai-cells", 0, &args);
+ ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0);
if (ret) {
- dev_err(card->dev, "%s: error getting cpu phandle\n", link->name);
+ dev_err_probe(card->dev, ret,
+ "%s: error getting cpu dai info\n", link->name);
goto err;
}
@@ -582,17 +582,9 @@ static int imx_card_parse_of(struct imx_card_data *data)
}
}
- link->cpus->of_node = args.np;
link->platforms->of_node = link->cpus->of_node;
link->id = args.args[0];
- ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
- if (ret) {
- dev_err_probe(card->dev, ret,
- "%s: error getting cpu dai name\n", link->name);
- goto err;
- }
-
codec = of_get_child_by_name(np, "codec");
if (codec) {
ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
@@ -615,17 +607,8 @@ static int imx_card_parse_of(struct imx_card_data *data)
plat_data->type = CODEC_AK5552;
} else {
- dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
- if (!dlc) {
- ret = -ENOMEM;
- goto err;
- }
-
- link->codecs = dlc;
+ link->codecs = &asoc_dummy_dlc;
link->num_codecs = 1;
-
- link->codecs->dai_name = "snd-soc-dummy-dai";
- link->codecs->name = "snd-soc-dummy";
}
if (!strncmp(link->name, "HiFi-ASRC-FE", 12)) {
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index 89178106fe2c..3c7b95db2eac 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -92,13 +92,11 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
/* Optional codec node */
ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args);
if (ret) {
- data->dai.codecs->dai_name = "snd-soc-dummy-dai";
- data->dai.codecs->name = "snd-soc-dummy";
+ *data->dai.codecs = asoc_dummy_dlc;
} else {
struct clk *clk;
- data->dai.codecs->of_node = args.np;
- ret = snd_soc_get_dai_name(&args, &data->dai.codecs->dai_name);
+ ret = snd_soc_get_dlc(&args, data->dai.codecs);
if (ret) {
dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
goto fail;
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index ab978431ac98..44463f92e522 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -26,7 +26,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- comp = devm_kzalloc(&pdev->dev, 2 * sizeof(*comp), GFP_KERNEL);
+ comp = devm_kzalloc(&pdev->dev, sizeof(*comp), GFP_KERNEL);
if (!data || !comp) {
ret = -ENOMEM;
goto end;
@@ -37,8 +37,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
* platform is using soc-generic-dmaengine-pcm
*/
data->dai.cpus =
- data->dai.platforms = &comp[0];
- data->dai.codecs = &comp[1];
+ data->dai.platforms = comp;
+ data->dai.codecs = &asoc_dummy_dlc;
data->dai.num_cpus = 1;
data->dai.num_codecs = 1;
@@ -46,8 +46,6 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
data->dai.name = "S/PDIF PCM";
data->dai.stream_name = "S/PDIF PCM";
- data->dai.codecs->dai_name = "snd-soc-dummy-dai";
- data->dai.codecs->name = "snd-soc-dummy";
data->dai.cpus->of_node = spdif_np;
data->dai.playback_only = true;
data->dai.capture_only = true;
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 4e85536a1b26..c6e0f9132193 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -55,60 +55,6 @@ static const struct snd_soc_ops graph_ops = {
.hw_params = asoc_simple_hw_params,
};
-static int graph_get_dai_id(struct device_node *ep)
-{
- struct device_node *node;
- struct device_node *endpoint;
- struct of_endpoint info;
- int i, id;
- const u32 *reg;
- int ret;
-
- /* use driver specified DAI ID if exist */
- ret = snd_soc_get_dai_id(ep);
- if (ret != -ENOTSUPP)
- return ret;
-
- /* use endpoint/port reg if exist */
- ret = of_graph_parse_endpoint(ep, &info);
- if (ret == 0) {
- /*
- * Because it will count port/endpoint if it doesn't have "reg".
- * But, we can't judge whether it has "no reg", or "reg = <0>"
- * only of_graph_parse_endpoint().
- * We need to check "reg" property
- */
- if (of_property_present(ep, "reg"))
- return info.id;
-
- node = of_get_parent(ep);
- reg = of_get_property(node, "reg", NULL);
- of_node_put(node);
- if (reg)
- return info.port;
- }
- node = of_graph_get_port_parent(ep);
-
- /*
- * Non HDMI sound case, counting port/endpoint on its DT
- * is enough. Let's count it.
- */
- i = 0;
- id = -1;
- for_each_endpoint_of_node(node, endpoint) {
- if (endpoint == ep)
- id = i;
- i++;
- }
-
- of_node_put(node);
-
- if (id < 0)
- return -ENODEV;
-
- return id;
-}
-
static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
{
struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
@@ -120,57 +66,6 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
return false;
}
-static int asoc_simple_parse_dai(struct device_node *ep,
- struct snd_soc_dai_link_component *dlc,
- int *is_single_link)
-{
- struct device_node *node;
- struct of_phandle_args args;
- int ret;
-
- if (!ep)
- return 0;
-
- node = of_graph_get_port_parent(ep);
-
- /* Get dai->name */
- args.np = node;
- args.args[0] = graph_get_dai_id(ep);
- args.args_count = (of_graph_get_endpoint_count(node) > 1);
-
- /*
- * FIXME
- *
- * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
- * If user unbinded CPU or Codec driver, but not for Sound Card,
- * dlc->dai_name is keeping unbinded CPU or Codec
- * driver's pointer.
- *
- * If user re-bind CPU or Codec driver again, ALSA SoC will try
- * to rebind Card via snd_soc_try_rebind_card(), but because of
- * above reason, it might can't bind Sound Card.
- * Because Sound Card is pointing to released dai_name pointer.
- *
- * To avoid this rebind Card issue,
- * 1) It needs to alloc memory to keep dai_name eventhough
- * CPU or Codec driver was unbinded, or
- * 2) user need to rebind Sound Card everytime
- * if he unbinded CPU or Codec.
- */
- ret = snd_soc_get_dai_name(&args, &dlc->dai_name);
- if (ret < 0) {
- of_node_put(node);
- return ret;
- }
-
- dlc->of_node = node;
-
- if (is_single_link)
- *is_single_link = of_graph_get_endpoint_count(node) == 1;
-
- return 0;
-}
-
static void graph_parse_convert(struct device *dev,
struct device_node *ep,
struct asoc_simple_data *adata)
@@ -231,7 +126,7 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(top, ep, dai_props);
- ret = asoc_simple_parse_dai(ep, dlc, cpu);
+ ret = asoc_graph_parse_dai(ep, dlc, cpu);
if (ret < 0)
return ret;
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
index 994db61a26b3..2ac0de3c21da 100644
--- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
+++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
@@ -42,6 +42,15 @@
* [Normal]
* cpu0 <-@-----------------> codec0
*
+ * [Semi-Multi]
+ *
+ * CPU:Codec = 1:N
+ *
+ * +-+
+ * cpu7 <-@------->| |-> codec12
+ * | |-> codec13
+ * +-+
+ *
* [Multi-CPU/Codec]
* +-+ +-+
* cpu1 <--| |<-@--------->| |-> codec1
@@ -128,6 +137,9 @@
*/
&cpu0
+ /* [Semi-Multi] */
+ &sm0
+
/*
* [Multi-CPU/Codec]: cpu side only
* cpu1/cpu2/codec1/codec2
@@ -160,76 +172,127 @@
>;
multi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
ports@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
/* [Multi-CPU] */
- mcpu0: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
- port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
- port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
+ mcpu0: port@0 { reg = <0>; mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
+ port@1 { reg = <1>; mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
+ port@2 { reg = <2>; mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
};
/* [Multi-Codec] */
ports@1 {
- port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
- port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
- port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
+ port@1 { reg = <1>; mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+ port@2 { reg = <2>; mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
};
/* [DPCM-Multi]::BE */
ports@2 {
- port@0 { mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; };
- port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; };
- port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; };
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; };
+ port@1 { reg = <1>; mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; };
+ port@2 { reg = <2>; mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; };
};
/* [Codec2Codec-Multi]::CPU */
ports@3 {
- port@0 { mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; };
- port@1 { mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; };
- port@2 { mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; };
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; };
+ port@1 { reg = <1>; mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; };
+ port@2 { reg = <2>; mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; };
};
/* [Codec2Codec-Multi]::Codec */
ports@4 {
- port@0 { mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; };
- port@1 { mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; };
- port@2 { mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; };
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; };
+ port@1 { reg = <1>; mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; };
+ port@2 { reg = <2>; mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; };
+ };
+
+ /* [Semi-Multi] */
+ ports@5 {
+ reg = <5>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; smcodec0_ep: endpoint { remote-endpoint = <&cpu7_ep>; }; };
+ port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; }; };
+ port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; }; };
};
};
dpcm {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
ports@0 {
+ reg = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
/* [DPCM]::FE */
- fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; };
- fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; };
+ fe00: port@0 { reg = <0>; fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; };
+ fe01: port@1 { reg = <1>; fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; };
/* [DPCM-Multi]::FE */
- fe10: port@2 { fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; };
- fe11: port@3 { fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; };
+ fe10: port@2 { reg = <2>; fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; };
+ fe11: port@3 { reg = <3>; fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; };
};
ports@1 {
+ reg = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
/* [DPCM]::BE */
- be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; };
+ be0: port@0 { reg = <0>; be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; };
/* [DPCM-Multi]::BE */
- be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; };
+ be1: port@1 { reg = <1>; be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; };
};
};
codec2codec {
+ #address-cells = <1>;
+ #size-cells = <0>;
/* [Codec2Codec] */
ports@0 {
+ reg = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
/* use default settings */
- c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; };
- port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; };
+ c2c: port@0 { reg = <0>; c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; };
+ port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; };
};
/* [Codec2Codec-Multi] */
ports@1 {
+ reg = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
/* use original settings */
rate = <48000>;
- c2c_m: port@0 { c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; };
- port@1 { c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; };
+ c2c_m: port@0 { reg = <0>; c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; };
+ port@1 { reg = <1>; c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; };
};
};
};
@@ -245,22 +308,28 @@
*/
compatible = "test-cpu";
ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
bitclock-master;
frame-master;
/* [Normal] */
- cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+ cpu0: port@0 { reg = <0>; cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
/* [Multi-CPU] */
- port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
- port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
+ port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
+ port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
/* [DPCM]::FE */
- port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; };
- port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; };
+ port@3 { reg = <3>; cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; };
+ port@4 { reg = <4>; cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; };
/* [DPCM-Multi]::FE */
- port@5 { cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; };
- port@6 { cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; };
+ port@5 { reg = <5>; cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; };
+ port@6 { reg = <6>; cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; };
+
+ /* [Semi-Multi] */
+ sm0: port@7 { reg = <7>; cpu7_ep: endpoint { remote-endpoint = <&smcodec0_ep>; }; };
};
};
@@ -275,6 +344,9 @@
*/
compatible = "test-codec";
ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
/*
* prefix can be added to *component*,
* see audio-graph-card2::routing
@@ -282,35 +354,40 @@
prefix = "TC";
/* [Normal] */
- port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; };
+ port@0 { reg = <0>; codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; };
/* [Multi-Codec] */
- port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
- port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
+ port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
+ port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
/* [DPCM]::BE */
port@3 {
convert-rate = <44100>;
- codec3_ep: endpoint { remote-endpoint = <&be00_ep>; };
+ reg = <3>; codec3_ep: endpoint { remote-endpoint = <&be00_ep>; };
};
/* [DPCM-Multi]::BE */
- port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; };
- port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; };
+ port@4 { reg = <4>; codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; };
+ port@5 { reg = <5>; codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; };
/* [Codec2Codec] */
port@6 { bitclock-master;
frame-master;
- codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
- port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
+ reg = <6>; codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
+ port@7 { reg = <7>; codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
/* [Codec2Codec-Multi] */
port@8 { bitclock-master;
frame-master;
- codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; };
- port@9 { codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; };
- port@10 { codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; };
- port@11 { codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; };
+ reg = <8>; codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; };
+ port@9 { reg = <9>; codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; };
+ port@a { reg = <10>; codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; };
+ port@b { reg = <11>; codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; };
+
+ /* [Semi-Multi] */
+ port@c { reg = <12>; codec12_ep: endpoint { remote-endpoint = <&smcodec1_ep>; }; };
+ port@d { reg = <13>; codec13_ep: endpoint { remote-endpoint = <&smcodec2_ep>; }; };
+
};
};
};
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index 25aa79dd55b3..542c4a114940 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -353,111 +353,6 @@ static const struct snd_soc_ops graph_ops = {
.hw_params = asoc_simple_hw_params,
};
-static int graph_get_dai_id(struct device_node *ep)
-{
- struct device_node *node;
- struct device_node *endpoint;
- struct of_endpoint info;
- int i, id;
- const u32 *reg;
- int ret;
-
- /* use driver specified DAI ID if exist */
- ret = snd_soc_get_dai_id(ep);
- if (ret != -ENOTSUPP)
- return ret;
-
- /* use endpoint/port reg if exist */
- ret = of_graph_parse_endpoint(ep, &info);
- if (ret == 0) {
- /*
- * Because it will count port/endpoint if it doesn't have "reg".
- * But, we can't judge whether it has "no reg", or "reg = <0>"
- * only of_graph_parse_endpoint().
- * We need to check "reg" property
- */
- if (of_property_present(ep, "reg"))
- return info.id;
-
- node = of_get_parent(ep);
- reg = of_get_property(node, "reg", NULL);
- of_node_put(node);
- if (reg)
- return info.port;
- }
- node = of_graph_get_port_parent(ep);
-
- /*
- * Non HDMI sound case, counting port/endpoint on its DT
- * is enough. Let's count it.
- */
- i = 0;
- id = -1;
- for_each_endpoint_of_node(node, endpoint) {
- if (endpoint == ep)
- id = i;
- i++;
- }
-
- of_node_put(node);
-
- if (id < 0)
- return -ENODEV;
-
- return id;
-}
-
-static int asoc_simple_parse_dai(struct device_node *ep,
- struct snd_soc_dai_link_component *dlc,
- int *is_single_link)
-{
- struct device_node *node;
- struct of_phandle_args args;
- int ret;
-
- if (!ep)
- return 0;
-
- node = of_graph_get_port_parent(ep);
-
- /* Get dai->name */
- args.np = node;
- args.args[0] = graph_get_dai_id(ep);
- args.args_count = (of_graph_get_endpoint_count(node) > 1);
-
- /*
- * FIXME
- *
- * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
- * If user unbinded CPU or Codec driver, but not for Sound Card,
- * dlc->dai_name is keeping unbinded CPU or Codec
- * driver's pointer.
- *
- * If user re-bind CPU or Codec driver again, ALSA SoC will try
- * to rebind Card via snd_soc_try_rebind_card(), but because of
- * above reason, it might can't bind Sound Card.
- * Because Sound Card is pointing to released dai_name pointer.
- *
- * To avoid this rebind Card issue,
- * 1) It needs to alloc memory to keep dai_name eventhough
- * CPU or Codec driver was unbinded, or
- * 2) user need to rebind Sound Card everytime
- * if he unbinded CPU or Codec.
- */
- ret = snd_soc_get_dai_name(&args, &dlc->dai_name);
- if (ret < 0) {
- of_node_put(node);
- return ret;
- }
-
- dlc->of_node = node;
-
- if (is_single_link)
- *is_single_link = of_graph_get_endpoint_count(node) == 1;
-
- return 0;
-}
-
static void graph_parse_convert(struct device_node *ep,
struct simple_dai_props *props)
{
@@ -512,7 +407,7 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(ep, dai_props);
- ret = asoc_simple_parse_dai(ep, dlc, &is_single_links);
+ ret = asoc_graph_parse_dai(ep, dlc, &is_single_links);
if (ret < 0)
return ret;
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index e5ff61c1e9d1..3019626b0592 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -889,11 +889,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
li->link, dai_num, cnf_num);
- /* dummy CPU/Codec */
- priv->dummy.of_node = NULL;
- priv->dummy.dai_name = "snd-soc-dummy-dai";
- priv->dummy.name = "snd-soc-dummy";
-
priv->dai_props = dai_props;
priv->dai_link = dai_link;
priv->dais = dais;
@@ -908,7 +903,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
for (i = 0; i < li->link; i++) {
if (li->num[i].cpus) {
/* Normal CPU */
- dai_props[i].cpus =
dai_link[i].cpus = dlcs;
dai_props[i].num.cpus =
dai_link[i].num_cpus = li->num[i].cpus;
@@ -918,15 +912,13 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
dais += li->num[i].cpus;
} else {
/* DPCM Be's CPU = dummy */
- dai_props[i].cpus =
- dai_link[i].cpus = &priv->dummy;
+ dai_link[i].cpus = &asoc_dummy_dlc;
dai_props[i].num.cpus =
dai_link[i].num_cpus = 1;
}
if (li->num[i].codecs) {
/* Normal Codec */
- dai_props[i].codecs =
dai_link[i].codecs = dlcs;
dai_props[i].num.codecs =
dai_link[i].num_codecs = li->num[i].codecs;
@@ -942,15 +934,13 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
}
} else {
/* DPCM Fe's Codec = dummy */
- dai_props[i].codecs =
- dai_link[i].codecs = &priv->dummy;
+ dai_link[i].codecs = &asoc_dummy_dlc;
dai_props[i].num.codecs =
dai_link[i].num_codecs = 1;
}
if (li->num[i].platforms) {
/* Have Platform */
- dai_props[i].platforms =
dai_link[i].platforms = dlcs;
dai_props[i].num.platforms =
dai_link[i].num_platforms = li->num[i].platforms;
@@ -958,7 +948,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
dlcs += li->num[i].platforms;
} else {
/* Doesn't have Platform */
- dai_props[i].platforms =
dai_link[i].platforms = NULL;
dai_props[i].num.platforms =
dai_link[i].num_platforms = 0;
@@ -1024,6 +1013,109 @@ int asoc_graph_is_ports0(struct device_node *np)
}
EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
+static int graph_get_dai_id(struct device_node *ep)
+{
+ struct device_node *node;
+ struct device_node *endpoint;
+ struct of_endpoint info;
+ int i, id;
+ int ret;
+
+ /* use driver specified DAI ID if exist */
+ ret = snd_soc_get_dai_id(ep);
+ if (ret != -ENOTSUPP)
+ return ret;
+
+ /* use endpoint/port reg if exist */
+ ret = of_graph_parse_endpoint(ep, &info);
+ if (ret == 0) {
+ /*
+ * Because it will count port/endpoint if it doesn't have "reg".
+ * But, we can't judge whether it has "no reg", or "reg = <0>"
+ * only of_graph_parse_endpoint().
+ * We need to check "reg" property
+ */
+ if (of_property_present(ep, "reg"))
+ return info.id;
+
+ node = of_get_parent(ep);
+ ret = of_property_present(node, "reg");
+ of_node_put(node);
+ if (ret)
+ return info.port;
+ }
+ node = of_graph_get_port_parent(ep);
+
+ /*
+ * Non HDMI sound case, counting port/endpoint on its DT
+ * is enough. Let's count it.
+ */
+ i = 0;
+ id = -1;
+ for_each_endpoint_of_node(node, endpoint) {
+ if (endpoint == ep)
+ id = i;
+ i++;
+ }
+
+ of_node_put(node);
+
+ if (id < 0)
+ return -ENODEV;
+
+ return id;
+}
+
+int asoc_graph_parse_dai(struct device_node *ep,
+ struct snd_soc_dai_link_component *dlc,
+ int *is_single_link)
+{
+ struct device_node *node;
+ struct of_phandle_args args = {};
+ int ret;
+
+ if (!ep)
+ return 0;
+
+ node = of_graph_get_port_parent(ep);
+
+ /* Get dai->name */
+ args.np = node;
+ args.args[0] = graph_get_dai_id(ep);
+ args.args_count = (of_graph_get_endpoint_count(node) > 1);
+
+ /*
+ * FIXME
+ *
+ * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
+ * If user unbinded CPU or Codec driver, but not for Sound Card,
+ * dlc->dai_name is keeping unbinded CPU or Codec
+ * driver's pointer.
+ *
+ * If user re-bind CPU or Codec driver again, ALSA SoC will try
+ * to rebind Card via snd_soc_try_rebind_card(), but because of
+ * above reason, it might can't bind Sound Card.
+ * Because Sound Card is pointing to released dai_name pointer.
+ *
+ * To avoid this rebind Card issue,
+ * 1) It needs to alloc memory to keep dai_name eventhough
+ * CPU or Codec driver was unbinded, or
+ * 2) user need to rebind Sound Card everytime
+ * if he unbinded CPU or Codec.
+ */
+ ret = snd_soc_get_dlc(&args, dlc);
+ if (ret < 0) {
+ of_node_put(node);
+ return ret;
+ }
+
+ if (is_single_link)
+ *is_single_link = of_graph_get_endpoint_count(node) == 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_graph_parse_dai);
+
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 5a5e4ecd0f61..0745bf6a09aa 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -89,12 +89,10 @@ static int asoc_simple_parse_dai(struct device_node *node,
* 2) user need to rebind Sound Card everytime
* if he unbinded CPU or Codec.
*/
- ret = snd_soc_of_get_dai_name(node, &dlc->dai_name);
+ ret = snd_soc_get_dlc(&args, dlc);
if (ret < 0)
return ret;
- dlc->of_node = args.np;
-
if (is_single_link)
*is_single_link = !args.args_count;
diff --git a/sound/soc/google/Kconfig b/sound/soc/google/Kconfig
new file mode 100644
index 000000000000..7603782fb060
--- /dev/null
+++ b/sound/soc/google/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SND_SOC_CHV3_I2S
+ tristate "Google Chameleon v3 I2S device"
+ help
+ Enable support for the Google Chameleon v3 I2S device.
diff --git a/sound/soc/google/Makefile b/sound/soc/google/Makefile
new file mode 100644
index 000000000000..862496af1ae1
--- /dev/null
+++ b/sound/soc/google/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SND_SOC_CHV3_I2S) += chv3-i2s.o
diff --git a/sound/soc/google/chv3-i2s.c b/sound/soc/google/chv3-i2s.c
new file mode 100644
index 000000000000..0f6513444906
--- /dev/null
+++ b/sound/soc/google/chv3-i2s.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+/*
+ * The I2S interface consists of two ring buffers - one for RX and one for
+ * TX. A ring buffer has a producer index and a consumer index. Depending
+ * on which way the data is flowing, either the software or the hardware
+ * writes data and updates the producer index, and the other end reads data
+ * and updates the consumer index.
+ *
+ * The pointer managed by software is updated using the .ack callback
+ * (see chv3_dma_ack). This seems to be the only way to reliably obtain
+ * the appl_ptr from within the driver and pass it to hardware.
+ *
+ * Because of the two pointer design, the ring buffer can never be full. With
+ * capture this isn't a problem, because the hardware being the producer
+ * will wait for the consumer index to move out of the way. With playback,
+ * however, this is problematic, because ALSA wants to fill up the buffer
+ * completely when waiting for hardware. In the .ack callback, the driver
+ * would have to wait for the consumer index to move out of the way by
+ * busy-waiting, which would keep stalling the kernel for quite a long time.
+ *
+ * The workaround to this problem is to "lie" to ALSA that the hw_pointer
+ * is one frame behind what it actually is (see chv3_dma_pointer). This
+ * way, ALSA will not try to fill up the entire buffer, and all callbacks
+ * are wait-free.
+ */
+
+#define I2S_TX_ENABLE 0x00
+#define I2S_TX_BASE_ADDR 0x04
+#define I2S_TX_BUFFER_SIZE 0x08
+#define I2S_TX_PRODUCER_IDX 0x0c
+#define I2S_TX_CONSUMER_IDX 0x10
+#define I2S_RX_ENABLE 0x14
+#define I2S_RX_BASE_ADDR 0x18
+#define I2S_RX_BUFFER_SIZE 0x1c
+#define I2S_RX_PRODUCER_IDX 0x20
+#define I2S_RX_CONSUMER_IDX 0x24
+
+#define I2S_SOFT_RESET 0x2c
+#define I2S_SOFT_RESET_RX_BIT 0x1
+#define I2S_SOFT_RESET_TX_BIT 0x2
+
+#define I2S_RX_IRQ 0x4c
+#define I2S_RX_IRQ_CONST 0x50
+#define I2S_TX_IRQ 0x54
+#define I2S_TX_IRQ_CONST 0x58
+
+#define I2S_IRQ_MASK 0x8
+#define I2S_IRQ_CLR 0xc
+#define I2S_IRQ_RX_BIT 0x1
+#define I2S_IRQ_TX_BIT 0x2
+
+#define I2S_MAX_BUFFER_SIZE 0x200000
+
+struct chv3_i2s_dev {
+ struct device *dev;
+ void __iomem *iobase;
+ void __iomem *iobase_irq;
+ struct snd_pcm_substream *rx_substream;
+ struct snd_pcm_substream *tx_substream;
+ int tx_bytes_to_fetch;
+};
+
+static struct snd_soc_dai_driver chv3_i2s_dai = {
+ .name = "chv3-i2s",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 128,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 128,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+static const struct snd_pcm_hardware chv3_dma_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .buffer_bytes_max = I2S_MAX_BUFFER_SIZE,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 4,
+ .periods_max = 256,
+};
+
+static inline void chv3_i2s_wr(struct chv3_i2s_dev *i2s, int offset, u32 val)
+{
+ writel(val, i2s->iobase + offset);
+}
+
+static inline u32 chv3_i2s_rd(struct chv3_i2s_dev *i2s, int offset)
+{
+ return readl(i2s->iobase + offset);
+}
+
+static irqreturn_t chv3_i2s_isr(int irq, void *data)
+{
+ struct chv3_i2s_dev *i2s = data;
+ u32 reg;
+
+ reg = readl(i2s->iobase_irq + I2S_IRQ_CLR);
+ if (!reg)
+ return IRQ_NONE;
+
+ if (reg & I2S_IRQ_RX_BIT)
+ snd_pcm_period_elapsed(i2s->rx_substream);
+
+ if (reg & I2S_IRQ_TX_BIT)
+ snd_pcm_period_elapsed(i2s->tx_substream);
+
+ writel(reg, i2s->iobase_irq + I2S_IRQ_CLR);
+
+ return IRQ_HANDLED;
+}
+
+static int chv3_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+ int res;
+
+ snd_soc_set_runtime_hwparams(substream, &chv3_dma_hw);
+
+ res = snd_pcm_hw_constraint_pow2(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+ if (res)
+ return res;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ i2s->rx_substream = substream;
+ else
+ i2s->tx_substream = substream;
+
+ return 0;
+}
+static int chv3_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ chv3_i2s_wr(i2s, I2S_RX_ENABLE, 0);
+ else
+ chv3_i2s_wr(i2s, I2S_TX_ENABLE, 0);
+
+ return 0;
+}
+
+static int chv3_dma_pcm_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+ struct snd_pcm_substream *substream;
+ int res;
+
+ substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (substream) {
+ res = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, i2s->dev,
+ I2S_MAX_BUFFER_SIZE, &substream->dma_buffer);
+ if (res)
+ return res;
+ }
+
+ substream = rtd->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (substream) {
+ res = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, i2s->dev,
+ I2S_MAX_BUFFER_SIZE, &substream->dma_buffer);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int chv3_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int chv3_dma_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+ unsigned int buffer_bytes, period_bytes, period_size;
+
+ buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ period_size = substream->runtime->period_size;
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ chv3_i2s_wr(i2s, I2S_SOFT_RESET, I2S_SOFT_RESET_RX_BIT);
+ chv3_i2s_wr(i2s, I2S_RX_BASE_ADDR, substream->dma_buffer.addr);
+ chv3_i2s_wr(i2s, I2S_RX_BUFFER_SIZE, buffer_bytes);
+ chv3_i2s_wr(i2s, I2S_RX_IRQ, (period_size << 8) | 1);
+ chv3_i2s_wr(i2s, I2S_RX_ENABLE, 1);
+ } else {
+ chv3_i2s_wr(i2s, I2S_SOFT_RESET, I2S_SOFT_RESET_TX_BIT);
+ chv3_i2s_wr(i2s, I2S_TX_BASE_ADDR, substream->dma_buffer.addr);
+ chv3_i2s_wr(i2s, I2S_TX_BUFFER_SIZE, buffer_bytes);
+ chv3_i2s_wr(i2s, I2S_TX_IRQ, ((period_bytes / i2s->tx_bytes_to_fetch) << 8) | 1);
+ chv3_i2s_wr(i2s, I2S_TX_ENABLE, 1);
+ }
+ writel(I2S_IRQ_RX_BIT | I2S_IRQ_TX_BIT, i2s->iobase_irq + I2S_IRQ_MASK);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t chv3_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+ u32 frame_bytes, buffer_bytes;
+ u32 idx_bytes;
+
+ frame_bytes = substream->runtime->frame_bits * 8;
+ buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ idx_bytes = chv3_i2s_rd(i2s, I2S_RX_PRODUCER_IDX);
+ } else {
+ idx_bytes = chv3_i2s_rd(i2s, I2S_TX_CONSUMER_IDX);
+ /* lag the pointer by one frame */
+ idx_bytes = (idx_bytes - frame_bytes) & (buffer_bytes - 1);
+ }
+
+ return bytes_to_frames(substream->runtime, idx_bytes);
+}
+
+static int chv3_dma_ack(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct chv3_i2s_dev *i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+ unsigned int bytes, idx;
+
+ bytes = frames_to_bytes(runtime, runtime->control->appl_ptr);
+ idx = bytes & (snd_pcm_lib_buffer_bytes(substream) - 1);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ chv3_i2s_wr(i2s, I2S_RX_CONSUMER_IDX, idx);
+ else
+ chv3_i2s_wr(i2s, I2S_TX_PRODUCER_IDX, idx);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver chv3_i2s_comp = {
+ .name = "chv3-i2s-comp",
+ .open = chv3_dma_open,
+ .close = chv3_dma_close,
+ .pcm_construct = chv3_dma_pcm_construct,
+ .hw_params = chv3_dma_hw_params,
+ .prepare = chv3_dma_prepare,
+ .pointer = chv3_dma_pointer,
+ .ack = chv3_dma_ack,
+};
+
+static int chv3_i2s_probe(struct platform_device *pdev)
+{
+ struct chv3_i2s_dev *i2s;
+ int res;
+ int irq;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->iobase = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(i2s->iobase))
+ return PTR_ERR(i2s->iobase);
+
+ i2s->iobase_irq = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(i2s->iobase_irq))
+ return PTR_ERR(i2s->iobase_irq);
+
+ i2s->tx_bytes_to_fetch = (chv3_i2s_rd(i2s, I2S_TX_IRQ_CONST) >> 8) & 0xffff;
+
+ i2s->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENXIO;
+ res = devm_request_irq(i2s->dev, irq, chv3_i2s_isr, 0, "chv3-i2s", i2s);
+ if (res)
+ return res;
+
+ res = devm_snd_soc_register_component(&pdev->dev, &chv3_i2s_comp,
+ &chv3_i2s_dai, 1);
+ if (res) {
+ dev_err(&pdev->dev, "couldn't register component: %d\n", res);
+ return res;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id chv3_i2s_of_match[] = {
+ { .compatible = "google,chv3-i2s" },
+ {},
+};
+
+static struct platform_driver chv3_i2s_driver = {
+ .probe = chv3_i2s_probe,
+ .driver = {
+ .name = "chv3-i2s",
+ .of_match_table = chv3_i2s_of_match,
+ },
+};
+
+module_platform_driver(chv3_i2s_driver);
+
+MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>");
+MODULE_DESCRIPTION("Chameleon v3 I2S interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index d3973936426a..29d44c989e5f 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -15,7 +15,6 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index 4e039c7173d8..3fc2c9a6c44d 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -15,7 +15,6 @@
#include <linux/firmware.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c
index eea889001c24..bf4ba6bcc429 100644
--- a/sound/soc/intel/atom/sst/sst_loader.c
+++ b/sound/soc/intel/atom/sst/sst_loader.c
@@ -20,7 +20,6 @@
#include <linux/sched.h>
#include <linux/firmware.h>
#include <linux/dmaengine.h>
-#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c
index 5862fe968083..4058b4f80a0c 100644
--- a/sound/soc/intel/atom/sst/sst_pci.c
+++ b/sound/soc/intel/atom/sst/sst_pci.c
@@ -15,7 +15,6 @@
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <asm/platform_sst_audio.h>
diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c
index ea1ef8a61fa6..862a19ae5429 100644
--- a/sound/soc/intel/atom/sst/sst_stream.c
+++ b/sound/soc/intel/atom/sst/sst_stream.c
@@ -15,7 +15,6 @@
#include <linux/firmware.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
index 1a1d572cc1d0..964a763732ab 100644
--- a/sound/soc/intel/avs/boards/da7219.c
+++ b/sound/soc/intel/avs/boards/da7219.c
@@ -181,38 +181,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Capture");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, DA7219_DAI_NAME);
@@ -230,14 +198,13 @@ static int avs_card_resume_post(struct snd_soc_card *card)
static int avs_da7219_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -249,12 +216,6 @@ static int avs_da7219_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
@@ -271,8 +232,8 @@ static int avs_da7219_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
index 90a921638572..c270646faf86 100644
--- a/sound/soc/intel/avs/boards/dmic.c
+++ b/sound/soc/intel/avs/boards/dmic.c
@@ -44,8 +44,6 @@ static const struct snd_soc_dapm_widget card_widgets[] = {
static const struct snd_soc_dapm_route card_routes[] = {
{"DMic", NULL, "SoC DMIC"},
- {"DMIC Rx", NULL, "Capture"},
- {"DMIC WoV Rx", NULL, "Capture"},
};
static int avs_dmic_probe(struct platform_device *pdev)
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index a542a67e21d0..cb00bc86ac94 100644
--- a/sound/soc/intel/avs/boards/hdaudio.c
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -64,56 +64,6 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, struct hda_codec *codec, int pcm_count,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- struct hda_pcm *pcm;
- const char *cname = dev_name(&codec->core.dev);
- int i, n = 0;
-
- /* at max twice the number of pcms */
- dr = devm_kcalloc(dev, pcm_count * 2, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- 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 hda_pcm_stream *stream;
- int dir;
-
- dir = SNDRV_PCM_STREAM_PLAYBACK;
- stream = &pcm->stream[dir];
- if (!stream->substreams)
- goto capture_routes;
-
- dr[n].sink = devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
- snd_pcm_direction_name(dir));
- dr[n].source = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d Tx", cname, i);
- if (!dr[n].sink || !dr[n].source)
- return -ENOMEM;
- n++;
-
-capture_routes:
- dir = SNDRV_PCM_STREAM_CAPTURE;
- stream = &pcm->stream[dir];
- if (!stream->substreams)
- continue;
-
- dr[n].sink = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d Rx", cname, i);
- dr[n].source = devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
- snd_pcm_direction_name(dir));
- if (!dr[n].sink || !dr[n].source)
- return -ENOMEM;
- n++;
- }
-
- *routes = dr;
- *num_routes = n;
- return 0;
-}
-
/* Should be aligned with SectionPCM's name from topology */
#define FEDAI_NAME_PREFIX "HDMI"
@@ -172,13 +122,12 @@ static int avs_card_late_probe(struct snd_soc_card *card)
static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_acpi_mach *mach;
struct snd_soc_dai_link *links = NULL;
struct snd_soc_card *card = rtm->card;
struct hda_codec *codec;
struct hda_pcm *pcm;
- int ret, n, pcm_count = 0;
+ int ret, pcm_count = 0;
mach = dev_get_platdata(card->dev);
codec = mach->pdata;
@@ -200,18 +149,6 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
return ret;
}
- ret = avs_create_dapm_routes(card->dev, codec, pcm_count, &routes, &n);
- if (ret < 0) {
- dev_err(card->dev, "create routes failed: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dapm_add_routes(&card->dapm, routes, n);
- if (ret < 0) {
- dev_err(card->dev, "add routes failed: %d\n", ret);
- return ret;
- }
-
return 0;
}
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
index 8f0fd87bc866..bc3065c6ceda 100644
--- a/sound/soc/intel/avs/boards/i2s_test.c
+++ b/sound/soc/intel/avs/boards/i2s_test.c
@@ -28,13 +28,11 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
- dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
- if (!dl->name || !dl->cpus || !dl->codecs)
+ if (!dl->name || !dl->cpus)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
- dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "snd-soc-dummy");
- dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "snd-soc-dummy-dai");
+ dl->codecs = &asoc_dummy_dlc;
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
index 183123d08c5a..b9b20562c691 100644
--- a/sound/soc/intel/avs/boards/max98357a.c
+++ b/sound/soc/intel/avs/boards/max98357a.c
@@ -86,41 +86,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 1;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "HiFi Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_max98357a_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -132,12 +105,6 @@ static int avs_max98357a_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
@@ -151,8 +118,8 @@ static int avs_max98357a_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
ret = snd_soc_fixup_dai_links_platform_name(card, pname);
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
index 8e221ecd34b0..3833251ade26 100644
--- a/sound/soc/intel/avs/boards/max98373.c
+++ b/sound/soc/intel/avs/boards/max98373.c
@@ -141,47 +141,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left HiFi Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right HiFi Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_max98373_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -193,12 +160,6 @@ static int avs_max98373_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
@@ -214,8 +175,8 @@ static int avs_max98373_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
ret = snd_soc_fixup_dai_links_platform_name(card, pname);
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
index 7cccce99f92e..09b231bf4e6d 100644
--- a/sound/soc/intel/avs/boards/max98927.c
+++ b/sound/soc/intel/avs/boards/max98927.c
@@ -138,47 +138,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left HiFi Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right HiFi Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_max98927_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -190,12 +157,6 @@ static int avs_max98927_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
@@ -211,8 +172,8 @@ static int avs_max98927_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
ret = snd_soc_fixup_dai_links_platform_name(card, pname);
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index b69fc5567135..38c5087d98e9 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -215,38 +215,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Capture");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
@@ -274,14 +242,13 @@ static int avs_card_resume_post(struct snd_soc_card *card)
static int avs_nau8825_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -293,12 +260,6 @@ static int avs_nau8825_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
@@ -315,8 +276,8 @@ static int avs_nau8825_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index 6a1e121f082f..ebfee54814ce 100644
--- a/sound/soc/intel/avs/boards/rt274.c
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -188,38 +188,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
@@ -237,14 +205,13 @@ static int avs_card_resume_post(struct snd_soc_card *card)
static int avs_rt274_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -256,12 +223,6 @@ static int avs_rt274_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
@@ -278,8 +239,8 @@ static int avs_rt274_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 3551a05bd599..84cf9a0c8dfe 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -158,38 +158,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT286_CODEC_DAI);
@@ -207,14 +175,13 @@ static int avs_card_resume_post(struct snd_soc_card *card)
static int avs_rt286_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -226,12 +193,6 @@ static int avs_rt286_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
@@ -248,8 +209,8 @@ static int avs_rt286_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index 2923f3805bbe..3b0e2b1a3251 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -178,38 +178,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT298_CODEC_DAI);
@@ -227,14 +195,13 @@ static int avs_card_resume_post(struct snd_soc_card *card)
static int avs_rt298_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -246,12 +213,6 @@ static int avs_rt298_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
@@ -268,8 +229,8 @@ static int avs_rt298_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
index b2c2ba93dcb5..7142a67900bf 100644
--- a/sound/soc/intel/avs/boards/rt5682.c
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -234,38 +234,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 2;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, AVS_RT5682_CODEC_DAI_NAME);
@@ -283,14 +251,13 @@ static int avs_card_resume_post(struct snd_soc_card *card)
static int avs_rt5682_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
if (pdev->id_entry && pdev->id_entry->driver_data)
avs_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data;
@@ -308,12 +275,6 @@ static int avs_rt5682_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
@@ -330,8 +291,8 @@ static int avs_rt5682_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
index 2b7f5ad92aca..7324869d6132 100644
--- a/sound/soc/intel/avs/boards/ssm4567.c
+++ b/sound/soc/intel/avs/boards/ssm4567.c
@@ -129,59 +129,14 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
return 0;
}
-static int avs_create_dapm_routes(struct device *dev, int ssp_port,
- struct snd_soc_dapm_route **routes, int *num_routes)
-{
- struct snd_soc_dapm_route *dr;
- const int num_base = ARRAY_SIZE(card_base_routes);
- const int num_dr = num_base + 4;
- int idx;
-
- dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
-
- memcpy(dr, card_base_routes, num_base * sizeof(*dr));
-
- idx = num_base;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right Playback");
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port);
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Left Capture Sense");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- idx++;
- dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port);
- dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "Right Capture Sense");
- if (!dr[idx].sink || !dr[idx].source)
- return -ENOMEM;
-
- *routes = dr;
- *num_routes = num_dr;
-
- return 0;
-}
-
static int avs_ssm4567_probe(struct platform_device *pdev)
{
- struct snd_soc_dapm_route *routes;
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
- int num_routes, ssp_port, ret;
+ int ssp_port, ret;
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
@@ -193,12 +148,6 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
return ret;
}
- ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes);
- if (ret) {
- dev_err(dev, "Failed to create dapm routes: %d", ret);
- return ret;
- }
-
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
@@ -214,8 +163,8 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
- card->dapm_routes = routes;
- card->num_dapm_routes = num_routes;
+ card->dapm_routes = card_base_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
card->disable_route_checks = true;
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 99308ed85277..f472f603ab75 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -662,11 +662,14 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
depends on MFD_INTEL_LPSS || COMPILE_TEST
depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST
depends on SOUNDWIRE
+ select SND_SOC_MAX98363
select SND_SOC_MAX98373_I2C
select SND_SOC_MAX98373_SDW
select SND_SOC_RT700_SDW
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_RT1308_SDW
select SND_SOC_RT1308
select SND_SOC_RT1316_SDW
@@ -674,6 +677,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
select SND_SOC_RT5682_SDW
+ select SND_SOC_CS42L42_SDW
select SND_SOC_DMIC
select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_INTEL_SOF_MAXIM_COMMON
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index d1fd7a2b32db..931415d9cf6f 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -37,11 +37,13 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o
snd-soc-ehl-rt5660-objs := ehl_rt5660.o
snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o
snd-soc-sof-sdw-objs += sof_sdw.o \
- sof_sdw_max98373.o sof_sdw_rt_amp.o \
+ sof_sdw_maxim.o sof_sdw_rt_amp.o \
sof_sdw_rt5682.o sof_sdw_rt700.o \
- sof_sdw_rt711.o sof_sdw_rt711_sdca.o \
- sof_sdw_rt715.o sof_sdw_rt715_sdca.o \
- sof_sdw_dmic.o sof_sdw_hdmi.o
+ sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \
+ sof_sdw_rt712_sdca.o sof_sdw_rt715.o \
+ sof_sdw_rt715_sdca.o sof_sdw_dmic.o \
+ sof_sdw_cs42l42.o \
+ sof_sdw_hdmi.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o
diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c
index d5235c294c4c..fee80638cba2 100644
--- a/sound/soc/intel/boards/ehl_rt5660.c
+++ b/sound/soc/intel/boards/ehl_rt5660.c
@@ -254,7 +254,6 @@ static void hdmi_link_init(struct snd_soc_card *card,
struct sof_card_private *ctx,
struct snd_soc_acpi_mach *mach)
{
- struct snd_soc_dai_link *link;
int i;
if (mach->mach_params.common_hdmi_codec_drv &&
@@ -267,11 +266,8 @@ static void hdmi_link_init(struct snd_soc_card *card,
* if HDMI is not enabled in kernel config, or
* hdmi codec is not supported
*/
- for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++) {
- link = &card->dai_link[i];
- link->codecs[0].name = "snd-soc-dummy";
- link->codecs[0].dai_name = "snd-soc-dummy-dai";
- }
+ for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++)
+ card->dai_link[i].codecs[0] = asoc_dummy_dlc;
}
static int snd_ehl_rt5660_probe(struct platform_device *pdev)
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c
index 879ebba52832..a06e05154ae1 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_generic.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c
@@ -61,9 +61,6 @@ static const struct snd_soc_dapm_route skl_hda_map[] = {
{ "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" },
};
-SND_SOC_DAILINK_DEF(dummy_codec,
- DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")));
-
static int skl_hda_card_late_probe(struct snd_soc_card *card)
{
return skl_hda_hdmi_jack_init(card);
@@ -158,9 +155,8 @@ static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params)
card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets);
if (!ctx->idisp_codec) {
for (i = 0; i < IDISP_DAI_COUNT; i++) {
- skl_hda_be_dai_links[i].codecs = dummy_codec;
- skl_hda_be_dai_links[i].num_codecs =
- ARRAY_SIZE(dummy_codec);
+ skl_hda_be_dai_links[i].codecs = &asoc_dummy_dlc;
+ skl_hda_be_dai_links[i].num_codecs = 1;
}
}
}
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
index e9d190cb13b0..e6695e77d594 100644
--- a/sound/soc/intel/boards/sof_cs42l42.c
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -296,13 +296,6 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
-static struct snd_soc_dai_link_component dummy_component[] = {
- {
- .name = "snd-soc-dummy",
- .dai_name = "snd-soc-dummy-dai",
- }
-};
-
static int create_spk_amp_dai_links(struct device *dev,
struct snd_soc_dai_link *links,
struct snd_soc_dai_link_component *cpus,
@@ -510,8 +503,8 @@ static int create_bt_offload_dai_links(struct device *dev,
goto devm_err;
links[*id].id = *id;
- links[*id].codecs = dummy_component;
- links[*id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[*id].codecs = &asoc_dummy_dlc;
+ links[*id].num_codecs = 1;
links[*id].platforms = platform_component;
links[*id].num_platforms = ARRAY_SIZE(platform_component);
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index adf5852b2c9a..d6c38d8ea2ff 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -393,13 +393,6 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
-static struct snd_soc_dai_link_component dummy_component[] = {
- {
- .name = "snd-soc-dummy",
- .dai_name = "snd-soc-dummy-dai",
- }
-};
-
static int sof_es8336_late_probe(struct snd_soc_card *card)
{
struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card);
@@ -572,8 +565,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (!links[id].name)
return NULL;
links[id].id = id + hdmi_id_offset;
- links[id].codecs = dummy_component;
- links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[id].codecs = &asoc_dummy_dlc;
+ links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_capture = 1;
diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c
index 6794a0249a9a..4fc6e1c6aef3 100644
--- a/sound/soc/intel/boards/sof_nau8825.c
+++ b/sound/soc/intel/boards/sof_nau8825.c
@@ -346,13 +346,6 @@ static struct snd_soc_dai_link_component nau8318_components[] = {
}
};
-static struct snd_soc_dai_link_component dummy_component[] = {
- {
- .name = "snd-soc-dummy",
- .dai_name = "snd-soc-dummy-dai",
- }
-};
-
static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
int ssp_codec,
int ssp_amp,
@@ -532,8 +525,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
if (!links[id].name)
goto devm_err;
- links[id].codecs = dummy_component;
- links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[id].codecs = &asoc_dummy_dlc;
+ links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
@@ -681,6 +674,16 @@ static const struct platform_device_id board_ids[] = {
SOF_BT_OFFLOAD_SSP(2) |
SOF_SSP_BT_OFFLOAD_PRESENT),
},
+ {
+ .name = "rpl_max98373_8825",
+ .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98373_SPEAKER_AMP_PRESENT |
+ SOF_NAU8825_SSP_AMP(1) |
+ SOF_NAU8825_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_pcm512x.c b/sound/soc/intel/boards/sof_pcm512x.c
index 5192e02b3cee..9f673ccf81b5 100644
--- a/sound/soc/intel/boards/sof_pcm512x.c
+++ b/sound/soc/intel/boards/sof_pcm512x.c
@@ -331,8 +331,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
devm_kasprintf(dev, GFP_KERNEL,
"intel-hdmi-hifi%d", i);
} else {
- idisp_components[i - 1].name = "snd-soc-dummy";
- idisp_components[i - 1].dai_name = "snd-soc-dummy-dai";
+ idisp_components[i - 1] = asoc_dummy_dlc;
}
if (!idisp_components[i - 1].dai_name)
goto devm_err;
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 791a59c5f00d..7c034d671cf3 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -243,6 +243,20 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = {
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-ALC1019_ALC5682I_I2S"),
+ },
+ .driver_data = (void *)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(2) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT1019_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(0) |
+ SOF_RT5682_NUM_HDMIDEV(3)
+ ),
+ },
+ {
+ .callback = sof_rt5682_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(2) |
@@ -607,13 +621,6 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
-static struct snd_soc_dai_link_component dummy_component[] = {
- {
- .name = "snd-soc-dummy",
- .dai_name = "snd-soc-dummy-dai",
- }
-};
-
#define IDISP_CODEC_MASK 0x4
static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
@@ -745,8 +752,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (!idisp_components[i - 1].dai_name)
goto devm_err;
} else {
- idisp_components[i - 1].name = "snd-soc-dummy";
- idisp_components[i - 1].dai_name = "snd-soc-dummy-dai";
+ idisp_components[i - 1] = asoc_dummy_dlc;
}
links[id].codecs = &idisp_components[i - 1];
@@ -841,8 +847,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
if (!links[id].name)
goto devm_err;
- links[id].codecs = dummy_component;
- links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[id].codecs = &asoc_dummy_dlc;
+ links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
@@ -989,14 +995,13 @@ static const struct platform_device_id board_ids[] = {
.name = "sof_rt5682",
},
{
- .name = "tgl_mx98357_rt5682",
+ .name = "cml_rt1015_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_MCLK_24MHZ |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
- SOF_RT5682_SSP_AMP(1) |
- SOF_RT5682_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
+ SOF_RT1015_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1)),
},
{
.name = "jsl_rt5682_rt1015",
@@ -1008,33 +1013,38 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_SSP_AMP(1)),
},
{
- .name = "tgl_mx98373_rt5682",
+ .name = "jsl_rt5682_mx98360",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_MCLK_24MHZ |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
- SOF_MAX98373_SPEAKER_AMP_PRESENT |
- SOF_RT5682_SSP_AMP(1) |
- SOF_RT5682_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
+ SOF_MAX98360A_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1)),
},
{
- .name = "jsl_rt5682_mx98360",
+ .name = "jsl_rt5682_rt1015p",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_MCLK_24MHZ |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
- SOF_MAX98360A_SPEAKER_AMP_PRESENT |
+ SOF_RT1015P_SPEAKER_AMP_PRESENT |
SOF_RT5682_SSP_AMP(1)),
},
{
- .name = "cml_rt1015_rt5682",
+ .name = "jsl_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_MCLK_24MHZ |
+ SOF_RT5682_SSP_CODEC(0)),
+ },
+ {
+ .name = "tgl_mx98357_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
- SOF_RT1015_SPEAKER_AMP_PRESENT |
- SOF_RT5682_SSP_AMP(1)),
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
.name = "tgl_rt1011_rt5682",
@@ -1048,13 +1058,15 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
- .name = "jsl_rt5682_rt1015p",
+ .name = "tgl_mx98373_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
- SOF_RT5682_MCLK_24MHZ |
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
- SOF_RT1015P_SPEAKER_AMP_PRESENT |
- SOF_RT5682_SSP_AMP(1)),
+ SOF_MAX98373_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
.name = "adl_mx98373_rt5682",
@@ -1128,6 +1140,17 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
+ .name = "rpl_rt1019_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT1019_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
.name = "mtl_mx98357_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(0) |
@@ -1147,10 +1170,13 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_NUM_HDMIDEV(4)),
},
{
- .name = "jsl_rt5682",
+ .name = "mtl_rt1019_rt5682",
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
- SOF_RT5682_MCLK_24MHZ |
- SOF_RT5682_SSP_CODEC(0)),
+ SOF_RT5682_SSP_CODEC(2) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT1019_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(0) |
+ SOF_RT5682_NUM_HDMIDEV(3)),
},
{ }
};
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 144f082c63fd..dbee8c98ff01 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -22,11 +22,16 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override");
#define INC_ID(BE, CPU, LINK) do { (BE)++; (CPU)++; (LINK)++; } while (0)
+#define SDW_MAX_LINKS 4
+
+/* To store SDW Pin index for each SoundWire link */
+static unsigned int sdw_pin_index[SDW_MAX_LINKS];
+
static void log_quirks(struct device *dev)
{
- if (SOF_RT711_JDSRC(sof_sdw_quirk))
+ if (SOF_JACK_JDSRC(sof_sdw_quirk))
dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
- SOF_RT711_JDSRC(sof_sdw_quirk));
+ SOF_JACK_JDSRC(sof_sdw_quirk));
if (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n");
if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
@@ -192,6 +197,20 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
RT711_JD1),
},
{
+ /*
+ * this entry covers HP Spectre x360 where the DMI information
+ * changed somehow
+ */
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8709"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOF_SDW_PCH_DMIC |
+ RT711_JD1),
+ },
+ {
/* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */
.callback = sof_sdw_quirk_cb,
.matches = {
@@ -360,6 +379,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
{
.callback = sof_sdw_quirk_cb,
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"),
+ },
+ /* No Jack */
+ .driver_data = (void *)SOF_SDW_TGL_HDMI,
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16-k0xxx"),
},
@@ -371,6 +399,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA")
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ RT711_JD2 |
+ SOF_SDW_FOUR_SPK),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"),
},
/* No Jack */
@@ -413,7 +451,32 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"),
},
- .driver_data = (void *)(RT711_JD1 | SOF_SDW_TGL_HDMI),
+ .driver_data = (void *)(RT711_JD1),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"),
+ },
+ .driver_data = (void *)(RT711_JD2_100K),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Rex"),
+ },
+ .driver_data = (void *)(SOF_SDW_PCH_DMIC),
+ },
+ /* LunarLake devices */
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"),
+ },
+ .driver_data = (void *)(RT711_JD2_100K),
},
{}
};
@@ -497,6 +560,55 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
+int sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int ch = params_channels(params);
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
+ unsigned int ch_mask;
+ int num_codecs;
+ int step;
+ int i;
+ int j;
+
+ if (!rtd->dai_link->codec_ch_maps)
+ return 0;
+
+ /* Identical data will be sent to all codecs in playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ch_mask = GENMASK(ch - 1, 0);
+ step = 0;
+ } else {
+ num_codecs = rtd->dai_link->num_codecs;
+
+ if (ch < num_codecs || ch % num_codecs != 0) {
+ dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
+ ch, num_codecs);
+ return -EINVAL;
+ }
+
+ ch_mask = GENMASK(ch / num_codecs - 1, 0);
+ step = hweight_long(ch_mask);
+
+ }
+
+ /*
+ * The captured data will be combined from each cpu DAI if the dai
+ * link has more than one codec DAIs. Set codec channel mask and
+ * ASoC will set the corresponding channel numbers for each cpu dai.
+ */
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id != i)
+ continue;
+ rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step);
+ }
+ }
+ return 0;
+}
+
int sdw_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
@@ -525,6 +637,7 @@ static const struct snd_soc_ops sdw_ops = {
.startup = sdw_startup,
.prepare = sdw_prepare,
.trigger = sdw_trigger,
+ .hw_params = sdw_hw_params,
.hw_free = sdw_hw_free,
.shutdown = sdw_shutdown,
};
@@ -532,134 +645,323 @@ static const struct snd_soc_ops sdw_ops = {
static struct sof_sdw_codec_info codec_info_list[] = {
{
.part_id = 0x700,
- .direction = {true, true},
- .dai_name = "rt700-aif1",
- .init = sof_sdw_rt700_init,
- .codec_type = SOF_SDW_CODEC_TYPE_JACK,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt700-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt700_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x711,
.version_id = 3,
- .direction = {true, true},
- .dai_name = "rt711-sdca-aif1",
- .init = sof_sdw_rt711_sdca_init,
- .exit = sof_sdw_rt711_sdca_exit,
- .codec_type = SOF_SDW_CODEC_TYPE_JACK,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt711-sdca-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt_sdca_jack_init,
+ .exit = sof_sdw_rt_sdca_jack_exit,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x711,
.version_id = 2,
- .direction = {true, true},
- .dai_name = "rt711-aif1",
- .init = sof_sdw_rt711_init,
- .exit = sof_sdw_rt711_exit,
- .codec_type = SOF_SDW_CODEC_TYPE_JACK,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt711-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt711_init,
+ .exit = sof_sdw_rt711_exit,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x712,
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt712-sdca-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt_sdca_jack_init,
+ .exit = sof_sdw_rt_sdca_jack_exit,
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt712-sdca-aif2",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
+ .init = sof_sdw_rt712_spk_init,
+ },
+ },
+ .dai_num = 2,
+ },
+ {
+ .part_id = 0x1712,
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt712-sdca-dmic-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt712_sdca_dmic_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x713,
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt712-sdca-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt_sdca_jack_init,
+ .exit = sof_sdw_rt_sdca_jack_exit,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x1713,
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt712-sdca-dmic-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt712_sdca_dmic_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x1308,
.acpi_id = "10EC1308",
- .direction = {true, false},
- .dai_name = "rt1308-aif",
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "rt1308-aif",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
+ .init = sof_sdw_rt_amp_init,
+ .exit = sof_sdw_rt_amp_exit,
+ },
+ },
+ .dai_num = 1,
.ops = &sof_sdw_rt1308_i2s_ops,
- .init = sof_sdw_rt_amp_init,
- .exit = sof_sdw_rt_amp_exit,
- .codec_type = SOF_SDW_CODEC_TYPE_AMP,
},
{
.part_id = 0x1316,
- .direction = {true, true},
- .dai_name = "rt1316-aif",
- .init = sof_sdw_rt_amp_init,
- .exit = sof_sdw_rt_amp_exit,
- .codec_type = SOF_SDW_CODEC_TYPE_AMP,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt1316-aif",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
+ .init = sof_sdw_rt_amp_init,
+ .exit = sof_sdw_rt_amp_exit,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x1318,
- .direction = {true, true},
- .dai_name = "rt1318-aif",
- .init = sof_sdw_rt_amp_init,
- .codec_type = SOF_SDW_CODEC_TYPE_AMP,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt1318-aif",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
+ .init = sof_sdw_rt_amp_init,
+ .exit = sof_sdw_rt_amp_exit,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x714,
.version_id = 3,
- .direction = {false, true},
.ignore_pch_dmic = true,
- .dai_name = "rt715-aif2",
- .init = sof_sdw_rt715_sdca_init,
- .codec_type = SOF_SDW_CODEC_TYPE_MIC,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-aif2",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt715_sdca_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x715,
.version_id = 3,
- .direction = {false, true},
.ignore_pch_dmic = true,
- .dai_name = "rt715-aif2",
- .init = sof_sdw_rt715_sdca_init,
- .codec_type = SOF_SDW_CODEC_TYPE_MIC,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-aif2",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt715_sdca_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x714,
.version_id = 2,
- .direction = {false, true},
.ignore_pch_dmic = true,
- .dai_name = "rt715-aif2",
- .init = sof_sdw_rt715_init,
- .codec_type = SOF_SDW_CODEC_TYPE_MIC,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-aif2",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt715_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x715,
.version_id = 2,
- .direction = {false, true},
.ignore_pch_dmic = true,
- .dai_name = "rt715-aif2",
- .init = sof_sdw_rt715_init,
- .codec_type = SOF_SDW_CODEC_TYPE_MIC,
+ .dais = {
+ {
+ .direction = {false, true},
+ .dai_name = "rt715-aif2",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt715_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x8373,
- .direction = {true, true},
- .dai_name = "max98373-aif1",
- .init = sof_sdw_mx8373_init,
- .codec_type = SOF_SDW_CODEC_TYPE_AMP,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "max98373-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
+ .init = sof_sdw_maxim_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x8363,
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "max98363-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
+ .init = sof_sdw_maxim_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x5682,
- .direction = {true, true},
- .dai_name = "rt5682-sdw",
- .init = sof_sdw_rt5682_init,
- .codec_type = SOF_SDW_CODEC_TYPE_JACK,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt5682-sdw",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt5682_init,
+ },
+ },
+ .dai_num = 1,
+ },
+ {
+ .part_id = 0x4242,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "cs42l42-sdw",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_cs42l42_init,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0xaaaa, /* generic codec mockup */
.version_id = 0,
- .direction = {true, true},
- .dai_name = "sdw-mockup-aif1",
- .init = NULL,
- .codec_type = SOF_SDW_CODEC_TYPE_JACK,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = NULL,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0xaa55, /* headset codec mockup */
.version_id = 0,
- .direction = {true, true},
- .dai_name = "sdw-mockup-aif1",
- .init = NULL,
- .codec_type = SOF_SDW_CODEC_TYPE_JACK,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = NULL,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x55aa, /* amplifier mockup */
.version_id = 0,
- .direction = {true, false},
- .dai_name = "sdw-mockup-aif1",
- .init = NULL,
- .codec_type = SOF_SDW_CODEC_TYPE_AMP,
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "sdw-mockup-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
+ .init = NULL,
+ },
+ },
+ .dai_num = 1,
},
{
.part_id = 0x5555,
.version_id = 0,
- .direction = {false, true},
- .dai_name = "sdw-mockup-aif1",
- .codec_type = SOF_SDW_CODEC_TYPE_MIC,
+ .dais = {
+ {
+ .dai_name = "sdw-mockup-aif1",
+ .direction = {false, true},
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = NULL,
+ },
+ },
+ .dai_num = 1,
},
};
@@ -711,10 +1013,10 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li
int *sdw_be_num, int *sdw_cpu_dai_num)
{
const struct snd_soc_acpi_link_adr *link;
- int _codec_type = SOF_SDW_CODEC_TYPE_JACK;
bool group_visited[SDW_MAX_GROUPS];
bool no_aggregation;
int i;
+ int j;
no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
*sdw_cpu_dai_num = 0;
@@ -728,6 +1030,7 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li
for (link = links; link->num_adr; link++) {
const struct snd_soc_acpi_endpoint *endpoint;
+ struct sof_sdw_codec_info *codec_info;
int codec_index;
int stream;
u64 adr;
@@ -737,26 +1040,23 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li
codec_index = find_codec_info_part(adr);
if (codec_index < 0)
return codec_index;
-
- if (codec_info_list[codec_index].codec_type < _codec_type)
- dev_warn(dev,
- "Unexpected address table ordering. Expected order: jack -> amp -> mic\n");
-
- _codec_type = codec_info_list[codec_index].codec_type;
+ codec_info = &codec_info_list[codec_index];
endpoint = link->adr_d[i].endpoints;
- /* count DAI number for playback and capture */
- for_each_pcm_streams(stream) {
- if (!codec_info_list[codec_index].direction[stream])
- continue;
+ for (j = 0; j < codec_info->dai_num; j++) {
+ /* count DAI number for playback and capture */
+ for_each_pcm_streams(stream) {
+ if (!codec_info->dais[j].direction[stream])
+ continue;
- (*sdw_cpu_dai_num)++;
+ (*sdw_cpu_dai_num)++;
- /* count BE for each non-aggregated slave or group */
- if (!endpoint->aggregated || no_aggregation ||
- !group_visited[endpoint->group_id])
- (*sdw_be_num)++;
+ /* count BE for each non-aggregated slave or group */
+ if (!endpoint->aggregated || no_aggregation ||
+ !group_visited[endpoint->group_id])
+ (*sdw_be_num)++;
+ }
}
if (endpoint->aggregated)
@@ -832,7 +1132,8 @@ static int create_codec_dai_name(struct device *dev,
struct snd_soc_codec_conf *codec_conf,
int codec_count,
int *codec_conf_index,
- int adr_index)
+ int adr_index,
+ int dai_index)
{
int _codec_index = -1;
int i;
@@ -888,7 +1189,7 @@ static int create_codec_dai_name(struct device *dev,
_codec_index = codec_index;
codec[comp_index].dai_name =
- codec_info_list[codec_index].dai_name;
+ codec_info_list[codec_index].dais[dai_index].dai_name;
codec_conf[*codec_conf_index].dlc = codec[comp_index];
codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix;
@@ -902,35 +1203,43 @@ static int create_codec_dai_name(struct device *dev,
static int set_codec_init_func(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
- bool playback, int group_id)
+ bool playback, int group_id, int adr_index, int dai_index)
{
- int i;
+ int i = adr_index;
do {
/*
* Initialize the codec. If codec is part of an aggregated
* group (group_id>0), initialize all codecs belonging to
* same group.
+ * The first link should start with link->adr_d[adr_index]
+ * because that is the device that we want to initialize and
+ * we should end immediately if it is not aggregated (group_id=0)
*/
- for (i = 0; i < link->num_adr; i++) {
+ for ( ; i < link->num_adr; i++) {
int codec_index;
codec_index = find_codec_info_part(link->adr_d[i].adr);
if (codec_index < 0)
return codec_index;
+
/* The group_id is > 0 iff the codec is aggregated */
if (link->adr_d[i].endpoints->group_id != group_id)
continue;
- if (codec_info_list[codec_index].init)
- codec_info_list[codec_index].init(card,
+
+ if (codec_info_list[codec_index].dais[dai_index].init)
+ codec_info_list[codec_index].dais[dai_index].init(card,
link,
dai_links,
&codec_info_list[codec_index],
playback);
+ if (!group_id)
+ return 0;
}
+ i = 0;
link++;
- } while (link->mask && group_id);
+ } while (link->mask);
return 0;
}
@@ -1022,6 +1331,17 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
return 0;
}
+static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps,
+ int codec_num, int cpu_num)
+{
+ int step;
+ int i;
+
+ step = codec_num / cpu_num;
+ for (i = 0; i < codec_num; i++)
+ sdw_codec_ch_maps[i].connected_cpu_id = i / step;
+}
+
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
static int create_sdw_dailink(struct snd_soc_card *card,
@@ -1035,19 +1355,21 @@ static int create_sdw_dailink(struct snd_soc_card *card,
int codec_count, int *link_id,
int *codec_conf_index,
bool *ignore_pch_dmic,
- bool append_codec_type,
- int adr_index)
+ bool append_dai_type,
+ int adr_index,
+ int dai_index)
{
const struct snd_soc_acpi_link_adr *link_next;
struct snd_soc_dai_link_component *codecs;
+ struct sof_sdw_codec_info *codec_info;
int cpu_dai_id[SDW_MAX_CPU_DAIS];
int cpu_dai_num, cpu_dai_index;
unsigned int group_id;
int codec_idx = 0;
- int i = 0, j = 0;
int codec_index;
int codec_num;
int stream;
+ int i = 0;
int ret;
int k;
@@ -1075,7 +1397,8 @@ static int create_sdw_dailink(struct snd_soc_card *card,
continue;
ret = create_codec_dai_name(dev, link_next, codecs, codec_idx,
- codec_conf, codec_count, codec_conf_index, adr_index);
+ codec_conf, codec_count, codec_conf_index,
+ adr_index, dai_index);
if (ret < 0)
return ret;
@@ -1088,25 +1411,14 @@ static int create_sdw_dailink(struct snd_soc_card *card,
codec_index = find_codec_info_part(link->adr_d[adr_index].adr);
if (codec_index < 0)
return codec_index;
+ codec_info = &codec_info_list[codec_index];
- if (codec_info_list[codec_index].ignore_pch_dmic)
+ if (codec_info->ignore_pch_dmic)
*ignore_pch_dmic = true;
- /* Shift the first amplifier's *link_id to SDW_AMP_DAI_ID */
- if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_AMP &&
- *link_id < SDW_AMP_DAI_ID)
- *link_id = SDW_AMP_DAI_ID;
-
- /*
- * DAI ID is fixed at SDW_DMIC_DAI_ID for MICs to
- * keep sdw DMIC and HDMI setting static in UCM
- */
- if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_MIC &&
- *link_id < SDW_DMIC_DAI_ID)
- *link_id = SDW_DMIC_DAI_ID;
-
cpu_dai_index = *cpu_id;
for_each_pcm_streams(stream) {
+ struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps;
char *name, *cpu_name;
int playback, capture;
static const char * const sdw_stream_name[] = {
@@ -1116,14 +1428,25 @@ static int create_sdw_dailink(struct snd_soc_card *card,
"SDW%d-Capture-%s",
};
- if (!codec_info_list[codec_index].direction[stream])
+ if (!codec_info->dais[dai_index].direction[stream])
continue;
+ *link_id = codec_info->dais[dai_index].dailink[stream];
+ if (*link_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *link_id);
+ return -EINVAL;
+ }
+
+ sdw_codec_ch_maps = devm_kcalloc(dev, codec_num,
+ sizeof(*sdw_codec_ch_maps), GFP_KERNEL);
+ if (!sdw_codec_ch_maps)
+ return -ENOMEM;
+
/* create stream name according to first link id */
- if (append_codec_type) {
+ if (append_dai_type) {
name = devm_kasprintf(dev, GFP_KERNEL,
sdw_stream_name[stream + 2], cpu_dai_id[0],
- type_strings[codec_info_list[codec_index].codec_type]);
+ type_strings[codec_info->dais[dai_index].dai_type]);
} else {
name = devm_kasprintf(dev, GFP_KERNEL,
sdw_stream_name[stream], cpu_dai_id[0]);
@@ -1138,7 +1461,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
for (k = 0; k < cpu_dai_num; k++) {
cpu_name = devm_kasprintf(dev, GFP_KERNEL,
"SDW%d Pin%d", cpu_dai_id[k],
- j + SDW_INTEL_BIDIR_PDI_BASE);
+ sdw_pin_index[cpu_dai_id[k]]++);
if (!cpu_name)
return -ENOMEM;
@@ -1179,15 +1502,16 @@ static int create_sdw_dailink(struct snd_soc_card *card,
*/
dai_links[*link_index].nonatomic = true;
+ set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num);
+ dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps;
ret = set_codec_init_func(card, link, dai_links + (*link_index)++,
- playback, group_id);
+ playback, group_id, adr_index, dai_index);
if (ret < 0) {
dev_err(dev, "failed to init codec %d", codec_index);
return ret;
}
*cpu_id += cpu_dai_num;
- j++;
}
return 0;
@@ -1203,6 +1527,7 @@ static int sof_card_codec_conf_alloc(struct device *dev,
const struct snd_soc_acpi_link_adr *adr_link;
struct snd_soc_codec_conf *c_conf;
int num_codecs = 0;
+ int codec_index;
int i;
adr_link = mach_params->links;
@@ -1217,8 +1542,11 @@ static int sof_card_codec_conf_alloc(struct device *dev,
adr_link->adr_d[i].adr);
return -EINVAL;
}
+ codec_index = find_codec_info_part(adr_link->adr_d[i].adr);
+ if (codec_index < 0)
+ return codec_index;
+ num_codecs += codec_info_list[codec_index].dai_num;
}
- num_codecs += adr_link->num_adr;
}
c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL);
@@ -1243,7 +1571,7 @@ static int sof_card_dai_links_create(struct device *dev,
const struct snd_soc_acpi_link_adr *adr_link;
struct snd_soc_dai_link_component *cpus;
struct snd_soc_codec_conf *codec_conf;
- bool append_codec_type = false;
+ bool append_dai_type = false;
bool ignore_pch_dmic = false;
int codec_conf_count;
int codec_conf_index = 0;
@@ -1255,6 +1583,7 @@ static int sof_card_dai_links_create(struct device *dev,
int total_cpu_dai_num;
int sdw_cpu_dai_num;
int i, j, be_id = 0;
+ int codec_index;
int cpu_id = 0;
int comp_num;
int ret;
@@ -1335,6 +1664,9 @@ static int sof_card_dai_links_create(struct device *dev,
for (i = 0; i < SDW_MAX_GROUPS; i++)
group_generated[i] = false;
+ for (i = 0; i < SDW_MAX_LINKS; i++)
+ sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE;
+
for (; adr_link->num_adr; adr_link++) {
/*
* If there are two or more different devices on the same sdw link, we have to
@@ -1343,12 +1675,20 @@ static int sof_card_dai_links_create(struct device *dev,
* snd_soc_acpi_adr_device array. They won't be described in different adr_links.
*/
for (i = 0; i < adr_link->num_adr; i++) {
+ /* find codec info to get dai_num */
+ codec_index = find_codec_info_part(adr_link->adr_d[i].adr);
+ if (codec_index < 0)
+ return codec_index;
+ if (codec_info_list[codec_index].dai_num > 1) {
+ append_dai_type = true;
+ goto out;
+ }
for (j = 0; j < i; j++) {
if ((SDW_PART_ID(adr_link->adr_d[i].adr) !=
SDW_PART_ID(adr_link->adr_d[j].adr)) ||
(SDW_MFG_ID(adr_link->adr_d[i].adr) !=
SDW_MFG_ID(adr_link->adr_d[j].adr))) {
- append_codec_type = true;
+ append_dai_type = true;
goto out;
}
}
@@ -1373,15 +1713,22 @@ out:
group_generated[endpoint->group_id])
continue;
- ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num,
- sdw_cpu_dai_num, cpus, adr_link,
- &cpu_id, group_generated,
- codec_conf, codec_conf_count,
- &be_id, &codec_conf_index,
- &ignore_pch_dmic, append_codec_type, i);
- if (ret < 0) {
- dev_err(dev, "failed to create dai link %d", link_index);
- return ret;
+ /* find codec info to get dai_num */
+ codec_index = find_codec_info_part(adr_link->adr_d[i].adr);
+ if (codec_index < 0)
+ return codec_index;
+
+ for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) {
+ ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num,
+ sdw_cpu_dai_num, cpus, adr_link,
+ &cpu_id, group_generated,
+ codec_conf, codec_conf_count,
+ &be_id, &codec_conf_index,
+ &ignore_pch_dmic, append_dai_type, i, j);
+ if (ret < 0) {
+ dev_err(dev, "failed to create dai link %d", link_index);
+ return ret;
+ }
}
}
}
@@ -1420,18 +1767,19 @@ SSP:
return -ENOMEM;
ssp_components->name = codec_name;
- ssp_components->dai_name = info->dai_name;
+ /* TODO: support multi codec dai on SSP when it is needed */
+ ssp_components->dai_name = info->dais[0].dai_name;
cpus[cpu_id].dai_name = cpu_name;
- playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK];
- capture = info->direction[SNDRV_PCM_STREAM_CAPTURE];
+ playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK];
+ capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE];
init_dai_link(dev, links + link_index, be_id, name,
playback, capture,
cpus + cpu_id, 1,
ssp_components, 1,
NULL, info->ops);
- ret = info->init(card, NULL, links + link_index, info, 0);
+ ret = info->dais[0].init(card, NULL, links + link_index, info, 0);
if (ret < 0)
return ret;
@@ -1488,8 +1836,7 @@ HDMI:
if (!idisp_components[i].dai_name)
return -ENOMEM;
} else {
- idisp_components[i].name = "snd-soc-dummy";
- idisp_components[i].dai_name = "snd-soc-dummy-dai";
+ idisp_components[i] = asoc_dummy_dlc;
}
cpu_name = devm_kasprintf(dev, GFP_KERNEL,
@@ -1514,21 +1861,13 @@ HDMI:
if (!name)
return -ENOMEM;
- ssp_components = devm_kzalloc(dev, sizeof(*ssp_components),
- GFP_KERNEL);
- if (!ssp_components)
- return -ENOMEM;
-
- ssp_components->name = "snd-soc-dummy";
- ssp_components->dai_name = "snd-soc-dummy-dai";
-
cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port);
if (!cpu_name)
return -ENOMEM;
cpus[cpu_id].dai_name = cpu_name;
init_dai_link(dev, links + link_index, be_id, name, 1, 1,
- cpus + cpu_id, 1, ssp_components, 1, NULL, NULL);
+ cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL);
}
card->dai_link = links;
@@ -1570,6 +1909,24 @@ static struct snd_soc_card card_sof_sdw = {
.late_probe = sof_sdw_card_late_probe,
};
+/* helper to get the link that the codec DAI is used */
+static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card,
+ const char *dai_name)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+ int j;
+
+ for_each_card_prelinks(card, i, link) {
+ for (j = 0; j < link->num_codecs; j++) {
+ /* Check each codec in a link */
+ if (!strcmp(link->codecs[j].dai_name, dai_name))
+ return link;
+ }
+ }
+ return NULL;
+}
+
static void mc_dailink_exit_loop(struct snd_soc_card *card)
{
struct snd_soc_dai_link *link;
@@ -1577,16 +1934,18 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card)
int i, j;
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
- if (!codec_info_list[i].exit)
- continue;
- /*
- * We don't need to call .exit function if there is no matched
- * dai link found.
- */
- for_each_card_prelinks(card, j, link) {
- if (!strcmp(link->codecs[0].dai_name,
- codec_info_list[i].dai_name)) {
- ret = codec_info_list[i].exit(card, link);
+ for (j = 0; j < codec_info_list[i].dai_num; j++) {
+ /* Check each dai in codec_info_lis to see if it is used in the link */
+ if (!codec_info_list[i].dais[j].exit)
+ continue;
+ /*
+ * We don't need to call .exit function if there is no matched
+ * dai link found.
+ */
+ link = mc_find_codec_dai_used(card, codec_info_list[i].dais[j].dai_name);
+ if (link) {
+ /* Do the .exit function if the codec dai is used in the link */
+ ret = codec_info_list[i].dais[j].exit(card, link);
if (ret)
dev_warn(card->dev,
"codec exit failed %d\n",
diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h
index 081ab7eac5b6..37402170d5f9 100644
--- a/sound/soc/intel/boards/sof_sdw_common.h
+++ b/sound/soc/intel/boards/sof_sdw_common.h
@@ -15,7 +15,11 @@
#define MAX_NO_PROPS 2
#define MAX_HDMI_NUM 4
-#define SDW_AMP_DAI_ID 2
+#define SDW_UNUSED_DAI_ID -1
+#define SDW_JACK_OUT_DAI_ID 0
+#define SDW_JACK_IN_DAI_ID 1
+#define SDW_AMP_OUT_DAI_ID 2
+#define SDW_AMP_IN_DAI_ID 3
#define SDW_DMIC_DAI_ID 4
#define SDW_MAX_CPU_DAIS 16
#define SDW_INTEL_BIDIR_PDI_BASE 2
@@ -37,7 +41,7 @@ enum {
SOF_I2S_SSP5 = BIT(5),
};
-#define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
+#define SOF_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
#define SOF_SDW_FOUR_SPK BIT(4)
#define SOF_SDW_TGL_HDMI BIT(5)
#define SOF_SDW_PCH_DMIC BIT(6)
@@ -52,28 +56,37 @@ enum {
(((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18)
-#define SOF_SDW_CODEC_TYPE_JACK 0
-#define SOF_SDW_CODEC_TYPE_AMP 1
-#define SOF_SDW_CODEC_TYPE_MIC 2
+#define SOF_SDW_DAI_TYPE_JACK 0
+#define SOF_SDW_DAI_TYPE_AMP 1
+#define SOF_SDW_DAI_TYPE_MIC 2
+
+#define SOF_SDW_MAX_DAI_NUM 3
+
+struct sof_sdw_codec_info;
+
+struct sof_sdw_dai_info {
+ const bool direction[2]; /* playback & capture support */
+ const char *dai_name;
+ const int dai_type;
+ const int dailink[2]; /* dailink id for each direction */
+ int (*init)(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
+ int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+};
struct sof_sdw_codec_info {
const int part_id;
const int version_id;
- const int codec_type;
int amp_num;
const u8 acpi_id[ACPI_ID_LEN];
- const bool direction[2]; // playback & capture support
const bool ignore_pch_dmic;
- const char *dai_name;
const struct snd_soc_ops *ops;
+ struct sof_sdw_dai_info dais[SOF_SDW_MAX_DAI_NUM];
+ const int dai_num;
- int (*init)(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback);
-
- int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
int (*codec_card_late_probe)(struct snd_soc_card *card);
};
@@ -90,6 +103,8 @@ extern unsigned long sof_sdw_quirk;
int sdw_startup(struct snd_pcm_substream *substream);
int sdw_prepare(struct snd_pcm_substream *substream);
int sdw_trigger(struct snd_pcm_substream *substream, int cmd);
+int sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
int sdw_hw_free(struct snd_pcm_substream *substream);
void sdw_shutdown(struct snd_pcm_substream *substream);
@@ -110,12 +125,30 @@ int sof_sdw_rt711_init(struct snd_soc_card *card,
int sof_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
/* RT711-SDCA support */
-int sof_sdw_rt711_sdca_init(struct snd_soc_card *card,
+int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
+int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+
+/* RT712-SDCA support */
+int sof_sdw_rt712_sdca_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback);
-int sof_sdw_rt711_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+int sof_sdw_rt712_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+int sof_sdw_rt712_spk_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
+int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
/* RT700 support */
int sof_sdw_rt700_init(struct snd_soc_card *card,
@@ -151,12 +184,12 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback);
-/* MAX98373 support */
-int sof_sdw_mx8373_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback);
+/* MAXIM codec support */
+int sof_sdw_maxim_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
/* RT5682 support */
int sof_sdw_rt5682_init(struct snd_soc_card *card,
@@ -165,4 +198,11 @@ int sof_sdw_rt5682_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback);
+/* CS42L42 support */
+int sof_sdw_cs42l42_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
+
#endif
diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c
new file mode 100644
index 000000000000..c4a16e4c9f69
--- /dev/null
+++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023 Intel Corporation
+
+/*
+ * sof_sdw_cs42l42 - Helpers to handle CS42L42 from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include "sof_sdw_common.h"
+
+static const struct snd_soc_dapm_widget cs42l42_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route cs42l42_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone", NULL, "cs42l42 HP"},
+
+ /* other jacks */
+ {"cs42l42 HS", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new cs42l42_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_jack_pin cs42l42_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_jack *jack;
+ int ret;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:cs42l42",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_add_card_controls(card, cs42l42_controls,
+ ARRAY_SIZE(cs42l42_controls));
+ if (ret) {
+ dev_err(card->dev, "cs42l42 control addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, cs42l42_widgets,
+ ARRAY_SIZE(cs42l42_widgets));
+ if (ret) {
+ dev_err(card->dev, "cs42l42 widgets addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, cs42l42_map,
+ ARRAY_SIZE(cs42l42_map));
+
+ if (ret) {
+ dev_err(card->dev, "cs42l42 map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ cs42l42_jack_pins,
+ ARRAY_SIZE(cs42l42_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+
+int sof_sdw_cs42l42_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
+{
+ /*
+ * headset should be initialized once.
+ * Do it with dai link for playback.
+ */
+ if (!playback)
+ return 0;
+
+ dai_links->init = cs42l42_rtd_init;
+
+ return 0;
+}
diff --git a/sound/soc/intel/boards/sof_sdw_max98373.c b/sound/soc/intel/boards/sof_sdw_maxim.c
index 3d7df58c0f1d..414c4d8dac77 100644
--- a/sound/soc/intel/boards/sof_sdw_max98373.c
+++ b/sound/soc/intel/boards/sof_sdw_maxim.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2020 Intel Corporation
//
-// sof_sdw_max98373 - Helpers to handle 2x MAX98373
+// sof_sdw_maxim - Helpers to handle maxim codecs
// codec devices from generic machine driver
#include <linux/device.h>
@@ -13,12 +13,16 @@
#include "sof_sdw_common.h"
#include "sof_maxim_common.h"
-static const struct snd_soc_dapm_widget mx8373_widgets[] = {
+static int maxim_part_id;
+#define SOF_SDW_PART_ID_MAX98363 0x8363
+#define SOF_SDW_PART_ID_MAX98373 0x8373
+
+static const struct snd_soc_dapm_widget maxim_widgets[] = {
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
-static const struct snd_kcontrol_new mx8373_controls[] = {
+static const struct snd_kcontrol_new maxim_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
@@ -29,22 +33,25 @@ static int spk_init(struct snd_soc_pcm_runtime *rtd)
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:mx8373",
- card->components);
+ "%s spk:mx%04x",
+ card->components, maxim_part_id);
if (!card->components)
return -ENOMEM;
- ret = snd_soc_add_card_controls(card, mx8373_controls,
- ARRAY_SIZE(mx8373_controls));
+ dev_dbg(card->dev, "soundwire maxim card components assigned : %s\n",
+ card->components);
+
+ ret = snd_soc_add_card_controls(card, maxim_controls,
+ ARRAY_SIZE(maxim_controls));
if (ret) {
- dev_err(card->dev, "mx8373 ctrls addition failed: %d\n", ret);
+ dev_err(card->dev, "mx%04x ctrls addition failed: %d\n", maxim_part_id, ret);
return ret;
}
- ret = snd_soc_dapm_new_controls(&card->dapm, mx8373_widgets,
- ARRAY_SIZE(mx8373_widgets));
+ ret = snd_soc_dapm_new_controls(&card->dapm, maxim_widgets,
+ ARRAY_SIZE(maxim_widgets));
if (ret) {
- dev_err(card->dev, "mx8373 widgets addition failed: %d\n", ret);
+ dev_err(card->dev, "mx%04x widgets addition failed: %d\n", maxim_part_id, ret);
return ret;
}
@@ -116,6 +123,7 @@ static const struct snd_soc_ops max_98373_sdw_ops = {
.startup = sdw_startup,
.prepare = mx8373_sdw_prepare,
.trigger = sdw_trigger,
+ .hw_params = sdw_hw_params,
.hw_free = mx8373_sdw_hw_free,
.shutdown = sdw_shutdown,
};
@@ -130,19 +138,30 @@ static int mx8373_sdw_late_probe(struct snd_soc_card *card)
return snd_soc_dapm_sync(dapm);
}
-int sof_sdw_mx8373_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback)
+int sof_sdw_maxim_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
{
info->amp_num++;
if (info->amp_num == 2)
dai_links->init = spk_init;
- info->codec_card_late_probe = mx8373_sdw_late_probe;
-
- dai_links->ops = &max_98373_sdw_ops;
-
+ maxim_part_id = info->part_id;
+ switch (maxim_part_id) {
+ case SOF_SDW_PART_ID_MAX98363:
+ /* Default ops are set in function init_dai_link.
+ * called as part of function create_sdw_dailink
+ */
+ break;
+ case SOF_SDW_PART_ID_MAX98373:
+ info->codec_card_late_probe = mx8373_sdw_late_probe;
+ dai_links->ops = &max_98373_sdw_ops;
+ break;
+ default:
+ dev_err(card->dev, "Invalid maxim_part_id %#x\n", maxim_part_id);
+ return -EINVAL;
+ }
return 0;
}
diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c
index 8291967f23f3..2b05e2a707de 100644
--- a/sound/soc/intel/boards/sof_sdw_rt711.c
+++ b/sound/soc/intel/boards/sof_sdw_rt711.c
@@ -27,9 +27,9 @@ static int rt711_add_codec_device_props(struct device *sdw_dev)
struct fwnode_handle *fwnode;
int ret;
- if (!SOF_RT711_JDSRC(sof_sdw_quirk))
+ if (!SOF_JACK_JDSRC(sof_sdw_quirk))
return 0;
- props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_RT711_JDSRC(sof_sdw_quirk));
+ props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_JACK_JDSRC(sof_sdw_quirk));
fwnode = fwnode_create_software_node(props, NULL);
if (IS_ERR(fwnode))
diff --git a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c
new file mode 100644
index 000000000000..84c8025d24e3
--- /dev/null
+++ b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023 Intel Corporation
+
+/*
+ * sof_sdw_rt712_sdca - Helpers to handle RT712-SDCA from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include "sof_sdw_common.h"
+
+static const struct snd_soc_dapm_widget rt712_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/*
+ * dapm routes for rt712 spk will be registered dynamically according
+ * to the number of rt712 spk used. The first two entries will be registered
+ * for one codec case, and the last two entries are also registered
+ * if two rt712s are used.
+ */
+static const struct snd_soc_dapm_route rt712_spk_map[] = {
+ { "Speaker", NULL, "rt712 SPOL" },
+ { "Speaker", NULL, "rt712 SPOR" },
+};
+
+static const struct snd_kcontrol_new rt712_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s spk:rt712",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_add_card_controls(card, rt712_spk_controls,
+ ARRAY_SIZE(rt712_spk_controls));
+ if (ret) {
+ dev_err(card->dev, "rt712 spk controls addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt712_spk_widgets,
+ ARRAY_SIZE(rt712_spk_widgets));
+ if (ret) {
+ dev_err(card->dev, "rt712 spk widgets addition failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt712_spk_map, ARRAY_SIZE(rt712_spk_map));
+ if (ret)
+ dev_err(rtd->dev, "failed to add SPK map: %d\n", ret);
+
+ return ret;
+}
+
+int sof_sdw_rt712_spk_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
+{
+ dai_links->init = rt712_spk_init;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:rt712-sdca-dmic",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
+{
+ dai_links->init = rt712_sdca_dmic_rtd_init;
+
+ return 0;
+}
diff --git a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c
index 7f16304d025b..623e3bebb888 100644
--- a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c
+++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c
@@ -21,16 +21,16 @@
* Note this MUST be called before snd_soc_register_card(), so that the props
* are in place before the codec component driver's probe function parses them.
*/
-static int rt711_sdca_add_codec_device_props(struct device *sdw_dev)
+static int rt_sdca_jack_add_codec_device_props(struct device *sdw_dev)
{
struct property_entry props[MAX_NO_PROPS] = {};
struct fwnode_handle *fwnode;
int ret;
- if (!SOF_RT711_JDSRC(sof_sdw_quirk))
+ if (!SOF_JACK_JDSRC(sof_sdw_quirk))
return 0;
- props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_RT711_JDSRC(sof_sdw_quirk));
+ props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_JACK_JDSRC(sof_sdw_quirk));
fwnode = fwnode_create_software_node(props, NULL);
if (IS_ERR(fwnode))
@@ -43,23 +43,27 @@ static int rt711_sdca_add_codec_device_props(struct device *sdw_dev)
return ret;
}
-static const struct snd_soc_dapm_widget rt711_sdca_widgets[] = {
+static const struct snd_soc_dapm_widget rt_sdca_jack_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
static const struct snd_soc_dapm_route rt711_sdca_map[] = {
- /* Headphones */
{ "Headphone", NULL, "rt711 HP" },
{ "rt711 MIC2", NULL, "Headset Mic" },
};
-static const struct snd_kcontrol_new rt711_sdca_controls[] = {
+static const struct snd_soc_dapm_route rt712_sdca_map[] = {
+ { "Headphone", NULL, "rt712 HP" },
+ { "rt712 MIC2", NULL, "Headset Mic" },
+};
+
+static const struct snd_kcontrol_new rt_sdca_jack_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
-static struct snd_soc_jack_pin rt711_sdca_jack_pins[] = {
+static struct snd_soc_jack_pin rt_sdca_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
@@ -70,7 +74,7 @@ static struct snd_soc_jack_pin rt711_sdca_jack_pins[] = {
},
};
-static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
+static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
@@ -80,30 +84,38 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s hs:rt711-sdca",
- card->components);
+ "%s hs:%s-sdca",
+ card->components, component->name_prefix);
if (!card->components)
return -ENOMEM;
- ret = snd_soc_add_card_controls(card, rt711_sdca_controls,
- ARRAY_SIZE(rt711_sdca_controls));
+ ret = snd_soc_add_card_controls(card, rt_sdca_jack_controls,
+ ARRAY_SIZE(rt_sdca_jack_controls));
if (ret) {
- dev_err(card->dev, "rt711-sdca controls addition failed: %d\n", ret);
+ dev_err(card->dev, "rt sdca jack controls addition failed: %d\n", ret);
return ret;
}
- ret = snd_soc_dapm_new_controls(&card->dapm, rt711_sdca_widgets,
- ARRAY_SIZE(rt711_sdca_widgets));
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt_sdca_jack_widgets,
+ ARRAY_SIZE(rt_sdca_jack_widgets));
if (ret) {
- dev_err(card->dev, "rt711-sdca widgets addition failed: %d\n", ret);
+ dev_err(card->dev, "rt sdca jack widgets addition failed: %d\n", ret);
return ret;
}
- ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map,
- ARRAY_SIZE(rt711_sdca_map));
+ if (strstr(component->name_prefix, "rt711")) {
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map,
+ ARRAY_SIZE(rt711_sdca_map));
+ } else if (strstr(component->name_prefix, "rt712")) {
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt712_sdca_map,
+ ARRAY_SIZE(rt712_sdca_map));
+ } else {
+ dev_err(card->dev, "%s is not supported\n", component->name_prefix);
+ return -EINVAL;
+ }
if (ret) {
- dev_err(card->dev, "rt711-sdca map addition failed: %d\n", ret);
+ dev_err(card->dev, "rt sdca jack map addition failed: %d\n", ret);
return ret;
}
@@ -112,8 +124,8 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
&ctx->sdw_headset,
- rt711_sdca_jack_pins,
- ARRAY_SIZE(rt711_sdca_jack_pins));
+ rt_sdca_jack_pins,
+ ARRAY_SIZE(rt_sdca_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
@@ -136,24 +148,27 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
-int sof_sdw_rt711_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
if (!ctx->headset_codec_dev)
return 0;
+ if (!SOF_JACK_JDSRC(sof_sdw_quirk))
+ return 0;
+
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
return 0;
}
-int sof_sdw_rt711_sdca_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback)
+int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
{
struct mc_private *ctx = snd_soc_card_get_drvdata(card);
struct device *sdw_dev;
@@ -170,14 +185,14 @@ int sof_sdw_rt711_sdca_init(struct snd_soc_card *card,
if (!sdw_dev)
return -EPROBE_DEFER;
- ret = rt711_sdca_add_codec_device_props(sdw_dev);
+ ret = rt_sdca_jack_add_codec_device_props(sdw_dev);
if (ret < 0) {
put_device(sdw_dev);
return ret;
}
ctx->headset_codec_dev = sdw_dev;
- dai_links->init = rt711_sdca_rtd_init;
+ dai_links->init = rt_sdca_jack_rtd_init;
return 0;
}
diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c
index ffd9c583dab1..0aef718e82b2 100644
--- a/sound/soc/intel/boards/sof_ssp_amp.c
+++ b/sound/soc/intel/boards/sof_ssp_amp.c
@@ -167,13 +167,6 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
-static struct snd_soc_dai_link_component dummy_component[] = {
- {
- .name = "snd-soc-dummy",
- .dai_name = "snd-soc-dummy-dai",
- }
-};
-
static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
{
struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
@@ -233,8 +226,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (!links[id].name)
return NULL;
links[id].id = id;
- links[id].codecs = dummy_component;
- links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[id].codecs = &asoc_dummy_dlc;
+ links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_capture = 1;
@@ -331,8 +324,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (!idisp_components[i - 1].dai_name)
goto devm_err;
} else {
- idisp_components[i - 1].name = "snd-soc-dummy";
- idisp_components[i - 1].dai_name = "snd-soc-dummy-dai";
+ idisp_components[i - 1] = asoc_dummy_dlc;
}
links[id].codecs = &idisp_components[i - 1];
@@ -360,8 +352,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
if (!links[id].name)
goto devm_err;
- links[id].codecs = dummy_component;
- links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[id].codecs = &asoc_dummy_dlc;
+ links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
@@ -471,6 +463,15 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_BT_OFFLOAD_PRESENT |
SOF_CS35L41_SPEAKER_AMP_PRESENT),
},
+ {
+ .name = "adl_lt6911_hdmi_ssp",
+ .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_NO_OF_HDMI_PLAYBACK(3) |
+ SOF_HDMI_PLAYBACK_PRESENT),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
@@ -486,7 +487,7 @@ static struct platform_driver sof_ssp_amp_driver = {
module_platform_driver(sof_ssp_amp_driver);
MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
-MODULE_AUTHOR("balamurugan.c <balamurugan.c@intel.com>");
+MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>");
MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index 41054cf09ec9..07aa37dd90e9 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -10,6 +10,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m
soc-acpi-intel-tgl-match.o soc-acpi-intel-ehl-match.o \
soc-acpi-intel-jsl-match.o soc-acpi-intel-adl-match.o \
soc-acpi-intel-rpl-match.o soc-acpi-intel-mtl-match.o \
+ soc-acpi-intel-lnl-match.o \
soc-acpi-intel-hda-match.o \
soc-acpi-intel-sdw-mockup-match.o
diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
index d8c80041388a..bcd66e0094b4 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -133,6 +133,15 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = {
+ {
+ .adr = 0x000130025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt1316_2_single_adr[] = {
{
.adr = 0x000230025D131601ull,
@@ -312,6 +321,20 @@ static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link12_rt714_link0[] =
{}
};
+static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link1_rt714_link0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_single_adr),
+ .adr_d = rt1316_1_single_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link3[] = {
{
.mask = BIT(2),
@@ -557,12 +580,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
.quirk_data = &adl_max98360a_amp,
.sof_tplg_filename = "sof-adl-max98360a-cs42l42.tplg",
},
- /* place amp-only boards in the end of table */
- {
- .id = "CSC3541",
- .drv_name = "adl_cs35l41",
- .sof_tplg_filename = "sof-adl-cs35l41.tplg",
- },
{
.comp_ids = &essx_83x6,
.drv_name = "adl_es83x6_c1_h02",
@@ -578,6 +595,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
},
+ /* place amp-only boards in the end of table */
+ {
+ .id = "CSC3541",
+ .drv_name = "adl_cs35l41",
+ .sof_tplg_filename = "sof-adl-cs35l41.tplg",
+ },
+ {
+ .id = "INTC10B0",
+ .drv_name = "adl_lt6911_hdmi_ssp",
+ .sof_tplg_filename = "sof-adl-nocodec-hdmi-ssp02.tplg"
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines);
@@ -621,6 +649,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = {
.sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg",
},
{
+ .link_mask = 0x3, /* rt1316 on link1 & rt714 on link0 */
+ .links = adl_sdw_rt1316_link1_rt714_link0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-adl-rt1316-l1-mono-rt714-l0.tplg",
+ },
+ {
.link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */
.links = adl_sdw_rt1316_link12_rt714_link0,
.drv_name = "sof_sdw",
diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
new file mode 100644
index 000000000000..9f35b77deb11
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-intel-lnl-match.c - tables and support for LNL ACPI enumeration.
+ *
+ * Copyright (c) 2023, Intel Corporation. All rights reserved.
+ *
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "soc-acpi-intel-sdw-mockup-match.h"
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_machines);
+
+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_adr_device rt711_sdca_0_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr lnl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {}
+};
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
+ /* mockup tests need to be first */
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = sdw_mockup_headset_2amps_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711-rt1308-rt715.tplg",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1) | BIT(3),
+ .links = sdw_mockup_headset_1amp_mic,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711-rt1308-mono-rt715.tplg",
+ },
+ {
+ .link_mask = GENMASK(2, 0),
+ .links = sdw_mockup_mic_headset_1amp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt715-rt711-rt1308-mono.tplg",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = lnl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
index 7911c3af8071..ed9821adc1d9 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -20,6 +20,11 @@ static const struct snd_soc_acpi_codecs mtl_max98360a_amp = {
.codecs = {"MX98360A"}
};
+static const struct snd_soc_acpi_codecs mtl_rt1019p_amp = {
+ .num_codecs = 1,
+ .codecs = {"RTL1019"}
+};
+
static const struct snd_soc_acpi_codecs mtl_rt5682_rt5682s_hp = {
.num_codecs = 2,
.codecs = {"10EC5682", "RTL5682"},
@@ -40,6 +45,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = {
.quirk_data = &mtl_max98360a_amp,
.sof_tplg_filename = "sof-mtl-max98360a-rt5682.tplg",
},
+ {
+ .comp_ids = &mtl_rt5682_rt5682s_hp,
+ .drv_name = "mtl_rt1019_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mtl_rt1019p_amp,
+ .sof_tplg_filename = "sof-mtl-rt1019-rt5682.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_machines);
@@ -65,6 +77,21 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
.group_id = 1,
};
+static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
{
.adr = 0x000030025D071101ull,
@@ -74,6 +101,24 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = {
+ {
+ .adr = 0x000030025D071201ull,
+ .num_endpoints = ARRAY_SIZE(rt712_endpoints),
+ .endpoints = rt712_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = {
+ {
+ .adr = 0x000330025D171201ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt712-dmic"
+ }
+};
+
static const struct snd_soc_acpi_adr_device mx8373_0_adr[] = {
{
.adr = 0x000023019F837300ull,
@@ -98,6 +143,47 @@ static const struct snd_soc_acpi_adr_device rt5682_2_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
+ {
+ .adr = 0x000331025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_1_adr[] = {
+ {
+ .adr = 0x000130025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr mtl_712_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt712_0_single_adr),
+ .adr_d = rt712_0_single_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1712_3_single_adr),
+ .adr_d = rt1712_3_single_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = {
/* Expected order: jack -> amp */
{
@@ -122,6 +208,69 @@ static const struct snd_soc_acpi_link_adr mtl_rvp[] = {
{}
};
+static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_group1_adr),
+ .adr_d = rt1316_3_group1_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_1_adr),
+ .adr_d = rt714_1_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = {
+ {
+ .adr = 0x000230019F836300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "Left"
+ },
+ {
+ .adr = 0x000231019F836300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "Right"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = {
+ {
+ .adr = 0x00001001FA424200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "cs42l42"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr cs42l42_link0_max98363_link2[] = {
+ /* Expected order: jack -> amp */
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l42_0_adr),
+ .adr_d = cs42l42_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(mx8363_2_adr),
+ .adr_d = mx8363_2_adr,
+ },
+ {}
+};
+
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
/* mockup tests need to be first */
@@ -144,6 +293,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.sof_tplg_filename = "sof-mtl-rt715-rt711-rt1308-mono.tplg",
},
{
+ .link_mask = BIT(3) | BIT(0),
+ .links = mtl_712_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg",
+ },
+ {
+ .link_mask = GENMASK(3, 0),
+ .links = mtl_3_in_1_sdca,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg",
+ },
+ {
.link_mask = BIT(0),
.links = mtl_rvp,
.drv_name = "sof_sdw",
@@ -155,6 +316,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg",
},
+ {
+ .link_mask = BIT(0) | BIT(2),
+ .links = cs42l42_link0_max98363_link2,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
index 749d371a86ae..302a08018572 100644
--- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
@@ -179,6 +179,30 @@ static const struct snd_soc_acpi_link_adr rpl_sdca_3_in_1[] = {
{}
};
+static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link0_rt1316_link12_rt714_link3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group1_adr),
+ .adr_d = rt1316_1_group1_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt714_3_adr),
+ .adr_d = rt714_3_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr rpl_sdw_rt711_link2_rt1316_link01_rt714_link3[] = {
{
.mask = BIT(2),
@@ -303,6 +327,16 @@ static const struct snd_soc_acpi_codecs rpl_max98360a_amp = {
.codecs = {"MX98360A"},
};
+static const struct snd_soc_acpi_codecs rpl_max98373_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98373"}
+};
+
+static const struct snd_soc_acpi_codecs rpl_rt1019p_amp = {
+ .num_codecs = 1,
+ .codecs = {"RTL1019"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
{
.comp_ids = &rpl_rt5682_hp,
@@ -311,6 +345,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
.quirk_data = &rpl_max98360a_amp,
.sof_tplg_filename = "sof-rpl-max98360a-rt5682.tplg",
},
+ {
+ .id = "10508825",
+ .drv_name = "rpl_max98373_8825",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rpl_max98373_amp,
+ .sof_tplg_filename = "sof-rpl-max98373-nau8825.tplg",
+ },
+ {
+ .comp_ids = &rpl_rt5682_hp,
+ .drv_name = "rpl_rt1019_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rpl_rt1019p_amp,
+ .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines);
@@ -331,6 +379,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = {
},
{
.link_mask = 0xF, /* 4 active links required */
+ .links = rpl_sdw_rt711_link0_rt1316_link12_rt714_link3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711-l0-rt1316-l12-rt714-l3.tplg",
+ },
+ {
+ .link_mask = 0xF, /* 4 active links required */
.links = rpl_sdw_rt711_link0_rt1318_link12_rt714_link3,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-rpl-rt711-l0-rt1318-l12-rt714-l3.tplg",
diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
index ef19150e7b2e..5804926c8b56 100644
--- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
@@ -41,6 +41,21 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
.group_id = 1,
};
+static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
{
.adr = 0x000020025D071100ull,
@@ -170,6 +185,24 @@ static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt712_0_single_adr[] = {
+ {
+ .adr = 0x000030025D071201ull,
+ .num_endpoints = ARRAY_SIZE(rt712_endpoints),
+ .endpoints = rt712_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1712_1_single_adr[] = {
+ {
+ .adr = 0x000130025D171201ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt712-dmic"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = {
{
.adr = 0x000131025D131601ull, /* unique ID is set for some reason */
@@ -353,6 +386,20 @@ static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca_mono[] = {
{}
};
+static const struct snd_soc_acpi_link_adr tgl_712_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt712_0_single_adr),
+ .adr_d = rt712_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1712_1_single_adr),
+ .adr_d = rt1712_1_single_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_codecs tgl_max98373_amp = {
.num_codecs = 1,
.codecs = {"MX98373"}
@@ -436,6 +483,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = {
.sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg",
},
{
+ .link_mask = 0xF, /* 4 active links required */
+ .links = tgl_712_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-rt712.tplg",
+ },
+ {
.link_mask = 0x7,
.links = tgl_sdw_rt711_link1_rt1308_link2_rt715_link0,
.drv_name = "sof_sdw",
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index d0f6c945d9ae..578af21769c9 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -375,6 +375,14 @@ static const struct i2s_soc_info jz4760_i2s_soc_info = {
.field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
};
+static const struct i2s_soc_info x1000_i2s_soc_info = {
+ .dai = &jz4740_i2s_dai,
+ .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
+ .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
+ .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8),
+ .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8),
+};
+
static struct snd_soc_dai_driver jz4770_i2s_dai = {
.probe = jz4740_i2s_dai_probe,
.playback = {
@@ -486,6 +494,7 @@ static const struct of_device_id jz4740_of_matches[] = {
{ .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info },
{ .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info },
{ .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info },
+ { .compatible = "ingenic,x1000-i2s", .data = &x1000_i2s_soc_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, jz4740_of_matches);
diff --git a/sound/soc/loongson/Kconfig b/sound/soc/loongson/Kconfig
new file mode 100644
index 000000000000..b8d7e2bade24
--- /dev/null
+++ b/sound/soc/loongson/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+menu "SoC Audio for Loongson CPUs"
+ depends on LOONGARCH || COMPILE_TEST
+
+config SND_SOC_LOONGSON_I2S_PCI
+ tristate "Loongson I2S-PCI Device Driver"
+ select REGMAP_MMIO
+ depends on PCI
+ help
+ Say Y or M if you want to add support for I2S driver for
+ Loongson I2S controller.
+
+ The controller is found in loongson bridge chips or SoCs,
+ and work as a PCI device.
+
+config SND_SOC_LOONGSON_CARD
+ tristate "Loongson Sound Card Driver"
+ select SND_SOC_LOONGSON_I2S_PCI
+ depends on PCI
+ help
+ Say Y or M if you want to add support for SoC audio using
+ loongson I2S controller.
+
+ The driver add support for ALSA SoC Audio support using
+ loongson I2S controller.
+
+endmenu
diff --git a/sound/soc/loongson/Makefile b/sound/soc/loongson/Makefile
new file mode 100644
index 000000000000..601a905a4860
--- /dev/null
+++ b/sound/soc/loongson/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#Platform Support
+snd-soc-loongson-i2s-pci-objs := loongson_i2s_pci.o loongson_i2s.o loongson_dma.o
+obj-$(CONFIG_SND_SOC_LOONGSON_I2S_PCI) += snd-soc-loongson-i2s-pci.o
+
+#Machine Support
+snd-soc-loongson-card-objs := loongson_card.o
+obj-$(CONFIG_SND_SOC_LOONGSON_CARD) += snd-soc-loongson-card.o
diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c
new file mode 100644
index 000000000000..9ded16329747
--- /dev/null
+++ b/sound/soc/loongson/loongson_card.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Loongson ASoC Audio Machine driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <sound/pcm_params.h>
+
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+struct loongson_card_data {
+ struct snd_soc_card snd_card;
+ unsigned int mclk_fs;
+};
+
+static int loongson_card_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct loongson_card_data *ls_card = snd_soc_card_get_drvdata(rtd->card);
+ int ret, mclk;
+
+ if (ls_card->mclk_fs) {
+ mclk = ls_card->mclk_fs * params_rate(params);
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "cpu_dai clock not set\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops loongson_ops = {
+ .hw_params = loongson_card_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(analog,
+ DAILINK_COMP_ARRAY(COMP_CPU("loongson-i2s")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link loongson_dai_links[] = {
+ {
+ .name = "Loongson Audio Port",
+ .stream_name = "Loongson Audio",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(analog),
+ .ops = &loongson_ops,
+ },
+};
+
+static int loongson_card_parse_acpi(struct loongson_card_data *data)
+{
+ struct snd_soc_card *card = &data->snd_card;
+ struct fwnode_handle *fwnode = card->dev->fwnode;
+ struct fwnode_reference_args args;
+ const char *codec_dai_name;
+ struct acpi_device *adev;
+ struct device *phy_dev;
+ int ret, i;
+
+ /* fixup platform name based on reference node */
+ memset(&args, 0, sizeof(args));
+ ret = acpi_node_get_property_reference(fwnode, "cpu", 0, &args);
+ if (ret || !is_acpi_device_node(args.fwnode)) {
+ dev_err(card->dev, "No matching phy in ACPI table\n");
+ return ret ?: -ENOENT;
+ }
+ adev = to_acpi_device_node(args.fwnode);
+ phy_dev = acpi_get_first_physical_node(adev);
+ if (!phy_dev)
+ return -EPROBE_DEFER;
+ for (i = 0; i < card->num_links; i++)
+ loongson_dai_links[i].platforms->name = dev_name(phy_dev);
+
+ /* fixup codec name based on reference node */
+ memset(&args, 0, sizeof(args));
+ ret = acpi_node_get_property_reference(fwnode, "codec", 0, &args);
+ if (ret || !is_acpi_device_node(args.fwnode)) {
+ dev_err(card->dev, "No matching phy in ACPI table\n");
+ return ret ?: -ENOENT;
+ }
+ adev = to_acpi_device_node(args.fwnode);
+ snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev));
+ for (i = 0; i < card->num_links; i++)
+ loongson_dai_links[i].codecs->name = codec_name;
+
+ device_property_read_string(card->dev, "codec-dai-name",
+ &codec_dai_name);
+ for (i = 0; i < card->num_links; i++)
+ loongson_dai_links[i].codecs->dai_name = codec_dai_name;
+
+ return 0;
+}
+
+static int loongson_card_parse_of(struct loongson_card_data *data)
+{
+ struct device_node *cpu, *codec;
+ struct snd_soc_card *card = &data->snd_card;
+ struct device *dev = card->dev;
+ int ret, i;
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ if (!cpu) {
+ dev_err(dev, "platform property missing or invalid\n");
+ return -EINVAL;
+ }
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ if (!codec) {
+ dev_err(dev, "audio-codec property missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ ret = snd_soc_of_get_dlc(cpu, NULL, loongson_dai_links[i].cpus, 0);
+ if (ret < 0) {
+ dev_err(dev, "getting cpu dlc error (%d)\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_of_get_dlc(codec, NULL, loongson_dai_links[i].codecs, 0);
+ if (ret < 0) {
+ dev_err(dev, "getting codec dlc error (%d)\n", ret);
+ goto err;
+ }
+ }
+
+ of_node_put(cpu);
+ of_node_put(codec);
+
+ return 0;
+
+err:
+ of_node_put(cpu);
+ of_node_put(codec);
+ return ret;
+}
+
+static int loongson_asoc_card_probe(struct platform_device *pdev)
+{
+ struct loongson_card_data *ls_priv;
+ struct snd_soc_card *card;
+ int ret;
+
+ ls_priv = devm_kzalloc(&pdev->dev, sizeof(*ls_priv), GFP_KERNEL);
+ if (!ls_priv)
+ return -ENOMEM;
+
+ card = &ls_priv->snd_card;
+
+ card->dev = &pdev->dev;
+ card->owner = THIS_MODULE;
+ card->dai_link = loongson_dai_links;
+ card->num_links = ARRAY_SIZE(loongson_dai_links);
+ snd_soc_card_set_drvdata(card, ls_priv);
+
+ ret = device_property_read_string(&pdev->dev, "model", &card->name);
+ if (ret) {
+ dev_err(&pdev->dev, "Error parsing card name: %d\n", ret);
+ return ret;
+ }
+ ret = device_property_read_u32(&pdev->dev, "mclk-fs", &ls_priv->mclk_fs);
+ if (ret) {
+ dev_err(&pdev->dev, "Error parsing mclk-fs: %d\n", ret);
+ return ret;
+ }
+
+ if (has_acpi_companion(&pdev->dev))
+ ret = loongson_card_parse_acpi(ls_priv);
+ else
+ ret = loongson_card_parse_of(ls_priv);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+ return ret;
+}
+
+static const struct of_device_id loongson_asoc_dt_ids[] = {
+ { .compatible = "loongson,ls-audio-card" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids);
+
+static struct platform_driver loongson_audio_driver = {
+ .probe = loongson_asoc_card_probe,
+ .driver = {
+ .name = "loongson-asoc-card",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = of_match_ptr(loongson_asoc_dt_ids),
+ },
+};
+module_platform_driver(loongson_audio_driver);
+
+MODULE_DESCRIPTION("Loongson ASoc Sound Card driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/loongson/loongson_dma.c b/sound/soc/loongson/loongson_dma.c
new file mode 100644
index 000000000000..65b6719e61c5
--- /dev/null
+++ b/sound/soc/loongson/loongson_dma.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Loongson ALSA SoC Platform (DMA) driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "loongson_i2s.h"
+
+/* DMA dma_order Register */
+#define DMA_ORDER_STOP (1 << 4) /* DMA stop */
+#define DMA_ORDER_START (1 << 3) /* DMA start */
+#define DMA_ORDER_ASK_VALID (1 << 2) /* DMA ask valid flag */
+#define DMA_ORDER_AXI_UNCO (1 << 1) /* Uncache access */
+#define DMA_ORDER_ADDR_64 (1 << 0) /* 64bits address support */
+
+#define DMA_ORDER_ASK_MASK (~0x1fUL) /* Ask addr mask */
+#define DMA_ORDER_CTRL_MASK (0x0fUL) /* Control mask */
+
+/*
+ * DMA registers descriptor.
+ */
+struct loongson_dma_desc {
+ u32 order; /* Next descriptor address register */
+ u32 saddr; /* Source address register */
+ u32 daddr; /* Device address register */
+ u32 length; /* Total length register */
+ u32 step_length; /* Memory stride register */
+ u32 step_times; /* Repeat time register */
+ u32 cmd; /* Command register */
+ u32 stats; /* Status register */
+ u32 order_hi; /* Next descriptor high address register */
+ u32 saddr_hi; /* High source address register */
+ u32 res[6]; /* Reserved */
+} __packed;
+
+struct loongson_runtime_data {
+ struct loongson_dma_data *dma_data;
+
+ struct loongson_dma_desc *dma_desc_arr;
+ dma_addr_t dma_desc_arr_phy;
+ int dma_desc_arr_size;
+
+ struct loongson_dma_desc *dma_pos_desc;
+ dma_addr_t dma_pos_desc_phy;
+};
+
+static const struct snd_pcm_hardware ls_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .period_bytes_min = 128,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE / sizeof(struct loongson_dma_desc),
+ .buffer_bytes_max = 1024 * 1024,
+};
+
+static struct
+loongson_dma_desc *dma_desc_save(struct loongson_runtime_data *prtd)
+{
+ void __iomem *order_reg = prtd->dma_data->order_addr;
+ u64 val;
+
+ val = (u64)prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK;
+ val |= (readq(order_reg) & DMA_ORDER_CTRL_MASK);
+ val |= DMA_ORDER_ASK_VALID;
+ writeq(val, order_reg);
+
+ while (readl(order_reg) & DMA_ORDER_ASK_VALID)
+ udelay(2);
+
+ return prtd->dma_pos_desc;
+}
+
+static int loongson_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct loongson_runtime_data *prtd = substream->runtime->private_data;
+ struct device *dev = substream->pcm->card->dev;
+ void __iomem *order_reg = prtd->dma_data->order_addr;
+ u64 val;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = prtd->dma_pos_desc_phy & DMA_ORDER_ASK_MASK;
+ if (dev->coherent_dma_mask == DMA_BIT_MASK(64))
+ val |= DMA_ORDER_ADDR_64;
+ else
+ val &= ~DMA_ORDER_ADDR_64;
+ val |= (readq(order_reg) & DMA_ORDER_CTRL_MASK);
+ val |= DMA_ORDER_START;
+ writeq(val, order_reg);
+
+ while ((readl(order_reg) & DMA_ORDER_START))
+ udelay(2);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dma_desc_save(prtd);
+
+ /* dma stop */
+ val = readq(order_reg) | DMA_ORDER_STOP;
+ writeq(val, order_reg);
+ udelay(1000);
+
+ break;
+ default:
+ dev_err(dev, "Invalid pcm trigger operation\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int loongson_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 device *dev = substream->pcm->card->dev;
+ struct loongson_runtime_data *prtd = runtime->private_data;
+ size_t buf_len = params_buffer_bytes(params);
+ size_t period_len = params_period_bytes(params);
+ dma_addr_t order_addr, mem_addr;
+ struct loongson_dma_desc *desc;
+ u32 num_periods;
+ int i;
+
+ if (buf_len % period_len) {
+ dev_err(dev, "buf len not multiply of period len\n");
+ return -EINVAL;
+ }
+
+ num_periods = buf_len / period_len;
+ if (!num_periods || num_periods > prtd->dma_desc_arr_size) {
+ dev_err(dev, "dma data too small or too big\n");
+ return -EINVAL;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = buf_len;
+
+ /* initialize dma descriptor array */
+ mem_addr = runtime->dma_addr;
+ order_addr = prtd->dma_desc_arr_phy;
+ for (i = 0; i < num_periods; i++) {
+ desc = &prtd->dma_desc_arr[i];
+
+ /* next descriptor physical address */
+ order_addr += sizeof(*desc);
+ desc->order = lower_32_bits(order_addr | BIT(0));
+ desc->order_hi = upper_32_bits(order_addr);
+
+ desc->saddr = lower_32_bits(mem_addr);
+ desc->saddr_hi = upper_32_bits(mem_addr);
+ desc->daddr = prtd->dma_data->dev_addr;
+
+ desc->cmd = BIT(0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ desc->cmd |= BIT(12);
+
+ desc->length = period_len >> 2;
+ desc->step_length = 0;
+ desc->step_times = 1;
+
+ mem_addr += period_len;
+ }
+ desc = &prtd->dma_desc_arr[num_periods - 1];
+ desc->order = lower_32_bits(prtd->dma_desc_arr_phy | BIT(0));
+ desc->order_hi = upper_32_bits(prtd->dma_desc_arr_phy);
+
+ /* init position descriptor */
+ *prtd->dma_pos_desc = *prtd->dma_desc_arr;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+loongson_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loongson_runtime_data *prtd = runtime->private_data;
+ struct loongson_dma_desc *desc;
+ snd_pcm_uframes_t x;
+ u64 addr;
+
+ desc = dma_desc_save(prtd);
+ addr = ((u64)desc->saddr_hi << 32) | desc->saddr;
+
+ x = bytes_to_frames(runtime, addr - runtime->dma_addr);
+ if (x == runtime->buffer_size)
+ x = 0;
+ return x;
+}
+
+static irqreturn_t loongson_pcm_dma_irq(int irq, void *devid)
+{
+ struct snd_pcm_substream *substream = devid;
+
+ snd_pcm_period_elapsed(substream);
+ return IRQ_HANDLED;
+}
+
+static int loongson_pcm_open(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 snd_card *card = substream->pcm->card;
+ struct loongson_runtime_data *prtd;
+ struct loongson_dma_data *dma_data;
+ int ret;
+
+ /*
+ * For mysterious reasons (and despite what the manual says)
+ * playback samples are lost if the DMA count is not a multiple
+ * of the DMA burst size. Let's add a rule to enforce that.
+ */
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
+ snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ snd_soc_set_runtime_hwparams(substream, &ls_pcm_hardware);
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ prtd->dma_desc_arr = dma_alloc_coherent(card->dev, PAGE_SIZE,
+ &prtd->dma_desc_arr_phy,
+ GFP_KERNEL);
+ if (!prtd->dma_desc_arr) {
+ ret = -ENOMEM;
+ goto desc_err;
+ }
+ prtd->dma_desc_arr_size = PAGE_SIZE / sizeof(*prtd->dma_desc_arr);
+
+ prtd->dma_pos_desc = dma_alloc_coherent(card->dev,
+ sizeof(*prtd->dma_pos_desc),
+ &prtd->dma_pos_desc_phy,
+ GFP_KERNEL);
+ if (!prtd->dma_pos_desc) {
+ ret = -ENOMEM;
+ goto pos_err;
+ }
+
+ dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ prtd->dma_data = dma_data;
+
+ substream->runtime->private_data = prtd;
+
+ return 0;
+pos_err:
+ dma_free_coherent(card->dev, PAGE_SIZE, prtd->dma_desc_arr,
+ prtd->dma_desc_arr_phy);
+desc_err:
+ kfree(prtd);
+
+ return ret;
+}
+
+static int loongson_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_card *card = substream->pcm->card;
+ struct loongson_runtime_data *prtd = substream->runtime->private_data;
+
+ dma_free_coherent(card->dev, PAGE_SIZE, prtd->dma_desc_arr,
+ prtd->dma_desc_arr_phy);
+
+ dma_free_coherent(card->dev, sizeof(*prtd->dma_pos_desc),
+ prtd->dma_pos_desc, prtd->dma_pos_desc_phy);
+
+ kfree(prtd);
+ return 0;
+}
+
+static int loongson_pcm_mmap(struct snd_soc_component *component,
+ 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 loongson_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm_substream *substream;
+ struct loongson_dma_data *dma_data;
+ unsigned int i;
+ int ret;
+
+ for_each_pcm_streams(i) {
+ substream = rtd->pcm->streams[i].substream;
+ if (!substream)
+ continue;
+
+ dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0),
+ substream);
+ ret = devm_request_irq(card->dev, dma_data->irq,
+ loongson_pcm_dma_irq,
+ IRQF_TRIGGER_HIGH, LS_I2S_DRVNAME,
+ substream);
+ if (ret < 0) {
+ dev_err(card->dev, "request irq for DMA failed\n");
+ return ret;
+ }
+ }
+
+ return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev,
+ ls_pcm_hardware.buffer_bytes_max);
+}
+
+const struct snd_soc_component_driver loongson_i2s_component = {
+ .name = LS_I2S_DRVNAME,
+ .open = loongson_pcm_open,
+ .close = loongson_pcm_close,
+ .hw_params = loongson_pcm_hw_params,
+ .trigger = loongson_pcm_trigger,
+ .pointer = loongson_pcm_pointer,
+ .mmap = loongson_pcm_mmap,
+ .pcm_construct = loongson_pcm_new,
+};
diff --git a/sound/soc/loongson/loongson_dma.h b/sound/soc/loongson/loongson_dma.h
new file mode 100644
index 000000000000..073ee8c0c046
--- /dev/null
+++ b/sound/soc/loongson/loongson_dma.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA ASoC interface for the Loongson platform
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ * Author: Yingkun Meng <mengyingkun@loongson.cn>
+ */
+
+#ifndef _LOONGSON_DMA_H
+#define _LOONGSON_DMA_H
+
+#include <sound/soc.h>
+
+extern const struct snd_soc_component_driver loongson_i2s_component;
+
+#endif
diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c
new file mode 100644
index 000000000000..b919f0fe8361
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Common functions for loongson I2S controller driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited.
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "loongson_i2s.h"
+
+#define LOONGSON_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static int loongson_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct loongson_i2s *i2s = 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:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN,
+ I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN);
+ else
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN,
+ I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN, 0);
+ else
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int loongson_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 clk_rate = i2s->clk_rate;
+ u32 sysclk = i2s->sysclk;
+ u32 bits = params_width(params);
+ u32 chans = params_channels(params);
+ u32 fs = params_rate(params);
+ u32 bclk_ratio, mclk_ratio;
+ u32 mclk_ratio_frac;
+ u32 val = 0;
+
+ switch (i2s->rev_id) {
+ case 0:
+ bclk_ratio = DIV_ROUND_CLOSEST(clk_rate,
+ (bits * chans * fs * 2)) - 1;
+ mclk_ratio = DIV_ROUND_CLOSEST(clk_rate, (sysclk * 2)) - 1;
+
+ /* According to 2k1000LA user manual, set bits == depth */
+ val |= (bits << 24);
+ val |= (bits << 16);
+ val |= (bclk_ratio << 8);
+ val |= mclk_ratio;
+ regmap_write(i2s->regmap, LS_I2S_CFG, val);
+
+ break;
+ case 1:
+ bclk_ratio = DIV_ROUND_CLOSEST(sysclk,
+ (bits * chans * fs * 2)) - 1;
+ mclk_ratio = clk_rate / sysclk;
+ mclk_ratio_frac = DIV_ROUND_CLOSEST_ULL(((u64)clk_rate << 16),
+ sysclk) - (mclk_ratio << 16);
+
+ regmap_read(i2s->regmap, LS_I2S_CFG, &val);
+ val |= (bits << 24);
+ val |= (bclk_ratio << 8);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ val |= (bits << 16);
+ else
+ val |= bits;
+ regmap_write(i2s->regmap, LS_I2S_CFG, val);
+
+ val = (mclk_ratio_frac << 16) | mclk_ratio;
+ regmap_write(i2s->regmap, LS_I2S_CFG1, val);
+
+ break;
+ default:
+ dev_err(i2s->dev, "I2S revision invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int loongson_i2s_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ i2s->sysclk = freq;
+
+ return 0;
+}
+
+static int loongson_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MSB,
+ I2S_CTRL_MSB);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ /* Enable master mode */
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER,
+ I2S_CTRL_MASTER);
+ if (i2s->rev_id == 1) {
+ ret = regmap_read_poll_timeout_atomic(i2s->regmap,
+ LS_I2S_CTRL, val,
+ val & I2S_CTRL_CLK_READY,
+ 10, 500000);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait BCLK ready timeout\n");
+ }
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ /* Enable MCLK */
+ if (i2s->rev_id == 1) {
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_MCLK_EN,
+ I2S_CTRL_MCLK_EN);
+ ret = regmap_read_poll_timeout_atomic(i2s->regmap,
+ LS_I2S_CTRL, val,
+ val & I2S_CTRL_MCLK_READY,
+ 10, 500000);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait MCLK ready timeout\n");
+ }
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ /* Enable MCLK */
+ if (i2s->rev_id == 1) {
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL,
+ I2S_CTRL_MCLK_EN,
+ I2S_CTRL_MCLK_EN);
+ ret = regmap_read_poll_timeout_atomic(i2s->regmap,
+ LS_I2S_CTRL, val,
+ val & I2S_CTRL_MCLK_READY,
+ 10, 500000);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait MCLK ready timeout\n");
+ }
+
+ /* Enable master mode */
+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_MASTER,
+ I2S_CTRL_MASTER);
+ if (i2s->rev_id == 1) {
+ ret = regmap_read_poll_timeout_atomic(i2s->regmap,
+ LS_I2S_CTRL, val,
+ val & I2S_CTRL_CLK_READY,
+ 10, 500000);
+ if (ret < 0)
+ dev_warn(dai->dev, "wait BCLK ready timeout\n");
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops loongson_i2s_dai_ops = {
+ .trigger = loongson_i2s_trigger,
+ .hw_params = loongson_i2s_hw_params,
+ .set_sysclk = loongson_i2s_set_dai_sysclk,
+ .set_fmt = loongson_i2s_set_fmt,
+};
+
+static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct loongson_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+
+ snd_soc_dai_init_dma_data(cpu_dai, &i2s->playback_dma_data,
+ &i2s->capture_dma_data);
+ snd_soc_dai_set_drvdata(cpu_dai, i2s);
+
+ return 0;
+}
+
+struct snd_soc_dai_driver loongson_i2s_dai = {
+ .name = "loongson-i2s",
+ .probe = loongson_i2s_dai_probe,
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = LOONGSON_I2S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = LOONGSON_I2S_FORMATS,
+ },
+ .ops = &loongson_i2s_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int i2s_suspend(struct device *dev)
+{
+ struct loongson_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, true);
+
+ return 0;
+}
+
+static int i2s_resume(struct device *dev)
+{
+ struct loongson_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(i2s->regmap, false);
+ regcache_mark_dirty(i2s->regmap);
+ ret = regcache_sync(i2s->regmap);
+
+ return ret;
+}
+
+const struct dev_pm_ops loongson_i2s_pm = {
+ SYSTEM_SLEEP_PM_OPS(i2s_suspend, i2s_resume)
+};
diff --git a/sound/soc/loongson/loongson_i2s.h b/sound/soc/loongson/loongson_i2s.h
new file mode 100644
index 000000000000..89492eebf834
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA I2S interface for the Loongson platform
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ * Author: Yingkun Meng <mengyingkun@loongson.cn>
+ */
+
+#ifndef _LOONGSON_I2S_H
+#define _LOONGSON_I2S_H
+
+#include <linux/regmap.h>
+#include <sound/dmaengine_pcm.h>
+
+/* I2S Common Registers */
+#define LS_I2S_VER 0x00 /* I2S Version */
+#define LS_I2S_CFG 0x04 /* I2S Config */
+#define LS_I2S_CTRL 0x08 /* I2S Control */
+#define LS_I2S_RX_DATA 0x0C /* I2S DMA RX Address */
+#define LS_I2S_TX_DATA 0x10 /* I2S DMA TX Address */
+
+/* 2K2000 I2S Specify Registers */
+#define LS_I2S_CFG1 0x14 /* I2S Config1 */
+
+/* 7A2000 I2S Specify Registers */
+#define LS_I2S_TX_ORDER 0x100 /* TX DMA Order */
+#define LS_I2S_RX_ORDER 0x110 /* RX DMA Order */
+
+/* Loongson I2S Control Register */
+#define I2S_CTRL_MCLK_READY (1 << 16) /* MCLK ready */
+#define I2S_CTRL_MASTER (1 << 15) /* Master mode */
+#define I2S_CTRL_MSB (1 << 14) /* MSB bit order */
+#define I2S_CTRL_RX_EN (1 << 13) /* RX enable */
+#define I2S_CTRL_TX_EN (1 << 12) /* TX enable */
+#define I2S_CTRL_RX_DMA_EN (1 << 11) /* DMA RX enable */
+#define I2S_CTRL_CLK_READY (1 << 8) /* BCLK ready */
+#define I2S_CTRL_TX_DMA_EN (1 << 7) /* DMA TX enable */
+#define I2S_CTRL_RESET (1 << 4) /* Controller soft reset */
+#define I2S_CTRL_MCLK_EN (1 << 3) /* Enable MCLK */
+#define I2S_CTRL_RX_INT_EN (1 << 1) /* RX interrupt enable */
+#define I2S_CTRL_TX_INT_EN (1 << 0) /* TX interrupt enable */
+
+#define LS_I2S_DRVNAME "loongson-i2s"
+
+struct loongson_dma_data {
+ dma_addr_t dev_addr; /* device physical address for DMA */
+ void __iomem *order_addr; /* DMA order register */
+ int irq; /* DMA irq */
+};
+
+struct loongson_i2s {
+ struct device *dev;
+ union {
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct loongson_dma_data tx_dma_data;
+ };
+ union {
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct loongson_dma_data rx_dma_data;
+ };
+ struct regmap *regmap;
+ void __iomem *reg_base;
+ u32 rev_id;
+ u32 clk_rate;
+ u32 sysclk;
+};
+
+extern const struct dev_pm_ops loongson_i2s_pm;
+extern struct snd_soc_dai_driver loongson_i2s_dai;
+
+#endif
diff --git a/sound/soc/loongson/loongson_i2s_pci.c b/sound/soc/loongson/loongson_i2s_pci.c
new file mode 100644
index 000000000000..fa90361865c6
--- /dev/null
+++ b/sound/soc/loongson/loongson_i2s_pci.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// loongson_i2s_pci.c -- Loongson I2S controller driver
+//
+// Copyright (C) 2023 Loongson Technology Corporation Limited
+// Author: Yingkun Meng <mengyingkun@loongson.cn>
+//
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <sound/soc.h>
+#include "loongson_i2s.h"
+#include "loongson_dma.h"
+
+static bool loongson_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LS_I2S_CFG:
+ case LS_I2S_CTRL:
+ case LS_I2S_RX_DATA:
+ case LS_I2S_TX_DATA:
+ case LS_I2S_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool loongson_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LS_I2S_VER:
+ case LS_I2S_CFG:
+ case LS_I2S_CTRL:
+ case LS_I2S_RX_DATA:
+ case LS_I2S_TX_DATA:
+ case LS_I2S_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool loongson_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LS_I2S_CFG:
+ case LS_I2S_CTRL:
+ case LS_I2S_RX_DATA:
+ case LS_I2S_TX_DATA:
+ case LS_I2S_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config loongson_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LS_I2S_CFG1,
+ .writeable_reg = loongson_i2s_wr_reg,
+ .readable_reg = loongson_i2s_rd_reg,
+ .volatile_reg = loongson_i2s_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int loongson_i2s_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pid)
+{
+ const struct fwnode_handle *fwnode = pdev->dev.fwnode;
+ struct loongson_dma_data *tx_data, *rx_data;
+ struct loongson_i2s *i2s;
+ int ret;
+
+ if (pcim_enable_device(pdev)) {
+ dev_err(&pdev->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->rev_id = pdev->revision;
+ i2s->dev = &pdev->dev;
+ pci_set_drvdata(pdev, i2s);
+
+ ret = pcim_iomap_regions(pdev, 1 << 0, dev_name(&pdev->dev));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "iomap_regions failed\n");
+ return ret;
+ }
+ i2s->reg_base = pcim_iomap_table(pdev)[0];
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->reg_base,
+ &loongson_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap)) {
+ dev_err(&pdev->dev, "regmap_init_mmio failed\n");
+ return PTR_ERR(i2s->regmap);
+ }
+
+ tx_data = &i2s->tx_dma_data;
+ rx_data = &i2s->rx_dma_data;
+
+ tx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_TX_DATA;
+ tx_data->order_addr = i2s->reg_base + LS_I2S_TX_ORDER;
+
+ rx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_RX_DATA;
+ rx_data->order_addr = i2s->reg_base + LS_I2S_RX_ORDER;
+
+ tx_data->irq = fwnode_irq_get_byname(fwnode, "tx");
+ if (tx_data->irq < 0) {
+ dev_err(&pdev->dev, "dma tx irq invalid\n");
+ return tx_data->irq;
+ }
+
+ rx_data->irq = fwnode_irq_get_byname(fwnode, "rx");
+ if (rx_data->irq < 0) {
+ dev_err(&pdev->dev, "dma rx irq invalid\n");
+ return rx_data->irq;
+ }
+
+ device_property_read_u32(&pdev->dev, "clock-frequency", &i2s->clk_rate);
+ if (!i2s->clk_rate) {
+ dev_err(&pdev->dev, "clock-frequency property invalid\n");
+ return -EINVAL;
+ }
+
+ dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+
+ if (i2s->rev_id == 1) {
+ regmap_write(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET);
+ udelay(200);
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &loongson_i2s_component,
+ &loongson_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "register DAI failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct pci_device_id loongson_i2s_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a27) },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, loongson_i2s_ids);
+
+static struct pci_driver loongson_i2s_driver = {
+ .name = "loongson-i2s-pci",
+ .id_table = loongson_i2s_ids,
+ .probe = loongson_i2s_pci_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .pm = pm_sleep_ptr(&loongson_i2s_pm),
+ },
+};
+module_pci_driver(loongson_i2s_driver);
+
+MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 4baac72677d9..90db67e0ce4f 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -223,8 +223,13 @@ config SND_SOC_MT8188
config SND_SOC_MT8188_MT6359
tristate "ASoC Audio driver for MT8188 with MT6359 and I2S codecs"
depends on SND_SOC_MT8188 && MTK_PMIC_WRAP
+ depends on I2C
select SND_SOC_MT6359
select SND_SOC_HDMI_CODEC
+ select SND_SOC_DMIC
+ select SND_SOC_MAX98390
+ select SND_SOC_NAU8315
+ select SND_SOC_NAU8825
help
This adds support for ASoC machine driver for MediaTek MT8188
boards with the MT6359 and other I2S audio codecs.
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c
index 738093451ccb..a58e1e3674de 100644
--- a/sound/soc/mediatek/common/mtk-soundcard-driver.c
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c
@@ -21,8 +21,10 @@ static int set_card_codec_info(struct snd_soc_card *card,
int ret;
codec_node = of_get_child_by_name(sub_node, "codec");
- if (!codec_node)
- return -EINVAL;
+ if (!codec_node) {
+ dev_dbg(dev, "%s no specified codec\n", dai_link->name);
+ return 0;
+ }
/* set card codec info */
ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);
@@ -36,6 +38,47 @@ static int set_card_codec_info(struct snd_soc_card *card,
return 0;
}
+static int set_dailink_daifmt(struct snd_soc_card *card,
+ struct device_node *sub_node,
+ struct snd_soc_dai_link *dai_link)
+{
+ unsigned int daifmt;
+ const char *str;
+ int ret;
+ struct {
+ char *name;
+ unsigned int val;
+ } of_clk_table[] = {
+ { "cpu", SND_SOC_DAIFMT_CBC_CFC },
+ { "codec", SND_SOC_DAIFMT_CBP_CFP },
+ };
+
+ daifmt = snd_soc_daifmt_parse_format(sub_node, NULL);
+ if (daifmt) {
+ dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ dai_link->dai_fmt |= daifmt;
+ }
+
+ /*
+ * check "mediatek,clk-provider = xxx"
+ * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area
+ */
+ ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str);
+ if (ret == 0) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) {
+ if (strcmp(str, of_clk_table[i].name) == 0) {
+ dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ dai_link->dai_fmt |= of_clk_table[i].val;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
int parse_dai_link_info(struct snd_soc_card *card)
{
struct device *dev = card->dev;
@@ -67,6 +110,12 @@ int parse_dai_link_info(struct snd_soc_card *card)
of_node_put(sub_node);
return ret;
}
+
+ ret = set_dailink_daifmt(card, sub_node, dai_link);
+ if (ret < 0) {
+ of_node_put(sub_node);
+ return ret;
+ }
}
return 0;
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index f93c2ec8beb7..06269f7e3756 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -1070,6 +1070,10 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
afe->dev = &pdev->dev;
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id <= 0)
+ return irq_id < 0 ? irq_id : -ENXIO;
+
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
return PTR_ERR(afe->base_addr);
@@ -1156,14 +1160,14 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL);
if (!comp_hdmi) {
ret = -ENOMEM;
- goto err_pm_disable;
+ goto err_cleanup_components;
}
ret = snd_soc_component_initialize(comp_hdmi,
&mt8173_afe_hdmi_dai_component,
&pdev->dev);
if (ret)
- goto err_pm_disable;
+ goto err_cleanup_components;
#ifdef CONFIG_DEBUG_FS
comp_hdmi->debugfs_prefix = "hdmi";
@@ -1175,14 +1179,11 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup_components;
- irq_id = platform_get_irq(pdev, 0);
- if (irq_id <= 0)
- return irq_id < 0 ? irq_id : -ENXIO;
ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
0, "Afe_ISR_Handle", (void *)afe);
if (ret) {
dev_err(afe->dev, "could not request_irq\n");
- goto err_pm_disable;
+ goto err_cleanup_components;
}
dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index e05f2b0231fe..3ece4b5eaca2 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -288,7 +288,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
if (np) {
- ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
+ ret = snd_soc_of_get_dai_name(np, &codec_capture_dai, 0);
of_node_put(np);
if (ret < 0) {
dev_err(&pdev->dev,
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-control.c b/sound/soc/mediatek/mt8186/mt8186-afe-control.c
index d714e9641571..55edf6374578 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-control.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-control.c
@@ -6,7 +6,6 @@
// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
#include "mt8186-afe-common.h"
-#include <linux/pm_runtime.h>
enum {
MTK_AFE_RATE_8K = 0,
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
index cdf54d1eb50d..0432f9d89020 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
@@ -10,7 +10,6 @@
#include <linux/input.h>
#include <linux/module.h>
#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
index 7538274641fd..9c11016f032c 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
@@ -12,7 +12,6 @@
#include <linux/input.h>
#include <linux/module.h>
#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include <sound/rt5682.h>
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
index 0fb97517f82c..e69c1bb2cb23 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
@@ -24,14 +24,19 @@ static const char *aud_clks[MT8188_CLK_NUM] = {
[MT8188_CLK_APMIXED_APLL2] = "apll2",
/* divider */
+ [MT8188_CLK_TOP_APLL1_D4] = "apll1_d4",
+ [MT8188_CLK_TOP_APLL2_D4] = "apll2_d4",
[MT8188_CLK_TOP_APLL12_DIV0] = "apll12_div0",
[MT8188_CLK_TOP_APLL12_DIV1] = "apll12_div1",
[MT8188_CLK_TOP_APLL12_DIV2] = "apll12_div2",
[MT8188_CLK_TOP_APLL12_DIV3] = "apll12_div3",
+ [MT8188_CLK_TOP_APLL12_DIV4] = "apll12_div4",
[MT8188_CLK_TOP_APLL12_DIV9] = "apll12_div9",
/* mux */
[MT8188_CLK_TOP_A1SYS_HP_SEL] = "top_a1sys_hp",
+ [MT8188_CLK_TOP_A2SYS_SEL] = "top_a2sys",
+ [MT8188_CLK_TOP_AUD_IEC_SEL] = "top_aud_iec",
[MT8188_CLK_TOP_AUD_INTBUS_SEL] = "top_aud_intbus",
[MT8188_CLK_TOP_AUDIO_H_SEL] = "top_audio_h",
[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL] = "top_audio_local_bus",
@@ -378,6 +383,19 @@ int mt8188_afe_get_default_mclk_source_by_rate(int rate)
MT8188_MCK_SEL_APLL1 : MT8188_MCK_SEL_APLL2;
}
+int mt8188_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return ((rate % 8000) == 0) ? MT8188_AUD_PLL1 : MT8188_AUD_PLL2;
+}
+
+int mt8188_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8188_AUD_PLL1;
+
+ return MT8188_AUD_PLL2;
+}
+
int mt8188_afe_init_clock(struct mtk_base_afe *afe)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
@@ -470,8 +488,8 @@ int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
if (clk && parent) {
ret = clk_set_parent(clk, parent);
if (ret) {
- dev_dbg(afe->dev, "%s(), failed to set clk parent\n",
- __func__);
+ dev_dbg(afe->dev, "%s(), failed to set clk parent %d\n",
+ __func__, ret);
return ret;
}
}
@@ -598,54 +616,132 @@ static int mt8188_afe_disable_afe_on(struct mtk_base_afe *afe)
return 0;
}
-static int mt8188_afe_enable_timing_sys(struct mtk_base_afe *afe)
+static int mt8188_afe_enable_a1sys(struct mtk_base_afe *afe)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ if (ret)
+ return ret;
- mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
- mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
- mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+}
+
+static int mt8188_afe_disable_a1sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
return 0;
}
-static int mt8188_afe_disable_timing_sys(struct mtk_base_afe *afe)
+static int mt8188_afe_enable_a2sys(struct mtk_base_afe *afe)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
- mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
- mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+ if (ret)
+ return ret;
- mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
- mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
- mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+ return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+}
+
+static int mt8188_afe_disable_a2sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
return 0;
}
-int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe)
+int mt8188_apll1_enable(struct mtk_base_afe *afe)
{
- mt8188_afe_enable_timing_sys(afe);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
- mt8188_afe_enable_afe_on(afe);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+ if (ret)
+ return ret;
+
+ ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL],
+ afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+ if (ret)
+ goto err_clk_parent;
- mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1);
- mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2);
+ ret = mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1);
+ if (ret)
+ goto err_apll_tuner;
+
+ ret = mt8188_afe_enable_a1sys(afe);
+ if (ret)
+ goto err_a1sys;
return 0;
+
+err_a1sys:
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1);
+err_apll_tuner:
+ mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL],
+ afe_priv->clk[MT8188_CLK_XTAL_26M]);
+err_clk_parent:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
+
+ return ret;
}
-int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe)
+int mt8188_apll1_disable(struct mtk_base_afe *afe)
{
- mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_a1sys(afe);
mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1);
+ mt8188_afe_set_clk_parent(afe, afe_priv->clk[MT8188_CLK_TOP_A1SYS_HP_SEL],
+ afe_priv->clk[MT8188_CLK_XTAL_26M]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_APLL1_D4]);
- mt8188_afe_disable_afe_on(afe);
+ return 0;
+}
+
+int mt8188_apll2_enable(struct mtk_base_afe *afe)
+{
+ int ret;
- mt8188_afe_disable_timing_sys(afe);
+ ret = mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2);
+ if (ret)
+ return ret;
+ ret = mt8188_afe_enable_a2sys(afe);
+ if (ret)
+ goto err_a2sys;
+
+ return 0;
+err_a2sys:
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2);
+
+ return ret;
+}
+
+int mt8188_apll2_disable(struct mtk_base_afe *afe)
+{
+ mt8188_afe_disable_a2sys(afe);
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2);
+ return 0;
+}
+
+int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ mt8188_afe_enable_afe_on(afe);
+ return 0;
+}
+
+int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8188_afe_disable_afe_on(afe);
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
return 0;
}
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
index a4203a87a1e3..ec53c171c170 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
@@ -11,6 +11,10 @@
#ifndef _MT8188_AFE_CLK_H_
#define _MT8188_AFE_CLK_H_
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+
enum {
/* xtal */
MT8188_CLK_XTAL_26M,
@@ -18,13 +22,18 @@ enum {
MT8188_CLK_APMIXED_APLL1,
MT8188_CLK_APMIXED_APLL2,
/* divider */
+ MT8188_CLK_TOP_APLL1_D4,
+ MT8188_CLK_TOP_APLL2_D4,
MT8188_CLK_TOP_APLL12_DIV0,
MT8188_CLK_TOP_APLL12_DIV1,
MT8188_CLK_TOP_APLL12_DIV2,
MT8188_CLK_TOP_APLL12_DIV3,
+ MT8188_CLK_TOP_APLL12_DIV4,
MT8188_CLK_TOP_APLL12_DIV9,
/* mux */
MT8188_CLK_TOP_A1SYS_HP_SEL,
+ MT8188_CLK_TOP_A2SYS_SEL,
+ MT8188_CLK_TOP_AUD_IEC_SEL,
MT8188_CLK_TOP_AUD_INTBUS_SEL,
MT8188_CLK_TOP_AUDIO_H_SEL,
MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL,
@@ -99,6 +108,8 @@ struct mtk_base_afe;
int mt8188_afe_get_mclk_source_clk_id(int sel);
int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll);
int mt8188_afe_get_default_mclk_source_by_rate(int rate);
+int mt8188_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8188_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
int mt8188_afe_init_clock(struct mtk_base_afe *afe);
int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
@@ -106,6 +117,10 @@ int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
unsigned int rate);
int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
struct clk *parent);
+int mt8188_apll1_enable(struct mtk_base_afe *afe);
+int mt8188_apll1_disable(struct mtk_base_afe *afe);
+int mt8188_apll2_enable(struct mtk_base_afe *afe);
+int mt8188_apll2_disable(struct mtk_base_afe *afe);
int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe);
int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe);
int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h
index eb7e57c239bd..1304d685a306 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-common.h
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h
@@ -39,7 +39,7 @@ enum {
MT8188_AFE_MEMIF_END,
MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START),
MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END,
- MT8188_AFE_IO_ADDA = MT8188_AFE_IO_START,
+ MT8188_AFE_IO_DL_SRC = MT8188_AFE_IO_START,
MT8188_AFE_IO_DMIC_IN,
MT8188_AFE_IO_DPTX,
MT8188_AFE_IO_ETDM_START,
@@ -52,6 +52,7 @@ enum {
MT8188_AFE_IO_ETDM_NUM =
(MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START),
MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END,
+ MT8188_AFE_IO_UL_SRC,
MT8188_AFE_IO_END,
MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START),
MT8188_DAI_END = MT8188_AFE_IO_END,
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
index bcf7025886df..6a24b339444b 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
@@ -17,6 +17,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
+#include <linux/soc/mediatek/infracfg.h>
#include <linux/reset.h>
#include <sound/pcm_params.h>
#include "mt8188-afe-common.h"
@@ -1898,10 +1899,6 @@ static const struct snd_kcontrol_new mt8188_memif_controls[] = {
MT8188_AFE_MEMIF_UL10),
};
-static const struct snd_soc_component_driver mt8188_afe_pcm_dai_component = {
- .name = "mt8188-afe-pcm-dai",
-};
-
static const struct mtk_base_memif_data memif_data[MT8188_AFE_MEMIF_NUM] = {
[MT8188_AFE_MEMIF_DL2] = {
.name = "DL2",
@@ -3137,14 +3134,69 @@ static int mt8188_afe_parse_of(struct mtk_base_afe *afe,
return 0;
}
+#define MT8188_DELAY_US 10
+#define MT8188_TIMEOUT_US USEC_PER_SEC
+
+static int bus_protect_enable(struct regmap *regmap)
+{
+ int ret;
+ u32 val;
+ u32 mask;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_SET, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, (val & mask) == mask,
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_SET, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, (val & mask) == mask,
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ return ret;
+}
+
+static int bus_protect_disable(struct regmap *regmap)
+{
+ int ret;
+ u32 val;
+ u32 mask;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_CLR, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, !(val & mask),
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ val = 0;
+ mask = MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1;
+ regmap_write(regmap, MT8188_TOP_AXI_PROT_EN_2_CLR, mask);
+
+ ret = regmap_read_poll_timeout(regmap, MT8188_TOP_AXI_PROT_EN_2_STA,
+ val, !(val & mask),
+ MT8188_DELAY_US, MT8188_TIMEOUT_US);
+ return ret;
+}
+
static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
struct mt8188_afe_private *afe_priv;
struct device *dev;
- int i, irq_id, ret;
- struct snd_soc_component *component;
struct reset_control *rstc;
+ struct regmap *infra_ao;
+ int i, irq_id, ret;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
if (ret)
@@ -3168,18 +3220,37 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(afe->base_addr),
"AFE base_addr not found\n");
+ infra_ao = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "mediatek,infracfg");
+ if (IS_ERR(infra_ao))
+ return dev_err_probe(dev, PTR_ERR(infra_ao),
+ "%s() Cannot find infra_ao controller\n",
+ __func__);
+
/* reset controller to reset audio regs before regmap cache */
rstc = devm_reset_control_get_exclusive(dev, "audiosys");
if (IS_ERR(rstc))
return dev_err_probe(dev, PTR_ERR(rstc),
"could not get audiosys reset\n");
+ ret = bus_protect_enable(infra_ao);
+ if (ret) {
+ dev_err(dev, "bus_protect_enable failed\n");
+ return ret;
+ }
+
ret = reset_control_reset(rstc);
if (ret) {
dev_err(dev, "failed to trigger audio reset:%d\n", ret);
return ret;
}
+ ret = bus_protect_disable(infra_ao);
+ if (ret) {
+ dev_err(dev, "bus_protect_disable failed\n");
+ return ret;
+ }
+
/* initial audio related clock */
ret = mt8188_afe_init_clock(afe);
if (ret)
@@ -3276,34 +3347,12 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
/* register component */
ret = devm_snd_soc_register_component(dev, &mt8188_afe_component,
- NULL, 0);
+ afe->dai_drivers, afe->num_dai_drivers);
if (ret) {
dev_warn(dev, "err_platform\n");
goto err_pm_put;
}
- component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL);
- if (!component) {
- ret = -ENOMEM;
- goto err_pm_put;
- }
-
- ret = snd_soc_component_initialize(component,
- &mt8188_afe_pcm_dai_component,
- &pdev->dev);
- if (ret)
- goto err_pm_put;
-#ifdef CONFIG_DEBUG_FS
- component->debugfs_prefix = "pcm";
-#endif
- ret = snd_soc_add_component(component,
- afe->dai_drivers,
- afe->num_dai_drivers);
- if (ret) {
- dev_warn(dev, "err_add_component\n");
- goto err_pm_put;
- }
-
mt8188_afe_init_registers(afe);
pm_runtime_put_sync(&pdev->dev);
@@ -3319,11 +3368,6 @@ err_pm_put:
return ret;
}
-static void mt8188_afe_pcm_dev_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_component(&pdev->dev);
-}
-
static const struct of_device_id mt8188_afe_pcm_dt_match[] = {
{ .compatible = "mediatek,mt8188-afe", },
{},
@@ -3342,7 +3386,6 @@ static struct platform_driver mt8188_afe_pcm_driver = {
.pm = &mt8188_afe_pm_ops,
},
.probe = mt8188_afe_pcm_dev_probe,
- .remove_new = mt8188_afe_pcm_dev_remove,
};
module_platform_driver(mt8188_afe_pcm_driver);
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
index d71696901553..7dc029f2b428 100644
--- a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
@@ -18,7 +18,6 @@
#define ADDA_HIRES_THRES 48000
enum {
- SUPPLY_SEQ_CLOCK_SEL,
SUPPLY_SEQ_ADDA_DL_ON,
SUPPLY_SEQ_ADDA_MTKAIF_CFG,
SUPPLY_SEQ_ADDA_UL_ON,
@@ -54,8 +53,7 @@ enum {
};
struct mtk_dai_adda_priv {
- unsigned int dl_rate;
- unsigned int ul_rate;
+ bool hires_required;
};
static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe,
@@ -242,70 +240,35 @@ static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int mtk_audio_hires_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event)
+static struct mtk_dai_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
{
- struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct clk *clk = afe_priv->clk[MT8188_CLK_TOP_AUDIO_H_SEL];
- struct clk *clk_parent;
-
- dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
- __func__, w->name, event);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- clk_parent = afe_priv->clk[MT8188_CLK_APMIXED_APLL1];
- break;
- case SND_SOC_DAPM_POST_PMD:
- clk_parent = afe_priv->clk[MT8188_CLK_XTAL_26M];
- break;
- default:
- return 0;
- }
- mt8188_afe_set_clk_parent(afe, clk, clk_parent);
-
- return 0;
-}
-
-static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
-{
- struct snd_soc_dapm_widget *w = source;
- struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_adda_priv *adda_priv;
-
- adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA];
- if (!adda_priv) {
- dev_err(afe->dev, "%s adda_priv == NULL", __func__);
- return 0;
- }
-
- return !!(adda_priv->ul_rate > ADDA_HIRES_THRES);
+ if (strstr(name, "aud_adc_hires"))
+ return afe_priv->dai_priv[MT8188_AFE_IO_UL_SRC];
+ else if (strstr(name, "aud_dac_hires"))
+ return afe_priv->dai_priv[MT8188_AFE_IO_DL_SRC];
+ else
+ return NULL;
}
-static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
+static int mtk_afe_adda_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
{
struct snd_soc_dapm_widget *w = source;
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_adda_priv *adda_priv;
- adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA];
+ adda_priv = get_adda_priv_by_name(afe, w->name);
if (!adda_priv) {
- dev_err(afe->dev, "%s adda_priv == NULL", __func__);
+ dev_dbg(afe->dev, "adda_priv == NULL");
return 0;
}
- return !!(adda_priv->dl_rate > ADDA_HIRES_THRES);
+ return (adda_priv->hires_required) ? 1 : 0;
}
static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = {
@@ -364,12 +327,6 @@ static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
mtk_adda_ul_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY_S("AUDIO_HIRES", SUPPLY_SEQ_CLOCK_SEL,
- SND_SOC_NOPM,
- 0, 0,
- mtk_audio_hires_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
-
SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
SND_SOC_NOPM,
0, 0,
@@ -396,8 +353,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
{"ADDA Capture", NULL, "ADDA Capture Enable"},
{"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
{"ADDA Capture", NULL, "aud_adc"},
- {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adc_hires_connect},
- {"aud_adc_hires", NULL, "AUDIO_HIRES"},
+ {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adda_hires_connect},
{"I168", NULL, "ADDA Capture"},
{"I169", NULL, "ADDA Capture"},
@@ -405,8 +361,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
{"ADDA Playback", NULL, "ADDA Enable"},
{"ADDA Playback", NULL, "ADDA Playback Enable"},
{"ADDA Playback", NULL, "aud_dac"},
- {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_dac_hires_connect},
- {"aud_dac_hires", NULL, "AUDIO_HIRES"},
+ {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_adda_hires_connect},
{"DL_GAIN", NULL, "O176"},
{"DL_GAIN", NULL, "O177"},
@@ -540,13 +495,12 @@ static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n",
__func__, id, substream->stream, rate);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- adda_priv->dl_rate = rate;
+ adda_priv->hires_required = (rate > ADDA_HIRES_THRES);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = mtk_dai_da_configure(afe, rate, id);
- } else {
- adda_priv->ul_rate = rate;
+ else
ret = mtk_dai_ad_configure(afe, rate, id);
- }
return ret;
}
@@ -573,8 +527,8 @@ static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
{
- .name = "ADDA",
- .id = MT8188_AFE_IO_ADDA,
+ .name = "DL_SRC",
+ .id = MT8188_AFE_IO_DL_SRC,
.playback = {
.stream_name = "ADDA Playback",
.channels_min = 1,
@@ -582,6 +536,11 @@ static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
.rates = MTK_ADDA_PLAYBACK_RATES,
.formats = MTK_ADDA_FORMATS,
},
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "UL_SRC",
+ .id = MT8188_AFE_IO_UL_SRC,
.capture = {
.stream_name = "ADDA Capture",
.channels_min = 1,
@@ -597,13 +556,18 @@ static int init_adda_priv_data(struct mtk_base_afe *afe)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_adda_priv *adda_priv;
+ int adda_dai_list[] = {MT8188_AFE_IO_DL_SRC, MT8188_AFE_IO_UL_SRC};
+ int i;
- adda_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_adda_priv),
- GFP_KERNEL);
- if (!adda_priv)
- return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
+ adda_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_adda_priv),
+ GFP_KERNEL);
+ if (!adda_priv)
+ return -ENOMEM;
- afe_priv->dai_priv[MT8188_AFE_IO_ADDA] = adda_priv;
+ afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
+ }
return 0;
}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
index 7a37752d4244..16440dd0a89c 100644
--- a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
@@ -22,6 +22,14 @@
#define ENUM_TO_STR(x) #x
enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_ETDM_MCLK,
+ SUPPLY_SEQ_ETDM_CG,
+ SUPPLY_SEQ_DPTX_EN,
+ SUPPLY_SEQ_ETDM_EN,
+};
+
+enum {
MTK_DAI_ETDM_FORMAT_I2S = 0,
MTK_DAI_ETDM_FORMAT_LJ,
MTK_DAI_ETDM_FORMAT_RJ,
@@ -84,11 +92,11 @@ struct mtk_dai_etdm_rate {
};
struct mtk_dai_etdm_priv {
- unsigned int clock_mode;
unsigned int data_mode;
bool slave_mode;
bool lrck_inv;
bool bck_inv;
+ unsigned int rate;
unsigned int format;
unsigned int slots;
unsigned int lrck_width;
@@ -100,8 +108,6 @@ struct mtk_dai_etdm_priv {
unsigned int cowork_slv_count;
int cowork_slv_id[MT8188_AFE_IO_ETDM_NUM - 1]; //dai_id
bool in_disable_ch[MT8188_ETDM_MAX_CHANNELS];
- unsigned int en_ref_cnt;
- bool is_prepared;
};
static const struct mtk_dai_etdm_rate mt8188_etdm_rates[] = {
@@ -345,13 +351,85 @@ static int mtk_dai_etdm_get_clkdiv_id_by_dai_id(int dai_id)
}
}
+static int get_etdm_id_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ if (!strncmp(name, "ETDM1_IN", strlen("ETDM1_IN")))
+ return MT8188_AFE_IO_ETDM1_IN;
+ else if (!strncmp(name, "ETDM2_IN", strlen("ETDM2_IN")))
+ return MT8188_AFE_IO_ETDM2_IN;
+ else if (!strncmp(name, "ETDM1_OUT", strlen("ETDM1_OUT")))
+ return MT8188_AFE_IO_ETDM1_OUT;
+ else if (!strncmp(name, "ETDM2_OUT", strlen("ETDM2_OUT")))
+ return MT8188_AFE_IO_ETDM2_OUT;
+ else if (!strncmp(name, "ETDM3_OUT", strlen("ETDM3_OUT")))
+ return MT8188_AFE_IO_ETDM3_OUT;
+ else if (!strncmp(name, "DPTX", strlen("DPTX")))
+ return MT8188_AFE_IO_ETDM3_OUT;
+ else
+ return -EINVAL;
+}
+
+static struct mtk_dai_etdm_priv *get_etdm_priv_by_name(struct mtk_base_afe *afe,
+ const char *name)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_etdm_id_by_name(afe, name);
+
+ if (dai_id < MT8188_AFE_IO_ETDM_START ||
+ dai_id >= MT8188_AFE_IO_ETDM_END)
+ return NULL;
+
+ return afe_priv->dai_priv[dai_id];
+}
+
static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ unsigned int val = 0;
+ unsigned int mask;
+ int clkmux_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+ int apll_clk_id;
+ int apll;
+ int ret;
- if (clkdiv_id < 0)
+ if (!is_valid_etdm_dai(dai_id))
return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ apll = etdm_data->mclk_apll;
+ apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll);
+
+ if (clkmux_id < 0 || clkdiv_id < 0)
+ return -EINVAL;
+
+ if (apll_clk_id < 0)
+ return apll_clk_id;
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ mask = ETDM_CON1_MCLK_OUTPUT;
+ if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
+ val = ETDM_CON1_MCLK_OUTPUT;
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ /* enable parent clock before select apll*/
+ mt8188_afe_enable_clk(afe, afe_priv->clk[clkmux_id]);
+
+ /* select apll */
+ ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clkmux_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret)
+ return ret;
+
+ /* set rate */
+ ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id],
+ etdm_data->mclk_freq);
mt8188_afe_enable_clk(afe, afe_priv->clk[clkdiv_id]);
@@ -361,12 +439,275 @@ static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id)
static int mtk_dai_etdm_disable_mclk(struct mtk_base_afe *afe, int dai_id)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int clkmux_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
- if (clkdiv_id < 0)
+ if (clkmux_id < 0 || clkdiv_id < 0)
return -EINVAL;
mt8188_afe_disable_clk(afe, afe_priv->clk[clkdiv_id]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[clkmux_id]);
+
+ return 0;
+}
+
+static int mtk_afe_etdm_apll_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int cur_apll;
+ int need_apll;
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+ if (!etdm_priv) {
+ dev_dbg(afe->dev, "etdm_priv == NULL\n");
+ return 0;
+ }
+
+ cur_apll = mt8188_get_apll_by_name(afe, source->name);
+ need_apll = mt8188_get_apll_by_rate(afe, etdm_priv->rate);
+
+ return (need_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_afe_mclk_apll_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int cur_apll;
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+
+ cur_apll = mt8188_get_apll_by_name(afe, source->name);
+
+ return (etdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static int mtk_etdm_mclk_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int mclk_id;
+
+ mclk_id = get_etdm_id_by_name(afe, source->name);
+ if (mclk_id < 0) {
+ dev_dbg(afe->dev, "mclk_id < 0\n");
+ return 0;
+ }
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+ if (!etdm_priv) {
+ dev_dbg(afe->dev, "etdm_priv == NULL\n");
+ return 0;
+ }
+
+ if (get_etdm_id_by_name(afe, sink->name) == mclk_id)
+ return !!(etdm_priv->mclk_freq > 0);
+
+ if (etdm_priv->cowork_source_id == mclk_id) {
+ etdm_priv = afe_priv->dai_priv[mclk_id];
+ return !!(etdm_priv->mclk_freq > 0);
+ }
+
+ return 0;
+}
+
+static int mtk_etdm_cowork_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int source_id;
+ int i;
+
+ source_id = get_etdm_id_by_name(afe, source->name);
+ if (source_id < 0) {
+ dev_dbg(afe->dev, "%s() source_id < 0\n", __func__);
+ return 0;
+ }
+
+ etdm_priv = get_etdm_priv_by_name(afe, w->name);
+ if (!etdm_priv) {
+ dev_dbg(afe->dev, "%s() etdm_priv == NULL\n", __func__);
+ return 0;
+ }
+
+ if (etdm_priv->cowork_source_id != COWORK_ETDM_NONE) {
+ if (etdm_priv->cowork_source_id == source_id)
+ return 1;
+
+ etdm_priv = afe_priv->dai_priv[etdm_priv->cowork_source_id];
+ for (i = 0; i < etdm_priv->cowork_slv_count; i++) {
+ if (etdm_priv->cowork_slv_id[i] == source_id)
+ return 1;
+ }
+ } else {
+ for (i = 0; i < etdm_priv->cowork_slv_count; i++) {
+ if (etdm_priv->cowork_slv_id[i] == source_id)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int mtk_apll_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (strcmp(w->name, APLL1_W_NAME) == 0)
+ mt8188_apll1_enable(afe);
+ else
+ mt8188_apll2_enable(afe);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (strcmp(w->name, APLL1_W_NAME) == 0)
+ mt8188_apll1_disable(afe);
+ else
+ mt8188_apll2_disable(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_etdm_mclk_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ int mclk_id = get_etdm_id_by_name(afe, w->name);
+
+ if (mclk_id < 0) {
+ dev_dbg(afe->dev, "%s() mclk_id < 0\n", __func__);
+ return 0;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_dai_etdm_enable_mclk(afe, mclk_id);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mtk_dai_etdm_disable_mclk(afe, mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dptx_mclk_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_dai_etdm_enable_mclk(afe, MT8188_AFE_IO_DPTX);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mtk_dai_etdm_disable_mclk(afe, MT8188_AFE_IO_DPTX);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_etdm_cg_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int etdm_id;
+ int cg_id;
+
+ etdm_id = get_etdm_id_by_name(afe, w->name);
+ if (etdm_id < 0) {
+ dev_dbg(afe->dev, "%s() etdm_id < 0\n", __func__);
+ return 0;
+ }
+
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(etdm_id);
+ if (cg_id < 0) {
+ dev_dbg(afe->dev, "%s() cg_id < 0\n", __func__);
+ return 0;
+ }
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_etdm3_cg_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 mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_HDMI_OUT]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_HDMI_OUT]);
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -906,11 +1247,181 @@ static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
&hdmi_ch7_mux_control),
+ /* mclk en */
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("DPTX_MCLK", SUPPLY_SEQ_ETDM_MCLK,
+ SND_SOC_NOPM, 0, 0,
+ mtk_dptx_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* cg */
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ETDM3_OUT_CG", SUPPLY_SEQ_ETDM_CG,
+ SND_SOC_NOPM, 0, 0,
+ mtk_etdm3_cg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* en */
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_IN_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_IN1_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_IN_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_IN2_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM1_OUT_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_OUT1_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM2_OUT_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_OUT2_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ETDM3_OUT_EN", SUPPLY_SEQ_ETDM_EN,
+ ETDM_OUT3_CON0, ETDM_CON0_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DPTX_EN", SUPPLY_SEQ_DPTX_EN,
+ AFE_DPTX_CON, AFE_DPTX_CON_ON_SHIFT, 0, NULL, 0),
+
+ /* apll */
+ SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL,
+ SND_SOC_NOPM, 0, 0,
+ mtk_apll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_INPUT("ETDM_INPUT"),
SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"),
};
static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
+ /* mclk */
+ {"ETDM1_IN", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_IN", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_IN", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_IN", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"ETDM2_IN", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_IN", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_IN", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_IN", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"ETDM1_OUT", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_OUT", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"ETDM2_OUT", NULL, "ETDM1_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_IN_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_OUT", NULL, "ETDM1_OUT_MCLK", mtk_etdm_mclk_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_OUT_MCLK", mtk_etdm_mclk_connect},
+
+ {"DPTX", NULL, "DPTX_MCLK"},
+
+ {"ETDM1_IN_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM1_IN_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"ETDM2_IN_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM2_IN_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"ETDM1_OUT_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM1_OUT_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"ETDM2_OUT_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"ETDM2_OUT_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ {"DPTX_MCLK", NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect},
+ {"DPTX_MCLK", NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect},
+
+ /* cg */
+ {"ETDM1_IN", NULL, "ETDM1_IN_CG"},
+ {"ETDM1_IN", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect},
+
+ {"ETDM2_IN", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_IN_CG"},
+ {"ETDM2_IN", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect},
+
+ {"ETDM1_OUT", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM1_OUT_CG"},
+ {"ETDM1_OUT", NULL, "ETDM2_OUT_CG", mtk_etdm_cowork_connect},
+
+ {"ETDM2_OUT", NULL, "ETDM1_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_IN_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM1_OUT_CG", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_OUT_CG"},
+
+ {"ETDM3_OUT", NULL, "ETDM3_OUT_CG"},
+ {"DPTX", NULL, "ETDM3_OUT_CG"},
+
+ /* en */
+ {"ETDM1_IN", NULL, "ETDM1_IN_EN"},
+ {"ETDM1_IN", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_IN", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect},
+
+ {"ETDM2_IN", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_IN_EN"},
+ {"ETDM2_IN", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_IN", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect},
+
+ {"ETDM1_OUT", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM1_OUT", NULL, "ETDM1_OUT_EN"},
+ {"ETDM1_OUT", NULL, "ETDM2_OUT_EN", mtk_etdm_cowork_connect},
+
+ {"ETDM2_OUT", NULL, "ETDM1_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_IN_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM1_OUT_EN", mtk_etdm_cowork_connect},
+ {"ETDM2_OUT", NULL, "ETDM2_OUT_EN"},
+
+ {"ETDM3_OUT", NULL, "ETDM3_OUT_EN"},
+ {"DPTX", NULL, "ETDM3_OUT_EN"},
+ {"DPTX", NULL, "DPTX_EN"},
+
+ {"ETDM1_IN_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM1_IN_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM2_IN_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM2_IN_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM1_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM1_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM2_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM2_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
+ {"ETDM3_OUT_EN", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
+ {"ETDM3_OUT_EN", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
+
{"I012", NULL, "ETDM2_IN"},
{"I013", NULL, "ETDM2_IN"},
{"I014", NULL, "ETDM2_IN"},
@@ -1163,64 +1674,6 @@ static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
{"ETDM2_IN", NULL, "ETDM_INPUT"},
};
-static int mt8188_afe_enable_etdm(struct mtk_base_afe *afe, int dai_id)
-{
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *etdm_data;
- struct etdm_con_reg etdm_reg;
- unsigned long flags;
- int ret = 0;
-
- if (!is_valid_etdm_dai(dai_id))
- return -EINVAL;
- etdm_data = afe_priv->dai_priv[dai_id];
-
- dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt);
- spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
- etdm_data->en_ref_cnt++;
- if (etdm_data->en_ref_cnt == 1) {
- ret = get_etdm_reg(dai_id, &etdm_reg);
- if (ret < 0)
- goto out;
-
- regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_EN);
- }
-
-out:
- spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
- return ret;
-}
-
-static int mt8188_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id)
-{
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *etdm_data;
- struct etdm_con_reg etdm_reg;
- unsigned long flags;
- int ret = 0;
-
- if (!is_valid_etdm_dai(dai_id))
- return -EINVAL;
- etdm_data = afe_priv->dai_priv[dai_id];
-
- dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt);
- spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
- if (etdm_data->en_ref_cnt > 0) {
- etdm_data->en_ref_cnt--;
- if (etdm_data->en_ref_cnt == 0) {
- ret = get_etdm_reg(dai_id, &etdm_reg);
- if (ret < 0)
- goto out;
- regmap_clear_bits(afe->regmap, etdm_reg.con0,
- ETDM_CON0_EN);
- }
- }
-
-out:
- spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
- return ret;
-}
-
static int etdm_cowork_slv_sel(int id, int slave_mode)
{
if (slave_mode) {
@@ -1408,121 +1861,6 @@ static int mt8188_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id)
}
/* dai ops */
-static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *mst_etdm_data;
- int mst_dai_id;
- int slv_dai_id;
- int cg_id;
- int i;
-
- if (is_cowork_mode(dai)) {
- mst_dai_id = get_etdm_cowork_master_id(dai);
- if (!is_valid_etdm_dai(mst_dai_id))
- return -EINVAL;
- mtk_dai_etdm_enable_mclk(afe, mst_dai_id);
-
- cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
- if (cg_id >= 0)
- mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
-
- mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
-
- for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
- slv_dai_id = mst_etdm_data->cowork_slv_id[i];
- cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id);
- if (cg_id >= 0)
- mt8188_afe_enable_clk(afe,
- afe_priv->clk[cg_id]);
- }
- } else {
- mtk_dai_etdm_enable_mclk(afe, dai->id);
-
- cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
- if (cg_id >= 0)
- mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
- }
-
- return 0;
-}
-
-static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *mst_etdm_data;
- int mst_dai_id;
- int slv_dai_id;
- int cg_id;
- int ret;
- int i;
-
- if (!is_valid_etdm_dai(dai->id))
- return;
- mst_etdm_data = afe_priv->dai_priv[dai->id];
-
- dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id,
- mst_etdm_data->is_prepared);
-
- if (mst_etdm_data->is_prepared) {
- mst_etdm_data->is_prepared = false;
-
- if (is_cowork_mode(dai)) {
- mst_dai_id = get_etdm_cowork_master_id(dai);
- if (!is_valid_etdm_dai(mst_dai_id))
- return;
- mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
-
- ret = mt8188_afe_disable_etdm(afe, mst_dai_id);
- if (ret)
- dev_dbg(afe->dev, "%s disable %d failed\n",
- __func__, mst_dai_id);
-
- for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
- slv_dai_id = mst_etdm_data->cowork_slv_id[i];
- ret = mt8188_afe_disable_etdm(afe, slv_dai_id);
- if (ret)
- dev_dbg(afe->dev, "%s disable %d failed\n",
- __func__, slv_dai_id);
- }
- } else {
- ret = mt8188_afe_disable_etdm(afe, dai->id);
- if (ret)
- dev_dbg(afe->dev, "%s disable %d failed\n",
- __func__, dai->id);
- }
- }
-
- if (is_cowork_mode(dai)) {
- mst_dai_id = get_etdm_cowork_master_id(dai);
- if (!is_valid_etdm_dai(mst_dai_id))
- return;
- cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
- if (cg_id >= 0)
- mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
-
- mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
- for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
- slv_dai_id = mst_etdm_data->cowork_slv_id[i];
- cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id);
- if (cg_id >= 0)
- mt8188_afe_disable_clk(afe,
- afe_priv->clk[cg_id]);
- }
- mtk_dai_etdm_disable_mclk(afe, mst_dai_id);
- } else {
- cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
- if (cg_id >= 0)
- mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
-
- mtk_dai_etdm_disable_mclk(afe, dai->id);
- }
-}
-
static int mtk_dai_etdm_fifo_mode(struct mtk_base_afe *afe,
int dai_id, unsigned int rate)
{
@@ -1759,60 +2097,6 @@ static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe,
return 0;
}
-static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id)
-{
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *etdm_data;
- struct etdm_con_reg etdm_reg;
- int clk_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
- int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
- int apll_clk_id;
- int apll;
- int ret;
-
- if (clk_id < 0 || clkdiv_id < 0)
- return -EINVAL;
-
- if (!is_valid_etdm_dai(dai_id))
- return -EINVAL;
- etdm_data = afe_priv->dai_priv[dai_id];
-
- ret = get_etdm_reg(dai_id, &etdm_reg);
- if (ret < 0)
- return ret;
-
- if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
- regmap_set_bits(afe->regmap, etdm_reg.con1,
- ETDM_CON1_MCLK_OUTPUT);
- else
- regmap_clear_bits(afe->regmap, etdm_reg.con1,
- ETDM_CON1_MCLK_OUTPUT);
-
- if (etdm_data->mclk_freq) {
- apll = etdm_data->mclk_apll;
- apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll);
- if (apll_clk_id < 0)
- return apll_clk_id;
-
- /* select apll */
- ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clk_id],
- afe_priv->clk[apll_clk_id]);
- if (ret)
- return ret;
-
- /* set rate */
- ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id],
- etdm_data->mclk_freq);
- if (ret)
- return ret;
- } else {
- if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
- dev_dbg(afe->dev, "%s mclk freq = 0\n", __func__);
- }
-
- return 0;
-}
-
static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
unsigned int rate,
unsigned int channels,
@@ -1834,15 +2118,16 @@ static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
return -EINVAL;
etdm_data = afe_priv->dai_priv[dai_id];
slave_mode = etdm_data->slave_mode;
+ etdm_data->rate = rate;
ret = get_etdm_reg(dai_id, &etdm_reg);
if (ret < 0)
return ret;
- dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, clock %u slv %u\n",
+ dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, slv %u\n",
__func__, etdm_data->format, etdm_data->data_mode,
etdm_data->lrck_inv, etdm_data->lrck_width, etdm_data->bck_inv,
- etdm_data->clock_mode, etdm_data->slave_mode);
+ etdm_data->slave_mode);
dev_dbg(afe->dev, "%s rate %u channels %u bitwidth %u, id %d\n",
__func__, rate, channels, bit_width, dai_id);
@@ -1909,16 +2194,15 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
if (!is_valid_etdm_dai(mst_dai_id))
return -EINVAL;
- ret = mtk_dai_etdm_mclk_configure(afe, mst_dai_id);
- if (ret)
- return ret;
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+ if (mst_etdm_data->slots)
+ channels = mst_etdm_data->slots;
ret = mtk_dai_etdm_configure(afe, rate, channels,
bit_width, mst_dai_id);
if (ret)
return ret;
- mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
slv_dai_id = mst_etdm_data->cowork_slv_id[i];
ret = mtk_dai_etdm_configure(afe, rate, channels,
@@ -1931,9 +2215,11 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
} else {
- ret = mtk_dai_etdm_mclk_configure(afe, dai->id);
- if (ret)
- return ret;
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ mst_etdm_data = afe_priv->dai_priv[dai->id];
+ if (mst_etdm_data->slots)
+ channels = mst_etdm_data->slots;
ret = mtk_dai_etdm_configure(afe, rate, channels,
bit_width, dai->id);
@@ -1944,66 +2230,6 @@ static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int mtk_dai_etdm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *mst_etdm_data;
- int mst_dai_id;
- int slv_dai_id;
- int ret;
- int i;
-
- if (!is_valid_etdm_dai(dai->id))
- return -EINVAL;
- mst_etdm_data = afe_priv->dai_priv[dai->id];
-
- dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id,
- mst_etdm_data->is_prepared);
-
- if (mst_etdm_data->is_prepared)
- return 0;
-
- mst_etdm_data->is_prepared = true;
-
- if (is_cowork_mode(dai)) {
- mst_dai_id = get_etdm_cowork_master_id(dai);
- if (!is_valid_etdm_dai(mst_dai_id))
- return -EINVAL;
- mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
-
- for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
- slv_dai_id = mst_etdm_data->cowork_slv_id[i];
- ret = mt8188_afe_enable_etdm(afe, slv_dai_id);
- if (ret) {
- dev_dbg(afe->dev, "%s enable %d failed\n",
- __func__, slv_dai_id);
-
- return ret;
- }
- }
-
- ret = mt8188_afe_enable_etdm(afe, mst_dai_id);
- if (ret) {
- dev_dbg(afe->dev, "%s enable %d failed\n",
- __func__, mst_dai_id);
-
- return ret;
- }
- } else {
- ret = mt8188_afe_enable_etdm(afe, dai->id);
- if (ret) {
- dev_dbg(afe->dev, "%s enable %d failed\n",
- __func__, dai->id);
-
- return ret;
- }
- }
-
- return 0;
-}
-
static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
@@ -2073,10 +2299,16 @@ static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai,
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
- if (!is_valid_etdm_dai(dai->id))
+ if (is_cowork_mode(dai))
+ dai_id = get_etdm_cowork_master_id(dai);
+ else
+ dai_id = dai->id;
+
+ if (!is_valid_etdm_dai(dai_id))
return -EINVAL;
- etdm_data = afe_priv->dai_priv[dai->id];
+ etdm_data = afe_priv->dai_priv[dai_id];
dev_dbg(dai->dev, "%s id %d slot_width %d\n",
__func__, dai->id, slot_width);
@@ -2151,53 +2383,6 @@ static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int mtk_dai_hdmitx_dptx_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
-
- if (cg_id >= 0)
- mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
-
- mtk_dai_etdm_enable_mclk(afe, dai->id);
-
- return 0;
-}
-
-static void mtk_dai_hdmitx_dptx_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
- struct mtk_dai_etdm_priv *etdm_data;
- int ret;
-
- if (!is_valid_etdm_dai(dai->id))
- return;
- etdm_data = afe_priv->dai_priv[dai->id];
-
- if (etdm_data->is_prepared) {
- etdm_data->is_prepared = false;
- /* disable etdm_out3 */
- ret = mt8188_afe_disable_etdm(afe, dai->id);
- if (ret)
- dev_dbg(afe->dev, "%s disable failed\n", __func__);
-
- /* disable dptx interface */
- if (dai->id == MT8188_AFE_IO_DPTX)
- regmap_clear_bits(afe->regmap, AFE_DPTX_CON,
- AFE_DPTX_CON_ON);
- }
-
- mtk_dai_etdm_disable_mclk(afe, dai->id);
-
- if (cg_id >= 0)
- mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
-}
-
static unsigned int mtk_dai_get_dptx_ch_en(unsigned int channel)
{
switch (channel) {
@@ -2265,42 +2450,11 @@ static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream,
etdm_data->data_mode = MTK_DAI_ETDM_DATA_MULTI_PIN;
}
- ret = mtk_dai_etdm_mclk_configure(afe, dai->id);
- if (ret)
- return ret;
-
ret = mtk_dai_etdm_configure(afe, rate, channels, width, dai->id);
return ret;
}
-static int mtk_dai_hdmitx_dptx_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
- struct mt8188_afe_private *afe_priv = afe->platform_priv;
- struct mtk_dai_etdm_priv *etdm_data;
-
- if (!is_valid_etdm_dai(dai->id))
- return -EINVAL;
- etdm_data = afe_priv->dai_priv[dai->id];
-
- dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id,
- etdm_data->is_prepared);
-
- if (etdm_data->is_prepared)
- return 0;
-
- etdm_data->is_prepared = true;
-
- /* enable dptx interface */
- if (dai->id == MT8188_AFE_IO_DPTX)
- regmap_set_bits(afe->regmap, AFE_DPTX_CON, AFE_DPTX_CON_ON);
-
- /* enable etdm_out3 */
- return mt8188_afe_enable_etdm(afe, dai->id);
-}
-
static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai,
int clk_id,
unsigned int freq,
@@ -2322,20 +2476,14 @@ static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai,
}
static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
- .startup = mtk_dai_etdm_startup,
- .shutdown = mtk_dai_etdm_shutdown,
.hw_params = mtk_dai_etdm_hw_params,
- .prepare = mtk_dai_etdm_prepare,
.set_sysclk = mtk_dai_etdm_set_sysclk,
.set_fmt = mtk_dai_etdm_set_fmt,
.set_tdm_slot = mtk_dai_etdm_set_tdm_slot,
};
static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = {
- .startup = mtk_dai_hdmitx_dptx_startup,
- .shutdown = mtk_dai_hdmitx_dptx_shutdown,
.hw_params = mtk_dai_hdmitx_dptx_hw_params,
- .prepare = mtk_dai_hdmitx_dptx_prepare,
.set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk,
.set_fmt = mtk_dai_etdm_set_fmt,
};
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index 919d74ea1934..ac69c23e0da1 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -6,6 +6,8 @@
* Author: Trevor Wu <trevor.wu@mediatek.com>
*/
+#include <linux/bitfield.h>
+#include <linux/input.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
@@ -13,10 +15,36 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "mt8188-afe-common.h"
+#include "../../codecs/nau8825.h"
#include "../../codecs/mt6359.h"
#include "../common/mtk-afe-platform-driver.h"
#include "../common/mtk-soundcard-driver.h"
+#define CKSYS_AUD_TOP_CFG 0x032c
+ #define RG_TEST_ON BIT(0)
+ #define RG_TEST_TYPE BIT(2)
+#define CKSYS_AUD_TOP_MON 0x0330
+ #define TEST_MISO_COUNT_1 GENMASK(3, 0)
+ #define TEST_MISO_COUNT_2 GENMASK(7, 4)
+ #define TEST_MISO_DONE_1 BIT(28)
+ #define TEST_MISO_DONE_2 BIT(29)
+
+#define NAU8825_HS_PRESENT BIT(0)
+
+/*
+ * Maxim MAX98390
+ */
+#define MAX98390_CODEC_DAI "max98390-aif1"
+#define MAX98390_DEV0_NAME "max98390.0-0038" /* rear right */
+#define MAX98390_DEV1_NAME "max98390.0-0039" /* rear left */
+#define MAX98390_DEV2_NAME "max98390.0-003a" /* front right */
+#define MAX98390_DEV3_NAME "max98390.0-003b" /* front left */
+
+/*
+ * Nau88l25
+ */
+#define NAU8825_CODEC_DAI "nau8825-hifi"
+
/* FE */
SND_SOC_DAILINK_DEFS(playback2,
DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
@@ -99,8 +127,8 @@ SND_SOC_DAILINK_DEFS(capture10,
DAILINK_COMP_ARRAY(COMP_EMPTY()));
/* BE */
-SND_SOC_DAILINK_DEFS(adda,
- DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+SND_SOC_DAILINK_DEFS(dl_src,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
"mt6359-snd-codec-aif1")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
@@ -140,9 +168,44 @@ SND_SOC_DAILINK_DEFS(pcm1,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(ul_src,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1"),
+ COMP_CODEC("dmic-codec",
+ "dmic-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
struct mt8188_mt6359_priv {
struct snd_soc_jack dp_jack;
struct snd_soc_jack hdmi_jack;
+ struct snd_soc_jack headset_jack;
+ void *private_data;
+};
+
+static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = {
+ {
+ .pin = "HDMI",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = {
+ {
+ .pin = "DP",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static struct snd_soc_jack_pin nau8825_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
};
struct mt8188_card_data {
@@ -150,9 +213,39 @@ struct mt8188_card_data {
unsigned long quirk;
};
+static const struct snd_kcontrol_new mt8188_dumb_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_dumb_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_dual_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_dual_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_rear_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Rear Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Rear Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8188_rear_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Rear Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Rear Right Spk", NULL),
+};
+
static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SINK("HDMI"),
+ SND_SOC_DAPM_SINK("DP"),
};
static const struct snd_kcontrol_new mt8188_mt6359_controls[] = {
@@ -160,8 +253,13 @@ static const struct snd_kcontrol_new mt8188_mt6359_controls[] = {
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
-#define CKSYS_AUD_TOP_CFG 0x032c
-#define CKSYS_AUD_TOP_MON 0x0330
+static const struct snd_soc_dapm_widget mt8188_nau8825_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_nau8825_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
{
@@ -174,13 +272,13 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
struct mtkaif_param *param;
int chosen_phase_1, chosen_phase_2;
int prev_cycle_1, prev_cycle_2;
- int test_done_1, test_done_2;
+ u8 test_done_1, test_done_2;
int cycle_1, cycle_2;
int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM];
int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM];
int mtkaif_calibration_num_phase;
bool mtkaif_calibration_ok;
- unsigned int monitor = 0;
+ u32 monitor = 0;
int counter;
int phase;
int i;
@@ -212,8 +310,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
mt6359_mtkaif_calibration_enable(cmpnt_codec);
/* set test type to synchronizer pulse */
- regmap_update_bits(afe_priv->topckgen,
- CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
+ regmap_write(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_TYPE);
mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
mtkaif_calibration_ok = true;
@@ -223,7 +320,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
phase, phase, phase);
- regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1);
+ regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_ON);
test_done_1 = 0;
test_done_2 = 0;
@@ -235,20 +332,19 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
while (!(test_done_1 & test_done_2)) {
regmap_read(afe_priv->topckgen,
CKSYS_AUD_TOP_MON, &monitor);
- test_done_1 = (monitor >> 28) & 0x1;
- test_done_2 = (monitor >> 29) & 0x1;
+ test_done_1 = FIELD_GET(TEST_MISO_DONE_1, monitor);
+ test_done_2 = FIELD_GET(TEST_MISO_DONE_2, monitor);
if (test_done_1 == 1)
- cycle_1 = monitor & 0xf;
+ cycle_1 = FIELD_GET(TEST_MISO_COUNT_1, monitor);
if (test_done_2 == 1)
- cycle_2 = (monitor >> 4) & 0xf;
+ cycle_2 = FIELD_GET(TEST_MISO_COUNT_2, monitor);
/* handle if never test done */
if (++counter > 10000) {
- dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n",
- __func__,
- cycle_1, cycle_2, monitor);
+ dev_err(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n",
+ __func__, cycle_1, cycle_2, monitor);
mtkaif_calibration_ok = false;
break;
}
@@ -271,7 +367,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] = prev_cycle_2;
}
- regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1);
+ regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, RG_TEST_ON);
if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] >= 0 &&
mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] >= 0)
@@ -307,8 +403,8 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++)
param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
- dev_info(afe->dev, "%s(), end, calibration ok %d\n",
- __func__, param->mtkaif_calibration_ok);
+ dev_dbg(afe->dev, "%s(), end, calibration ok %d\n",
+ __func__, param->mtkaif_calibration_ok);
return 0;
}
@@ -345,7 +441,7 @@ enum {
DAI_LINK_UL8_FE,
DAI_LINK_UL9_FE,
DAI_LINK_UL10_FE,
- DAI_LINK_ADDA_BE,
+ DAI_LINK_DL_SRC_BE,
DAI_LINK_DPTX_BE,
DAI_LINK_ETDM1_IN_BE,
DAI_LINK_ETDM2_IN_BE,
@@ -353,6 +449,7 @@ enum {
DAI_LINK_ETDM2_OUT_BE,
DAI_LINK_ETDM3_OUT_BE,
DAI_LINK_PCM1_BE,
+ DAI_LINK_UL_SRC_BE,
};
static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream,
@@ -389,19 +486,23 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
int ret = 0;
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &priv->hdmi_jack);
+ ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack",
+ SND_JACK_LINEOUT, &priv->hdmi_jack,
+ mt8188_hdmi_jack_pins,
+ ARRAY_SIZE(mt8188_hdmi_jack_pins));
if (ret) {
- dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
return ret;
}
ret = snd_soc_component_set_jack(component, &priv->hdmi_jack, NULL);
- if (ret)
- dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
- __func__, component->name, ret);
+ if (ret) {
+ dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+ return ret;
+ }
- return ret;
+ return 0;
}
static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
@@ -410,21 +511,207 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
int ret = 0;
- ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
- &priv->dp_jack);
+ ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT,
+ &priv->dp_jack, mt8188_dp_jack_pins,
+ ARRAY_SIZE(mt8188_dp_jack_pins));
if (ret) {
- dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ dev_err(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
return ret;
}
ret = snd_soc_component_set_jack(component, &priv->dp_jack, NULL);
- if (ret)
- dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
- __func__, component->name, ret);
+ if (ret) {
+ dev_err(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+ return ret;
+ }
- return ret;
+ return 0;
+}
+
+static int mt8188_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret = 0;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dumb_spk_widgets,
+ ARRAY_SIZE(mt8188_dumb_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_dumb_spk_controls,
+ ARRAY_SIZE(mt8188_dumb_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_max98390_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int bit_width = params_width(params);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ int i;
+
+ snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0xf, 4, bit_width);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV0_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x3, 4, bit_width);
+
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV1_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x3, 4, bit_width);
+
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV2_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x2, 0x3, 4, bit_width);
+
+ if (!strcmp(codec_dai->component->name, MAX98390_DEV3_NAME))
+ snd_soc_dai_set_tdm_slot(codec_dai, 0x1, 0x3, 4, bit_width);
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt8188_max98390_ops = {
+ .hw_params = mt8188_max98390_hw_params,
+};
+
+static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ /* add regular speakers dapm route */
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dual_spk_widgets,
+ ARRAY_SIZE(mt8188_dual_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Left/Right Speaker widget, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_dual_spk_controls,
+ ARRAY_SIZE(mt8188_dual_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Left/Right card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ if (rtd->dai_link->num_codecs <= 2)
+ return 0;
+
+ /* add widgets/controls/dapm for rear speakers */
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_rear_spk_widgets,
+ ARRAY_SIZE(mt8188_rear_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Rear Speaker widget, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_rear_spk_controls,
+ ARRAY_SIZE(mt8188_rear_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Rear card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_jack *jack = &priv->headset_jack;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_nau8825_widgets,
+ ARRAY_SIZE(mt8188_nau8825_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8188_nau8825_controls,
+ ARRAY_SIZE(mt8188_nau8825_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ nau8825_jack_pins,
+ ARRAY_SIZE(nau8825_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static void mt8188_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
}
+static int mt8188_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ int clk_freq, ret;
+
+ clk_freq = rate * 2 * bit_width;
+
+ /* Configure clock for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
+ return ret;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8188_nau8825_ops = {
+ .hw_params = mt8188_nau8825_hw_params,
+};
static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
/* FE */
[DAI_LINK_DL2_FE] = {
@@ -604,13 +891,11 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
SND_SOC_DAILINK_REG(capture10),
},
/* BE */
- [DAI_LINK_ADDA_BE] = {
- .name = "ADDA_BE",
+ [DAI_LINK_DL_SRC_BE] = {
+ .name = "DL_SRC_BE",
.no_pcm = 1,
.dpcm_playback = 1,
- .dpcm_capture = 1,
- .init = mt8188_mt6359_init,
- SND_SOC_DAILINK_REG(adda),
+ SND_SOC_DAILINK_REG(dl_src),
},
[DAI_LINK_DPTX_BE] = {
.name = "DPTX_BE",
@@ -676,8 +961,48 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(pcm1),
},
+ [DAI_LINK_UL_SRC_BE] = {
+ .name = "UL_SRC_BE",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(ul_src),
+ },
};
+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 void mt8188_fixup_controls(struct snd_soc_card *card)
+{
+ struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
+ struct mt8188_card_data *card_data = (struct mt8188_card_data *)priv->private_data;
+ struct snd_kcontrol *kctl;
+
+ if (card_data->quirk & NAU8825_HS_PRESENT) {
+ struct snd_soc_dapm_widget *w, *next_w;
+
+ for_each_card_widgets_safe(card, w, next_w) {
+ if (strcmp(w->name, "Headphone"))
+ continue;
+
+ snd_soc_dapm_free_widget(w);
+ }
+
+ kctl = ctl_find(card->snd_card, "Headphone Switch");
+ if (kctl)
+ snd_ctl_remove(card->snd_card, kctl);
+ else
+ dev_warn(card->dev, "Cannot find ctl : Headphone Switch\n");
+ }
+}
+
static struct snd_soc_card mt8188_mt6359_soc_card = {
.owner = THIS_MODULE,
.dai_link = mt8188_mt6359_dai_links,
@@ -686,6 +1011,7 @@ static struct snd_soc_card mt8188_mt6359_soc_card = {
.num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets),
.controls = mt8188_mt6359_controls,
.num_controls = ARRAY_SIZE(mt8188_mt6359_controls),
+ .fixup_controls = mt8188_fixup_controls,
};
static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
@@ -695,6 +1021,10 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
struct mt8188_mt6359_priv *priv;
struct mt8188_card_data *card_data;
struct snd_soc_dai_link *dai_link;
+ bool init_mt6359 = false;
+ bool init_nau8825 = false;
+ bool init_max98390 = false;
+ bool init_dumb = false;
int ret, i;
card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev);
@@ -739,9 +1069,41 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
} else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
dai_link->init = mt8188_hdmi_codec_init;
+ } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC_BE") == 0) {
+ if (!init_mt6359) {
+ dai_link->init = mt8188_mt6359_init;
+ init_mt6359 = true;
+ }
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM1_IN_BE") == 0 ||
+ strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) {
+ dai_link->ops = &mt8188_max98390_ops;
+ if (!init_max98390) {
+ dai_link->init = mt8188_max98390_codec_init;
+ init_max98390 = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) {
+ dai_link->ops = &mt8188_nau8825_ops;
+ if (!init_nau8825) {
+ dai_link->init = mt8188_nau8825_codec_init;
+ dai_link->exit = mt8188_nau8825_codec_exit;
+ init_nau8825 = true;
+ }
+ } else {
+ if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
+ if (!init_dumb) {
+ dai_link->init = mt8188_dumb_amp_init;
+ init_dumb = true;
+ }
+ }
+ }
}
}
+ priv->private_data = card_data;
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, card);
@@ -758,12 +1120,15 @@ static struct mt8188_card_data mt8188_evb_card = {
.name = "mt8188_mt6359",
};
+static struct mt8188_card_data mt8188_nau8825_card = {
+ .name = "mt8188_nau8825",
+ .quirk = NAU8825_HS_PRESENT,
+};
+
static const struct of_device_id mt8188_mt6359_dt_match[] = {
- {
- .compatible = "mediatek,mt8188-mt6359-evb",
- .data = &mt8188_evb_card,
- },
- {},
+ { .compatible = "mediatek,mt8188-mt6359-evb", .data = &mt8188_evb_card, },
+ { .compatible = "mediatek,mt8188-nau8825", .data = &mt8188_nau8825_card, },
+ { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match);
diff --git a/sound/soc/mediatek/mt8188/mt8188-reg.h b/sound/soc/mediatek/mt8188/mt8188-reg.h
index 51cd1a83dd9d..bdd885419ff3 100644
--- a/sound/soc/mediatek/mt8188/mt8188-reg.h
+++ b/sound/soc/mediatek/mt8188/mt8188-reg.h
@@ -3007,6 +3007,7 @@
#define ETDM_CON0_SLAVE_MODE BIT(5)
#define ETDM_CON0_SYNC_MODE BIT(1)
#define ETDM_CON0_EN BIT(0)
+#define ETDM_CON0_EN_SHIFT 0
#define ETDM_OUT_CON0_RELATCH_DOMAIN_MASK GENMASK(29, 28)
@@ -3108,6 +3109,7 @@
#define AFE_DPTX_CON_CH_NUM_8CH (0x1 << 1)
#define AFE_DPTX_CON_CH_NUM_MASK BIT(1)
#define AFE_DPTX_CON_ON BIT(0)
+#define AFE_DPTX_CON_ON_SHIFT 0
/* AFE_ADDA_DL_SRC2_CON0 */
#define DL_2_INPUT_MODE_CTL_MASK GENMASK(31, 28)
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-control.c b/sound/soc/mediatek/mt8192/mt8192-afe-control.c
index 9163e05e54e1..d01b62e10088 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-control.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-control.c
@@ -6,8 +6,6 @@
// Author: Shane Chien <shane.chien@mediatek.com>
//
-#include <linux/pm_runtime.h>
-
#include "mt8192-afe-common.h"
enum {
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
index 03dabc056b91..a0f2012211fb 100644
--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -3030,28 +3030,6 @@ static const struct reg_sequence mt8195_cg_patch[] = {
{ AUDIO_TOP_CON1, 0xfffffff8 },
};
-static int mt8195_afe_init_registers(struct mtk_base_afe *afe)
-{
- return regmap_multi_reg_write(afe->regmap,
- mt8195_afe_reg_defaults,
- ARRAY_SIZE(mt8195_afe_reg_defaults));
-}
-
-static void mt8195_afe_parse_of(struct mtk_base_afe *afe,
- struct device_node *np)
-{
-#if IS_ENABLED(CONFIG_SND_SOC_MT6359)
- struct mt8195_afe_private *afe_priv = afe->platform_priv;
-
- afe_priv->topckgen = syscon_regmap_lookup_by_phandle(afe->dev->of_node,
- "mediatek,topckgen");
- if (IS_ERR(afe_priv->topckgen)) {
- dev_info(afe->dev, "%s() Cannot find topckgen controller: %ld\n",
- __func__, PTR_ERR(afe_priv->topckgen));
- }
-#endif
-}
-
static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
@@ -3062,10 +3040,8 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
struct snd_soc_component *component;
ret = of_reserved_mem_device_init(dev);
- if (ret) {
- dev_err(dev, "failed to assign memory region: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to assign memory region\n");
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
@@ -3089,24 +3065,17 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
/* initial audio related clock */
ret = mt8195_afe_init_clock(afe);
- if (ret) {
- dev_err(dev, "init clock error\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "init clock error\n");
/* reset controller to reset audio regs before regmap cache */
rstc = devm_reset_control_get_exclusive(dev, "audiosys");
- if (IS_ERR(rstc)) {
- ret = PTR_ERR(rstc);
- dev_err(dev, "could not get audiosys reset:%d\n", ret);
- return ret;
- }
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n");
ret = reset_control_reset(rstc);
- if (ret) {
- dev_err(dev, "failed to trigger audio reset:%d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
spin_lock_init(&afe_priv->afe_ctrl_lock);
@@ -3143,30 +3112,22 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt8195_afe_irq_handler,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
- if (ret) {
- dev_err(dev, "could not request_irq for asys-isr\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n");
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
ret = dai_register_cbs[i](afe);
- if (ret) {
- dev_warn(dev, "dai register i %d fail, ret %d\n",
- i, ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "dai cb%i register fail\n", i);
}
/* init dai_driver and component_driver */
ret = mtk_afe_combine_sub_dai(afe);
- if (ret) {
- dev_warn(dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
- ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
afe->mtk_afe_hardware = &mt8195_afe_hardware;
afe->memif_fs = mt8195_memif_fs;
@@ -3177,18 +3138,21 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, afe);
- mt8195_afe_parse_of(afe, pdev->dev.of_node);
-
- pm_runtime_enable(dev);
- if (!pm_runtime_enabled(dev)) {
- ret = mt8195_afe_runtime_resume(dev);
- if (ret)
- return ret;
- }
+ afe_priv->topckgen = syscon_regmap_lookup_by_phandle(dev->of_node, "mediatek,topckgen");
+ if (IS_ERR(afe_priv->topckgen))
+ dev_dbg(afe->dev, "Cannot find topckgen controller: %ld\n",
+ PTR_ERR(afe_priv->topckgen));
/* enable clock for regcache get default value from hw */
afe_priv->pm_runtime_bypass_reg_ctl = true;
- pm_runtime_get_sync(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to resume device\n");
afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
&mt8195_afe_regmap_config);
@@ -3236,9 +3200,15 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
goto err_pm_put;
}
- mt8195_afe_init_registers(afe);
+ ret = regmap_multi_reg_write(afe->regmap, mt8195_afe_reg_defaults,
+ ARRAY_SIZE(mt8195_afe_reg_defaults));
+ if (ret)
+ goto err_pm_put;
+
+ ret = pm_runtime_put_sync(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to suspend device\n");
- pm_runtime_put_sync(dev);
afe_priv->pm_runtime_bypass_reg_ctl = false;
regcache_cache_only(afe->regmap, true);
@@ -3248,7 +3218,6 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
err_pm_put:
pm_runtime_put_sync(dev);
- pm_runtime_disable(dev);
return ret;
}
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
index a25c397c66c5..f10c0c17863e 100644
--- a/sound/soc/meson/axg-card.c
+++ b/sound/soc/meson/axg-card.c
@@ -120,20 +120,18 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
if (!lb->name)
return -ENOMEM;
- dlc = devm_kzalloc(card->dev, 2 * sizeof(*dlc), GFP_KERNEL);
+ dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL);
if (!dlc)
return -ENOMEM;
- lb->cpus = &dlc[0];
- lb->codecs = &dlc[1];
+ lb->cpus = dlc;
+ lb->codecs = &asoc_dummy_dlc;
lb->num_cpus = 1;
lb->num_codecs = 1;
lb->stream_name = lb->name;
lb->cpus->of_node = pad->cpus->of_node;
lb->cpus->dai_name = "TDM Loopback";
- lb->codecs->name = "snd-soc-dummy";
- lb->codecs->dai_name = "snd-soc-dummy-dai";
lb->dpcm_capture = 1;
lb->no_pcm = 1;
lb->ops = &axg_card_tdm_be_ops;
@@ -321,8 +319,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->cpus = cpu;
dai_link->num_cpus = 1;
- ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node,
- &dai_link->cpus->dai_name);
+ ret = meson_card_parse_dai(card, np, dai_link->cpus);
if (ret)
return ret;
diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c
index 58c411d3c489..a26b620fc177 100644
--- a/sound/soc/meson/gx-card.c
+++ b/sound/soc/meson/gx-card.c
@@ -90,8 +90,7 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->cpus = cpu;
dai_link->num_cpus = 1;
- ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node,
- &dai_link->cpus->dai_name);
+ ret = meson_card_parse_dai(card, np, dai_link->cpus);
if (ret)
return ret;
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index 2d8d5717fd8b..f7fd9c013e19 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -74,23 +74,18 @@ EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
int meson_card_parse_dai(struct snd_soc_card *card,
struct device_node *node,
- struct device_node **dai_of_node,
- const char **dai_name)
+ struct snd_soc_dai_link_component *dlc)
{
- struct of_phandle_args args;
int ret;
- if (!dai_name || !dai_of_node || !node)
+ if (!dlc || !node)
return -EINVAL;
- ret = of_parse_phandle_with_args(node, "sound-dai",
- "#sound-dai-cells", 0, &args);
+ ret = snd_soc_of_get_dlc(node, NULL, dlc, 0);
if (ret)
return dev_err_probe(card->dev, ret, "can't parse dai\n");
- *dai_of_node = args.np;
-
- return snd_soc_get_dai_name(&args, dai_name);
+ return ret;
}
EXPORT_SYMBOL_GPL(meson_card_parse_dai);
@@ -160,8 +155,7 @@ int meson_card_set_be_link(struct snd_soc_card *card,
link->num_codecs = num_codecs;
for_each_child_of_node(node, np) {
- ret = meson_card_parse_dai(card, np, &codec->of_node,
- &codec->dai_name);
+ ret = meson_card_parse_dai(card, np, codec);
if (ret) {
of_node_put(np);
return ret;
@@ -183,21 +177,13 @@ int meson_card_set_fe_link(struct snd_soc_card *card,
struct device_node *node,
bool is_playback)
{
- struct snd_soc_dai_link_component *codec;
-
- codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL);
- if (!codec)
- return -ENOMEM;
-
- link->codecs = codec;
+ link->codecs = &asoc_dummy_dlc;
link->num_codecs = 1;
link->dynamic = 1;
link->dpcm_merged_format = 1;
link->dpcm_merged_chan = 1;
link->dpcm_merged_rate = 1;
- link->codecs->dai_name = "snd-soc-dummy-dai";
- link->codecs->name = "snd-soc-dummy";
if (is_playback)
link->dpcm_playback = 1;
diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h
index 74314071c80d..a5374324a189 100644
--- a/sound/soc/meson/meson-card.h
+++ b/sound/soc/meson/meson-card.h
@@ -39,8 +39,7 @@ int meson_card_reallocate_links(struct snd_soc_card *card,
unsigned int num_links);
int meson_card_parse_dai(struct snd_soc_card *card,
struct device_node *node,
- struct device_node **dai_of_node,
- const char **dai_name);
+ struct snd_soc_dai_link_component *dlc);
int meson_card_set_be_link(struct snd_soc_card *card,
struct snd_soc_dai_link *link,
struct device_node *node);
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index c1f24af17506..e2d8c41945fa 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -8,6 +8,11 @@
#include "qdsp6/q6afe.h"
#include "common.h"
+static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
int qcom_snd_parse_of(struct snd_soc_card *card)
{
struct device_node *np;
@@ -96,22 +101,15 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
goto err;
}
- ret = of_parse_phandle_with_args(cpu, "sound-dai",
- "#sound-dai-cells", 0, &args);
- if (ret) {
- dev_err(card->dev, "%s: error getting cpu phandle\n", link->name);
- goto err;
- }
- link->cpus->of_node = args.np;
- link->id = args.args[0];
-
- ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
+ ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0);
if (ret) {
dev_err_probe(card->dev, ret,
"%s: error getting cpu dai name\n", link->name);
goto err;
}
+ link->id = args.args[0];
+
if (platform) {
link->platforms->of_node = of_parse_phandle(platform,
"sound-dai",
@@ -140,17 +138,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
}
} else {
/* DPCM frontend */
- dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
- if (!dlc) {
- ret = -ENOMEM;
- goto err;
- }
-
- link->codecs = dlc;
+ link->codecs = &asoc_dummy_dlc;
link->num_codecs = 1;
-
- link->codecs->dai_name = "snd-soc-dummy-dai";
- link->codecs->name = "snd-soc-dummy";
link->dynamic = 1;
}
@@ -169,6 +158,11 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
of_node_put(platform);
}
+ if (!card->dapm_widgets) {
+ card->dapm_widgets = qcom_jack_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(qcom_jack_snd_widgets);
+ }
+
return 0;
err:
of_node_put(cpu);
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index 41db6617e2ed..56db852f4eab 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm.h>
#include <dt-bindings/sound/sc7180-lpass.h>
#include <sound/pcm.h>
#include <sound/soc.h>
diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c
index d43f480cbae3..bcf18fe8e14d 100644
--- a/sound/soc/qcom/lpass-sc7280.c
+++ b/sound/soc/qcom/lpass-sc7280.c
@@ -8,7 +8,7 @@
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/soc.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm.h>
#include <dt-bindings/sound/sc7180-lpass.h>
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index 1e0c918eb576..5974c7929dd3 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -196,6 +196,12 @@ struct apm_codec_dma_module_intf_cfg {
#define APM_CDMA_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_codec_dma_module_intf_cfg), 8)
+struct apm_display_port_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_display_port_intf_cfg cfg;
+} __packed;
+#define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8)
+
static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr)
{
@@ -582,6 +588,72 @@ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pk
}
EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync);
+static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ struct apm_display_port_module_intf_cfg *intf_cfg;
+ struct apm_module_frame_size_factor_cfg *fs_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz, ep_sz, fs_sz, dl_sz;
+ int rc, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ ic_sz = APM_DP_INTF_CFG_PSIZE;
+ ep_sz = APM_HW_EP_CFG_PSIZE;
+ fs_sz = APM_FS_CFG_PSIZE;
+ dl_sz = 0;
+
+ payload_size = ic_sz + ep_sz + fs_sz + dl_sz;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+ p += ep_sz;
+
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+ p += fs_sz;
+
+ intf_cfg = p;
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_DISPLAY_PORT_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.channel_allocation = cfg->channel_allocation;
+ intf_cfg->cfg.mst_idx = 0;
+ intf_cfg->cfg.dptx_idx = cfg->dp_idx;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
/* LPASS Codec DMA port Module Media Format Setup */
static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
@@ -660,33 +732,32 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
return rc;
}
-static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
- struct audioreach_module *module, bool enable)
+int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ uint32_t param_id, uint32_t param_val)
{
struct apm_module_param_data *param_data;
- struct param_id_sal_limiter_enable *limiter_enable;
- int payload_size;
struct gpr_pkt *pkt;
- int rc;
+ uint32_t *param;
+ int rc, payload_size;
void *p;
- payload_size = sizeof(*limiter_enable) + APM_MODULE_PARAM_DATA_SIZE;
+ payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE;
+ p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(pkt))
- return PTR_ERR(pkt);
-
- p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = p;
param_data->module_instance_id = module->instance_id;
param_data->error_code = 0;
- param_data->param_id = PARAM_ID_SAL_LIMITER_ENABLE;
- param_data->param_size = sizeof(*limiter_enable);
- p = p + APM_MODULE_PARAM_DATA_SIZE;
- limiter_enable = p;
+ param_data->param_id = param_id;
+ param_data->param_size = sizeof(uint32_t);
- limiter_enable->enable_lim = enable;
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ param = p;
+ *param = param_val;
rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
@@ -694,77 +765,34 @@ static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
return rc;
}
+EXPORT_SYMBOL_GPL(audioreach_send_u32_param);
+
+static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
+ struct audioreach_module *module, bool enable)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable);
+}
static int audioreach_sal_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
struct audioreach_module_config *cfg)
{
- struct apm_module_param_data *param_data;
- struct param_id_sal_output_config *media_format;
- int payload_size;
- struct gpr_pkt *pkt;
- int rc;
- void *p;
-
- payload_size = sizeof(*media_format) + APM_MODULE_PARAM_DATA_SIZE;
-
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(pkt))
- return PTR_ERR(pkt);
-
- p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
-
- param_data = p;
- param_data->module_instance_id = module->instance_id;
- param_data->error_code = 0;
- param_data->param_id = PARAM_ID_SAL_OUTPUT_CFG;
- param_data->param_size = sizeof(*media_format);
- p = p + APM_MODULE_PARAM_DATA_SIZE;
- media_format = p;
-
- media_format->bits_per_sample = cfg->bit_width;
-
- rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
-
- kfree(pkt);
-
- return rc;
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width);
}
static int audioreach_module_enable(struct q6apm_graph *graph,
struct audioreach_module *module,
bool enable)
{
- struct apm_module_param_data *param_data;
- struct param_id_module_enable *param;
- int payload_size;
- struct gpr_pkt *pkt;
- int rc;
- void *p;
-
- payload_size = sizeof(*param) + APM_MODULE_PARAM_DATA_SIZE;
-
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(pkt))
- return PTR_ERR(pkt);
-
- p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
-
- param_data = p;
- param_data->module_instance_id = module->instance_id;
- param_data->error_code = 0;
- param_data->param_id = PARAM_ID_MODULE_ENABLE;
- param_data->param_size = sizeof(*param);
- p = p + APM_MODULE_PARAM_DATA_SIZE;
- param = p;
-
- param->enable = enable;
-
- rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
-
- kfree(pkt);
+ return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable);
+}
- return rc;
+static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY,
+ EARLY_EOS_DELAY_MS);
}
static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
@@ -814,6 +842,99 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
return rc;
}
+static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
+ void *p, struct audioreach_module_config *mcfg)
+{
+ struct payload_media_fmt_aac_t *aac_cfg;
+ struct payload_media_fmt_pcm *mp3_cfg;
+ struct payload_media_fmt_flac_t *flac_cfg;
+
+ switch (mcfg->fmt) {
+ case SND_AUDIOCODEC_MP3:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_MP3;
+ media_fmt_hdr->payload_size = 0;
+ p = p + sizeof(*media_fmt_hdr);
+ mp3_cfg = p;
+ mp3_cfg->sample_rate = mcfg->sample_rate;
+ mp3_cfg->bit_width = mcfg->bit_width;
+ mp3_cfg->alignment = PCM_LSB_ALIGNED;
+ mp3_cfg->bits_per_sample = mcfg->bit_width;
+ mp3_cfg->q_factor = mcfg->bit_width - 1;
+ mp3_cfg->endianness = PCM_LITTLE_ENDIAN;
+ mp3_cfg->num_channels = mcfg->num_channels;
+
+ if (mcfg->num_channels == 1) {
+ mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ } else if (mcfg->num_channels == 2) {
+ mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ mp3_cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ }
+ break;
+ case SND_AUDIOCODEC_AAC:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_AAC;
+ media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_aac_t);
+ p = p + sizeof(*media_fmt_hdr);
+ aac_cfg = p;
+ aac_cfg->aac_fmt_flag = 0;
+ aac_cfg->audio_obj_type = 5;
+ aac_cfg->num_channels = mcfg->num_channels;
+ aac_cfg->total_size_of_PCE_bits = 0;
+ aac_cfg->sample_rate = mcfg->sample_rate;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
+ media_fmt_hdr->fmt_id = MEDIA_FMT_ID_FLAC;
+ media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_flac_t);
+ p = p + sizeof(*media_fmt_hdr);
+ flac_cfg = p;
+ flac_cfg->sample_size = mcfg->codec.options.flac_d.sample_size;
+ flac_cfg->num_channels = mcfg->num_channels;
+ flac_cfg->min_blk_size = mcfg->codec.options.flac_d.min_blk_size;
+ flac_cfg->max_blk_size = mcfg->codec.options.flac_d.max_blk_size;
+ flac_cfg->sample_rate = mcfg->sample_rate;
+ flac_cfg->min_frame_size = mcfg->codec.options.flac_d.min_frame_size;
+ flac_cfg->max_frame_size = mcfg->codec.options.flac_d.max_frame_size;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg)
+{
+ struct media_format *header;
+ struct gpr_pkt *pkt;
+ int iid, payload_size, rc;
+ void *p;
+
+ payload_size = sizeof(struct apm_sh_module_media_fmt_cmd);
+
+ iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ pkt = audioreach_alloc_cmd_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT,
+ 0, graph->port->id, iid);
+
+ if (IS_ERR(pkt))
+ return -ENOMEM;
+
+ p = (void *)pkt + GPR_HDR_SIZE;
+ header = p;
+ rc = audioreach_set_compr_media_format(header, p, mcfg);
+ if (rc) {
+ kfree(pkt);
+ return rc;
+ }
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ kfree(pkt);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
+
static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
struct audioreach_module_config *cfg)
@@ -1017,25 +1138,33 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
p = p + APM_MODULE_PARAM_DATA_SIZE;
header = p;
- header->data_format = DATA_FORMAT_FIXED_POINT;
- header->fmt_id = MEDIA_FMT_ID_PCM;
- header->payload_size = payload_size - sizeof(*header);
-
- p = p + sizeof(*header);
- cfg = p;
- cfg->sample_rate = mcfg->sample_rate;
- cfg->bit_width = mcfg->bit_width;
- cfg->alignment = PCM_LSB_ALIGNED;
- cfg->bits_per_sample = mcfg->bit_width;
- cfg->q_factor = mcfg->bit_width - 1;
- cfg->endianness = PCM_LITTLE_ENDIAN;
- cfg->num_channels = mcfg->num_channels;
-
- if (mcfg->num_channels == 1) {
- cfg->channel_mapping[0] = PCM_CHANNEL_L;
- } else if (num_channels == 2) {
- cfg->channel_mapping[0] = PCM_CHANNEL_L;
- cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ if (mcfg->fmt == SND_AUDIOCODEC_PCM) {
+ header->data_format = DATA_FORMAT_FIXED_POINT;
+ header->fmt_id = MEDIA_FMT_ID_PCM;
+ header->payload_size = payload_size - sizeof(*header);
+
+ p = p + sizeof(*header);
+ cfg = p;
+ cfg->sample_rate = mcfg->sample_rate;
+ cfg->bit_width = mcfg->bit_width;
+ cfg->alignment = PCM_LSB_ALIGNED;
+ cfg->bits_per_sample = mcfg->bit_width;
+ cfg->q_factor = mcfg->bit_width - 1;
+ cfg->endianness = PCM_LITTLE_ENDIAN;
+ cfg->num_channels = mcfg->num_channels;
+
+ if (mcfg->num_channels == 1)
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ else if (num_channels == 2) {
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->channel_mapping[1] = PCM_CHANNEL_R;
+ }
+ } else {
+ rc = audioreach_set_compr_media_format(header, p, mcfg);
+ if (rc) {
+ kfree(pkt);
+ return rc;
+ }
}
rc = audioreach_graph_send_cmd_sync(graph, pkt, 0);
@@ -1120,8 +1249,13 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_PCM_DEC:
case MODULE_ID_PCM_ENC:
case MODULE_ID_PCM_CNV:
+ case MODULE_ID_PLACEHOLDER_DECODER:
+ case MODULE_ID_PLACEHOLDER_ENCODER:
rc = audioreach_pcm_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_DISPLAY_PORT_SINK:
+ rc = audioreach_display_port_set_media_format(graph, module, cfg);
+ break;
case MODULE_ID_I2S_SOURCE:
case MODULE_ID_I2S_SINK:
rc = audioreach_i2s_set_media_format(graph, module, cfg);
@@ -1144,6 +1278,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_MFC:
rc = audioreach_mfc_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_GAPLESS:
+ rc = audioreach_gapless_set_media_format(graph, module, cfg);
+ break;
default:
rc = 0;
}
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 1d1d47d47d40..e38111ffd7b9 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -15,6 +15,8 @@ struct q6apm_graph;
#define MODULE_ID_PCM_CNV 0x07001003
#define MODULE_ID_PCM_ENC 0x07001004
#define MODULE_ID_PCM_DEC 0x07001005
+#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008
+#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
#define MODULE_ID_SAL 0x07001010
#define MODULE_ID_MFC 0x07001015
#define MODULE_ID_CODEC_DMA_SINK 0x07001023
@@ -22,6 +24,11 @@ struct q6apm_graph;
#define MODULE_ID_I2S_SINK 0x0700100A
#define MODULE_ID_I2S_SOURCE 0x0700100B
#define MODULE_ID_DATA_LOGGING 0x0700101A
+#define MODULE_ID_AAC_DEC 0x0700101F
+#define MODULE_ID_FLAC_DEC 0x0700102F
+#define MODULE_ID_MP3_DECODE 0x0700103B
+#define MODULE_ID_GAPLESS 0x0700104D
+#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069
#define APM_CMD_GET_SPF_STATE 0x01001021
#define APM_CMD_RSP_GET_SPF_STATE 0x02001007
@@ -142,12 +149,15 @@ struct param_id_enc_bitrate_param {
} __packed;
#define DATA_FORMAT_FIXED_POINT 1
+#define DATA_FORMAT_GENERIC_COMPRESSED 5
+#define DATA_FORMAT_RAW_COMPRESSED 6
#define PCM_LSB_ALIGNED 1
#define PCM_MSB_ALIGNED 2
#define PCM_LITTLE_ENDIAN 1
#define PCM_BIT_ENDIAN 2
#define MEDIA_FMT_ID_PCM 0x09001000
+#define MEDIA_FMT_ID_MP3 0x09001009
#define PCM_CHANNEL_L 1
#define PCM_CHANNEL_R 2
#define SAMPLE_RATE_48K 48000
@@ -225,6 +235,28 @@ struct apm_media_format {
uint32_t payload_size;
} __packed;
+#define MEDIA_FMT_ID_FLAC 0x09001004
+
+struct payload_media_fmt_flac_t {
+ uint16_t num_channels;
+ uint16_t sample_size;
+ uint16_t min_blk_size;
+ uint16_t max_blk_size;
+ uint32_t sample_rate;
+ uint32_t min_frame_size;
+ uint32_t max_frame_size;
+} __packed;
+
+#define MEDIA_FMT_ID_AAC 0x09001001
+
+struct payload_media_fmt_aac_t {
+ uint16_t aac_fmt_flag;
+ uint16_t audio_obj_type;
+ uint16_t num_channels;
+ uint16_t total_size_of_PCE_bits;
+ uint32_t sample_rate;
+} __packed;
+
#define DATA_CMD_WR_SH_MEM_EP_EOS 0x04001002
#define WR_SH_MEM_EP_EOS_POLICY_LAST 1
#define WR_SH_MEM_EP_EOS_POLICY_EACH 2
@@ -444,6 +476,15 @@ struct param_id_i2s_intf_cfg {
#define PORT_ID_I2S_OUPUT 1
#define I2S_STACK_SIZE 2048
+#define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154
+
+struct param_id_display_port_intf_cfg {
+ uint32_t channel_allocation;
+ /* Multi-Steam Transport index */
+ uint32_t mst_idx;
+ uint32_t dptx_idx;
+} __packed;
+
#define PARAM_ID_HW_EP_MF_CFG 0x08001017
struct param_id_hw_ep_mf {
uint32_t sample_rate;
@@ -512,6 +553,8 @@ struct param_id_sal_limiter_enable {
} __packed;
#define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024
+#define PARAM_ID_EARLY_EOS_DELAY 0x0800114C
+#define EARLY_EOS_DELAY_MS 150
struct param_id_mfc_media_format {
uint32_t sample_rate;
@@ -520,6 +563,10 @@ struct param_id_mfc_media_format {
uint16_t channel_mapping[];
} __packed;
+struct param_id_gapless_early_eos_delay_t {
+ uint32_t early_eos_delay_ms;
+} __packed;
+
struct media_format {
uint32_t data_format;
uint32_t fmt_id;
@@ -598,6 +645,15 @@ struct param_id_vol_ctrl_master_gain {
} __packed;
+#define PARAM_ID_REMOVE_INITIAL_SILENCE 0x0800114B
+#define PARAM_ID_REMOVE_TRAILING_SILENCE 0x0800115D
+
+#define PARAM_ID_REAL_MODULE_ID 0x0800100B
+
+struct param_id_placeholder_real_module_id {
+ uint32_t real_module_id;
+} __packed;
+
/* Graph */
struct audioreach_connection {
/* Connections */
@@ -702,8 +758,11 @@ struct audioreach_module_config {
u16 data_format;
u16 num_channels;
u16 active_channels_mask;
+ u16 dp_idx;
+ u32 channel_allocation;
u32 sd_line_mask;
int fmt;
+ struct snd_codec codec;
u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
};
@@ -740,4 +799,8 @@ int audioreach_set_media_format(struct q6apm_graph *graph,
int audioreach_shared_memory_send_eos(struct q6apm_graph *graph);
int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
struct audioreach_module *module, int vol);
+int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ uint32_t param_id, uint32_t param_val);
+int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg);
+
#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index 8bb7452b8f18..31e0bad71e95 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -12,6 +12,7 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "q6dsp-lpass-ports.h"
+#include "q6dsp-common.h"
#include "q6afe.h"
@@ -69,6 +70,7 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
int channels = params_channels(params);
struct q6afe_hdmi_cfg *hdmi = &dai_data->port_config[dai->id].hdmi;
+ int ret;
hdmi->sample_rate = params_rate(params);
switch (params_format(params)) {
@@ -80,33 +82,11 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
break;
}
- /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */
- switch (channels) {
- case 2:
- hdmi->channel_allocation = 0;
- break;
- case 3:
- hdmi->channel_allocation = 0x02;
- break;
- case 4:
- hdmi->channel_allocation = 0x06;
- break;
- case 5:
- hdmi->channel_allocation = 0x0A;
- break;
- case 6:
- hdmi->channel_allocation = 0x0B;
- break;
- case 7:
- hdmi->channel_allocation = 0x12;
- break;
- case 8:
- hdmi->channel_allocation = 0x13;
- break;
- default:
- dev_err(dai->dev, "invalid Channels = %u\n", channels);
- return -EINVAL;
- }
+ ret = q6dsp_get_channel_allocation(channels);
+ if (ret < 0)
+ return ret;
+
+ hdmi->channel_allocation = (u16) ret;
return 0;
}
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
index 7f02f5b2c33f..5eb0b864c740 100644
--- a/sound/soc/qcom/qdsp6/q6apm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -28,8 +28,27 @@
#define CAPTURE_MIN_PERIOD_SIZE 320
#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
+#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
+#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
+#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
+#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
#define SID_MASK_DEFAULT 0xF
+static const struct snd_compr_codec_caps q6apm_compr_caps = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000 },
+ .descriptor[0].num_sample_rates = 13,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 128,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
enum stream_state {
Q6APM_STREAM_IDLE = 0,
Q6APM_STREAM_STOPPED,
@@ -39,6 +58,7 @@ enum stream_state {
struct q6apm_dai_rtd {
struct snd_pcm_substream *substream;
struct snd_compr_stream *cstream;
+ struct snd_codec codec;
struct snd_compr_params codec_param;
struct snd_dma_buffer dma_buffer;
phys_addr_t phys;
@@ -52,9 +72,13 @@ struct q6apm_dai_rtd {
uint16_t bits_per_sample;
uint16_t source; /* Encoding source bit mask */
uint16_t session_id;
+ bool next_track;
enum stream_state state;
struct q6apm_graph *graph;
spinlock_t lock;
+ uint32_t initial_samples_drop;
+ uint32_t trailing_samples_drop;
+ bool notify_on_drain;
};
struct q6apm_dai_data {
@@ -132,6 +156,69 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo
}
}
+static void event_handler_compr(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6apm_dai_rtd *prtd = priv;
+ struct snd_compr_stream *substream = prtd->cstream;
+ unsigned long flags;
+ uint32_t wflags = 0;
+ uint64_t avail;
+ uint32_t bytes_written, bytes_to_write;
+ bool is_last_buffer = false;
+
+ switch (opcode) {
+ case APM_CLIENT_EVENT_CMD_EOS_DONE:
+ spin_lock_irqsave(&prtd->lock, flags);
+ if (prtd->notify_on_drain) {
+ snd_compr_drain_notify(prtd->cstream);
+ prtd->notify_on_drain = false;
+ } else {
+ prtd->state = Q6APM_STREAM_STOPPED;
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ case APM_CLIENT_EVENT_DATA_WRITE_DONE:
+ spin_lock_irqsave(&prtd->lock, flags);
+ bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT;
+ prtd->copied_total += bytes_written;
+ snd_compr_fragment_elapsed(substream);
+
+ if (prtd->state != Q6APM_STREAM_RUNNING) {
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail > prtd->pcm_count) {
+ bytes_to_write = prtd->pcm_count;
+ } else {
+ if (substream->partial_drain || prtd->notify_on_drain)
+ is_last_buffer = true;
+ bytes_to_write = avail;
+ }
+
+ if (bytes_to_write) {
+ if (substream->partial_drain && is_last_buffer)
+ wflags |= APM_LAST_BUFFER_FLAG;
+
+ q6apm_write_async(prtd->graph,
+ bytes_to_write, 0, 0, wflags);
+
+ prtd->bytes_sent += bytes_to_write;
+
+ if (prtd->notify_on_drain && is_last_buffer)
+ audioreach_shared_memory_send_eos(prtd->graph);
+ }
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ default:
+ break;
+ }
+}
+
static int q6apm_dai_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -155,6 +242,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component,
cfg.sample_rate = runtime->rate;
cfg.num_channels = runtime->channels;
cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = SND_AUDIOCODEC_PCM;
if (prtd->state) {
/* clear the previous setup if any */
@@ -386,6 +474,362 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc
return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
}
+static int q6apm_dai_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd;
+ struct q6apm_dai_data *pdata;
+ struct device *dev = component->dev;
+ int ret, size;
+ int graph_id;
+
+ graph_id = cpu_dai->driver->id;
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ prtd->cstream = stream;
+ prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id);
+ if (IS_ERR(prtd->graph)) {
+ ret = PTR_ERR(prtd->graph);
+ kfree(prtd);
+ return ret;
+ }
+
+ runtime->private_data = prtd;
+ runtime->dma_bytes = BUFFER_BYTES_MAX;
+ size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer);
+ if (ret)
+ return ret;
+
+ if (pdata->sid < 0)
+ prtd->phys = prtd->dma_buffer.addr;
+ else
+ prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);
+
+ snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);
+ spin_lock_init(&prtd->lock);
+
+ q6apm_enable_compress_module(dev, prtd->graph, true);
+ return 0;
+}
+
+static int q6apm_dai_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+
+ q6apm_graph_stop(prtd->graph);
+ q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK);
+ q6apm_graph_close(prtd->graph);
+ snd_dma_free_pages(&prtd->dma_buffer);
+ prtd->graph = NULL;
+ kfree(prtd);
+ runtime->private_data = NULL;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps)
+{
+ caps->direction = SND_COMPRESS_PLAYBACK;
+ caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;
+ caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
+ caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
+ caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ caps->num_codecs = 3;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+ caps->codecs[2] = SND_AUDIOCODEC_FLAC;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_codec_caps *codec)
+{
+ switch (codec->codec) {
+ case SND_AUDIOCODEC_MP3:
+ *codec = q6apm_compr_caps;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int q6apm_dai_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ tstamp->copied_total = prtd->copied_total;
+ tstamp->byte_offset = prtd->copied_total % prtd->pcm_size;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return 0;
+}
+
+static int q6apm_dai_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, int cmd)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ case SND_COMPR_TRIGGER_NEXT_TRACK:
+ prtd->next_track = true;
+ break;
+ case SND_COMPR_TRIGGER_DRAIN:
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ prtd->notify_on_drain = true;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ prtd->bytes_received += count;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return count;
+}
+
+static int q6apm_dai_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct q6apm_dai_data *pdata;
+ struct audioreach_module_config cfg;
+ struct snd_codec *codec = &params->codec;
+ int dir = stream->direction;
+ int ret;
+
+ pdata = snd_soc_component_get_drvdata(component);
+ if (!pdata)
+ return -EINVAL;
+
+ prtd->periods = runtime->fragments;
+ prtd->pcm_count = runtime->fragment_size;
+ prtd->pcm_size = runtime->fragments * runtime->fragment_size;
+ prtd->bits_per_sample = 16;
+
+ prtd->pos = 0;
+
+ if (prtd->next_track != true) {
+ memcpy(&prtd->codec, codec, sizeof(*codec));
+
+ ret = q6apm_set_real_module_id(component->dev, prtd->graph, codec->id);
+ if (ret)
+ return ret;
+
+ cfg.direction = dir;
+ cfg.sample_rate = codec->sample_rate;
+ cfg.num_channels = 2;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = codec->id;
+ memcpy(&cfg.codec, codec, sizeof(*codec));
+
+ ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
+ if (ret < 0)
+ return ret;
+
+ ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
+ if (ret)
+ return ret;
+
+ ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK,
+ prtd->phys, (prtd->pcm_size / prtd->periods),
+ prtd->periods);
+ if (ret < 0)
+ return -ENOMEM;
+
+ ret = q6apm_graph_prepare(prtd->graph);
+ if (ret)
+ return ret;
+
+ ret = q6apm_graph_start(prtd->graph);
+ if (ret)
+ return ret;
+
+ } else {
+ cfg.direction = dir;
+ cfg.sample_rate = codec->sample_rate;
+ cfg.num_channels = 2;
+ cfg.bit_width = prtd->bits_per_sample;
+ cfg.fmt = codec->id;
+ memcpy(&cfg.codec, codec, sizeof(*codec));
+
+ ret = audioreach_compr_set_param(prtd->graph, &cfg);
+ if (ret < 0)
+ return ret;
+ }
+ prtd->state = Q6APM_STREAM_RUNNING;
+
+ return 0;
+}
+
+static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ int ret = 0;
+
+ switch (metadata->key) {
+ case SNDRV_COMPRESS_ENCODER_PADDING:
+ prtd->trailing_samples_drop = metadata->value[0];
+ q6apm_remove_trailing_silence(component->dev, prtd->graph,
+ prtd->trailing_samples_drop);
+ break;
+ case SNDRV_COMPRESS_ENCODER_DELAY:
+ prtd->initial_samples_drop = metadata->value[0];
+ q6apm_remove_initial_silence(component->dev, prtd->graph,
+ prtd->initial_samples_drop);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int q6apm_dai_compr_mmap(struct snd_soc_component *component,
+ struct snd_compr_stream *stream,
+ struct vm_area_struct *vma)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ struct device *dev = component->dev;
+
+ return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr,
+ prtd->dma_buffer.bytes);
+}
+
+static int q6apm_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *stream, char __user *buf,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct q6apm_dai_rtd *prtd = runtime->private_data;
+ void *dstn;
+ unsigned long flags;
+ size_t copy;
+ u32 wflags = 0;
+ u32 app_pointer;
+ u32 bytes_received;
+ uint32_t bytes_to_write;
+ int avail, bytes_in_flight = 0;
+
+ bytes_received = prtd->bytes_received;
+
+ /**
+ * Make sure that next track data pointer is aligned at 32 bit boundary
+ * This is a Mandatory requirement from DSP data buffers alignment
+ */
+ if (prtd->next_track)
+ bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);
+
+ app_pointer = bytes_received/prtd->pcm_size;
+ app_pointer = bytes_received - (app_pointer * prtd->pcm_size);
+ dstn = prtd->dma_buffer.area + app_pointer;
+
+ if (count < prtd->pcm_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = prtd->pcm_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy))
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ bytes_in_flight = prtd->bytes_received - prtd->copied_total;
+
+ if (prtd->next_track) {
+ prtd->next_track = false;
+ prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);
+ prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);
+ }
+
+ prtd->bytes_received = bytes_received + count;
+
+ /* Kick off the data to dsp if its starving!! */
+ if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) {
+ bytes_to_write = prtd->pcm_count;
+ avail = prtd->bytes_received - prtd->bytes_sent;
+
+ if (avail < prtd->pcm_count)
+ bytes_to_write = avail;
+
+ q6apm_write_async(prtd->graph, bytes_to_write, 0, 0, wflags);
+ prtd->bytes_sent += bytes_to_write;
+ }
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return count;
+}
+
+static const struct snd_compress_ops q6apm_dai_compress_ops = {
+ .open = q6apm_dai_compr_open,
+ .free = q6apm_dai_compr_free,
+ .get_caps = q6apm_dai_compr_get_caps,
+ .get_codec_caps = q6apm_dai_compr_get_codec_caps,
+ .pointer = q6apm_dai_compr_pointer,
+ .trigger = q6apm_dai_compr_trigger,
+ .ack = q6apm_dai_compr_ack,
+ .set_params = q6apm_dai_compr_set_params,
+ .set_metadata = q6apm_dai_compr_set_metadata,
+ .mmap = q6apm_dai_compr_mmap,
+ .copy = q6apm_compr_copy,
+};
+
static const struct snd_soc_component_driver q6apm_fe_dai_component = {
.name = DRV_NAME,
.open = q6apm_dai_open,
@@ -395,6 +839,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = {
.hw_params = q6apm_dai_hw_params,
.pointer = q6apm_dai_pointer,
.trigger = q6apm_dai_trigger,
+ .compress_ops = &q6apm_dai_compress_ops,
};
static int q6apm_dai_probe(struct platform_device *pdev)
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index 420e8aa11f42..7ad604b80e25 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -11,6 +11,7 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "q6dsp-lpass-ports.h"
+#include "q6dsp-common.h"
#include "audioreach.h"
#include "q6apm.h"
@@ -91,6 +92,36 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai,
return 0;
}
+static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
+ int channels = params_channels(params);
+ int ret;
+
+ cfg->bit_width = params_width(params);
+ cfg->sample_rate = params_rate(params);
+ cfg->num_channels = channels;
+
+ switch (dai->id) {
+ case DISPLAY_PORT_RX_0:
+ cfg->dp_idx = 0;
+ break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ cfg->dp_idx = dai->id - DISPLAY_PORT_RX_1 + 1;
+ break;
+ }
+
+ ret = q6dsp_get_channel_allocation(channels);
+ if (ret < 0)
+ return ret;
+
+ cfg->channel_allocation = ret;
+
+ return 0;
+}
+
static int q6dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@@ -215,6 +246,13 @@ static const struct snd_soc_dai_ops q6i2s_ops = {
.shutdown = q6apm_lpass_dai_shutdown,
.set_channel_map = q6dma_set_channel_map,
.hw_params = q6dma_hw_params,
+};
+
+static const struct snd_soc_dai_ops q6hdmi_ops = {
+ .prepare = q6apm_lpass_dai_prepare,
+ .startup = q6apm_lpass_dai_startup,
+ .shutdown = q6apm_lpass_dai_shutdown,
+ .hw_params = q6hdmi_hw_params,
.set_fmt = q6i2s_set_fmt,
};
@@ -242,6 +280,7 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
memset(&cfg, 0, sizeof(cfg));
cfg.q6i2s_ops = &q6i2s_ops;
cfg.q6dma_ops = &q6dma_ops;
+ cfg.q6hdmi_ops = &q6hdmi_ops;
dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais);
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index a7a3f973eb6d..7bfac9492ab5 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -298,6 +298,71 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
}
EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples);
+}
+EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence);
+
+int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples);
+}
+EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence);
+
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en)
+{
+ struct audioreach_module *module;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en);
+}
+EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);
+
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph,
+ uint32_t codec_id)
+{
+ struct audioreach_module *module;
+ uint32_t module_id;
+
+ module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
+ if (!module)
+ return -ENODEV;
+
+ switch (codec_id) {
+ case SND_AUDIOCODEC_MP3:
+ module_id = MODULE_ID_MP3_DECODE;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ module_id = MODULE_ID_AAC_DEC;
+ break;
+ case SND_AUDIOCODEC_FLAC:
+ module_id = MODULE_ID_FLAC_DEC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID,
+ module_id);
+}
+EXPORT_SYMBOL_GPL(q6apm_set_real_module_id);
+
int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
{
struct audioreach_graph_info *info = graph->info;
@@ -497,6 +562,9 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
}
break;
case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
+ client_event = APM_CLIENT_EVENT_CMD_EOS_DONE;
+ if (graph->cb)
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
break;
case GPR_BASIC_RSP_RESULT:
switch (result->opcode) {
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
index 7005be9b63e3..8ee40732ce9e 100644
--- a/sound/soc/qcom/qdsp6/q6apm.h
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -45,6 +45,8 @@
#define APM_WRITE_TOKEN_LEN_SHIFT 16
#define APM_MAX_SESSIONS 8
+#define APM_LAST_BUFFER_FLAG BIT(30)
+#define NO_TIMESTAMP 0xFF00
struct q6apm {
struct device *dev;
@@ -147,4 +149,8 @@ int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
bool q6apm_is_adsp_ready(void);
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en);
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
+int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id);
#endif /* __APM_GRAPH_ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.c b/sound/soc/qcom/qdsp6/q6dsp-common.c
index d393003492c7..95585dea2b36 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-common.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-common.c
@@ -63,4 +63,39 @@ int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch)
return 0;
}
EXPORT_SYMBOL_GPL(q6dsp_map_channels);
+
+int q6dsp_get_channel_allocation(int channels)
+{
+ int channel_allocation;
+
+ /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */
+ switch (channels) {
+ case 2:
+ channel_allocation = 0;
+ break;
+ case 3:
+ channel_allocation = 0x02;
+ break;
+ case 4:
+ channel_allocation = 0x06;
+ break;
+ case 5:
+ channel_allocation = 0x0A;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ break;
+ case 7:
+ channel_allocation = 0x12;
+ break;
+ case 8:
+ channel_allocation = 0x13;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return channel_allocation;
+}
+EXPORT_SYMBOL_GPL(q6dsp_get_channel_allocation);
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.h b/sound/soc/qcom/qdsp6/q6dsp-common.h
index 01094d108b8a..9e704db5f604 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-common.h
+++ b/sound/soc/qcom/qdsp6/q6dsp-common.h
@@ -20,5 +20,6 @@
#define PCM_CHANNELS 10 /* Top surround channel. */
int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch);
+int q6dsp_get_channel_allocation(int channels);
#endif /* __Q6DSP_COMMON_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
index f67c16fd90b9..ac937a6bf909 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
@@ -79,6 +79,22 @@
.id = did, \
}
+#define Q6AFE_DP_RX_DAI(did) { \
+ .playback = { \
+ .stream_name = #did" Playback", \
+ .rates = SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE, \
+ .channels_min = 2, \
+ .channels_max = 8, \
+ .rate_min = 48000, \
+ .rate_max = 192000, \
+ }, \
+ .name = #did, \
+ .id = did, \
+ }
static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
{
@@ -528,22 +544,14 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5),
Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6),
Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7),
- {
- .playback = {
- .stream_name = "Display Port Playback",
- .rates = SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .channels_min = 2,
- .channels_max = 8,
- .rate_max = 192000,
- .rate_min = 48000,
- },
- .id = DISPLAY_PORT_RX,
- .name = "DISPLAY_PORT",
- },
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_0),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_1),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_2),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_3),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_4),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_5),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_6),
+ Q6AFE_DP_RX_DAI(DISPLAY_PORT_RX_7),
Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0),
Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0),
Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1),
@@ -603,6 +611,9 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
case DISPLAY_PORT_RX:
q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
break;
+ case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6hdmi_ops;
+ break;
case SLIMBUS_0_RX ... SLIMBUS_6_TX:
q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops;
break;
diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c
index da7469a6a267..787dd49e03f6 100644
--- a/sound/soc/qcom/sc7280.c
+++ b/sound/soc/qcom/sc7280.c
@@ -14,6 +14,7 @@
#include <sound/soc.h>
#include <sound/rt5682s.h>
#include <linux/soundwire/sdw.h>
+#include <sound/pcm_params.h>
#include "../codecs/rt5682.h"
#include "../codecs/rt5682s.h"
@@ -196,8 +197,10 @@ static int sc7280_snd_hw_params(struct snd_pcm_substream *substream,
struct sdw_stream_runtime *sruntime;
int i;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
+ if (!rtd->dai_link->no_pcm) {
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
+ }
switch (cpu_dai->id) {
case LPASS_CDC_DMA_TX3:
@@ -358,6 +361,20 @@ static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = {
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
+static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
static int sc7280_snd_platform_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
@@ -387,6 +404,8 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev)
for_each_card_prelinks(card, i, link) {
link->init = sc7280_init;
link->ops = &sc7280_ops;
+ if (link->no_pcm == 1)
+ link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup;
}
return devm_snd_soc_register_card(dev, card);
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
index fd95a79cc9fa..a5442592bde4 100644
--- a/sound/soc/samsung/odroid.c
+++ b/sound/soc/samsung/odroid.c
@@ -205,7 +205,6 @@ static int odroid_audio_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_dai_link *link, *codec_link;
int num_pcms, ret, i;
- struct of_phandle_args args = {};
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -260,20 +259,7 @@ static int odroid_audio_probe(struct platform_device *pdev)
}
for (i = 0; i < num_pcms; i++, link += 2) {
- ret = of_parse_phandle_with_args(cpu, "sound-dai",
- "#sound-dai-cells", i, &args);
- if (ret < 0)
- break;
-
- if (!args.np) {
- dev_err(dev, "sound-dai property parse error: %d\n", ret);
- ret = -EINVAL;
- break;
- }
-
- ret = snd_soc_get_dai_name(&args, &link->cpus->dai_name);
- of_node_put(args.np);
-
+ ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name, i);
if (ret < 0)
break;
}
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
index 84e1b14e68e4..d0b5c543fd2f 100644
--- a/sound/soc/sh/siu_dai.c
+++ b/sound/soc/sh/siu_dai.c
@@ -796,3 +796,5 @@ module_platform_driver(siu_driver);
MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("siu_spb.bin");
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index ff25718ff2e8..4356cc320fea 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -236,6 +236,28 @@ int snd_soc_component_force_enable_pin_unlocked(
}
EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked);
+int snd_soc_component_notify_control(struct snd_soc_component *component,
+ const char * const ctl)
+{
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct snd_kcontrol *kctl;
+
+ if (component->name_prefix)
+ snprintf(name, ARRAY_SIZE(name), "%s %s", component->name_prefix, ctl);
+ else
+ snprintf(name, ARRAY_SIZE(name), "%s", ctl);
+
+ kctl = snd_soc_card_get_kcontrol(component->card, name);
+ if (!kctl)
+ return soc_component_ret(component, -EINVAL);
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_notify_control);
+
/**
* snd_soc_component_set_jack - configure component jack.
* @component: COMPONENTs
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index d8715db5e415..02fdb683f75f 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -20,7 +20,6 @@
#include <sound/initval.h>
#include <sound/soc-dpcm.h>
#include <sound/soc-link.h>
-#include <linux/pm_runtime.h>
static int snd_soc_compr_components_open(struct snd_compr_stream *cstream)
{
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index b48efc3a08d2..11bc5250ffd0 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3196,6 +3196,40 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np,
}
EXPORT_SYMBOL_GPL(snd_soc_daifmt_parse_clock_provider_raw);
+int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream)
+{
+ /*
+ * [Normal]
+ *
+ * Playback
+ * CPU : SNDRV_PCM_STREAM_PLAYBACK
+ * Codec: SNDRV_PCM_STREAM_PLAYBACK
+ *
+ * Capture
+ * CPU : SNDRV_PCM_STREAM_CAPTURE
+ * Codec: SNDRV_PCM_STREAM_CAPTURE
+ */
+ if (!dai_link->c2c_params)
+ return stream;
+
+ /*
+ * [Codec2Codec]
+ *
+ * Playback
+ * CPU : SNDRV_PCM_STREAM_CAPTURE
+ * Codec: SNDRV_PCM_STREAM_PLAYBACK
+ *
+ * Capture
+ * CPU : SNDRV_PCM_STREAM_PLAYBACK
+ * Codec: SNDRV_PCM_STREAM_CAPTURE
+ */
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return SNDRV_PCM_STREAM_PLAYBACK;
+
+ return SNDRV_PCM_STREAM_CAPTURE;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu);
+
int snd_soc_get_dai_id(struct device_node *ep)
{
struct snd_soc_component *component;
@@ -3223,12 +3257,13 @@ int snd_soc_get_dai_id(struct device_node *ep)
}
EXPORT_SYMBOL_GPL(snd_soc_get_dai_id);
-int snd_soc_get_dai_name(const struct of_phandle_args *args,
- const char **dai_name)
+int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_component *dlc)
{
struct snd_soc_component *pos;
int ret = -EPROBE_DEFER;
+ dlc->of_node = args->np;
+
mutex_lock(&client_mutex);
for_each_component(pos) {
struct device_node *component_of_node = soc_component_to_node(pos);
@@ -3236,7 +3271,7 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args,
if (component_of_node != args->np || !pos->num_dai)
continue;
- ret = snd_soc_component_of_xlate_dai_name(pos, args, dai_name);
+ ret = snd_soc_component_of_xlate_dai_name(pos, args, &dlc->dai_name);
if (ret == -ENOTSUPP) {
struct snd_soc_dai *dai;
int id = -1;
@@ -3267,9 +3302,9 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args,
id--;
}
- *dai_name = dai->driver->name;
- if (!*dai_name)
- *dai_name = pos->name;
+ dlc->dai_name = dai->driver->name;
+ if (!dlc->dai_name)
+ dlc->dai_name = pos->name;
} else if (ret) {
/*
* if another error than ENOTSUPP is returned go on and
@@ -3285,22 +3320,49 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args,
mutex_unlock(&client_mutex);
return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_get_dai_name);
+EXPORT_SYMBOL_GPL(snd_soc_get_dlc);
-int snd_soc_of_get_dai_name(struct device_node *of_node,
- const char **dai_name)
+int snd_soc_of_get_dlc(struct device_node *of_node,
+ struct of_phandle_args *args,
+ struct snd_soc_dai_link_component *dlc,
+ int index)
{
- struct of_phandle_args args;
+ struct of_phandle_args __args;
int ret;
+ if (!args)
+ args = &__args;
+
ret = of_parse_phandle_with_args(of_node, "sound-dai",
- "#sound-dai-cells", 0, &args);
+ "#sound-dai-cells", index, args);
if (ret)
return ret;
- ret = snd_soc_get_dai_name(&args, dai_name);
+ return snd_soc_get_dlc(args, dlc);
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dlc);
+
+int snd_soc_get_dai_name(const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ struct snd_soc_dai_link_component dlc;
+ int ret = snd_soc_get_dlc(args, &dlc);
+
+ if (ret == 0)
+ *dai_name = dlc.dai_name;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_name);
+
+int snd_soc_of_get_dai_name(struct device_node *of_node,
+ const char **dai_name, int index)
+{
+ struct snd_soc_dai_link_component dlc;
+ int ret = snd_soc_of_get_dlc(of_node, NULL, &dlc, index);
- of_node_put(args.np);
+ if (ret == 0)
+ *dai_name = dlc.dai_name;
return ret;
}
@@ -3341,26 +3403,6 @@ static int __snd_soc_of_get_dai_link_component_alloc(
return 0;
}
-static int __snd_soc_of_get_dai_link_component_parse(
- struct device_node *of_node,
- struct snd_soc_dai_link_component *component, int index)
-{
- struct of_phandle_args args;
- int ret;
-
- ret = of_parse_phandle_with_args(of_node, "sound-dai", "#sound-dai-cells",
- index, &args);
- if (ret)
- return ret;
-
- ret = snd_soc_get_dai_name(&args, &component->dai_name);
- if (ret < 0)
- return ret;
-
- component->of_node = args.np;
- return 0;
-}
-
/*
* snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array
* @dai_link: DAI link
@@ -3405,7 +3447,7 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev,
/* Parse the list */
for_each_link_codecs(dai_link, index, component) {
- ret = __snd_soc_of_get_dai_link_component_parse(of_node, component, index);
+ ret = snd_soc_of_get_dlc(of_node, NULL, component, index);
if (ret)
goto err;
}
@@ -3460,7 +3502,7 @@ int snd_soc_of_get_dai_link_cpus(struct device *dev,
/* Parse the list */
for_each_link_cpus(dai_link, index, component) {
- ret = __snd_soc_of_get_dai_link_component_parse(of_node, component, index);
+ ret = snd_soc_of_get_dlc(of_node, NULL, component, index);
if (ret)
goto err;
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index f2f04ce693a1..3091e8160bad 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2216,6 +2216,16 @@ static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
&dapm_widget_power_fops);
}
+static void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+
+ if (!dapm->debugfs_dapm || !w->name)
+ return;
+
+ debugfs_lookup_and_remove(w->name, dapm->debugfs_dapm);
+}
+
static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
debugfs_remove_recursive(dapm->debugfs_dapm);
@@ -2232,6 +2242,10 @@ static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
{
}
+static inline void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w)
+{
+}
+
static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
}
@@ -2495,6 +2509,8 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
dapm_free_path(p);
}
+ dapm_debugfs_free_widget(w);
+
kfree(w->kcontrols);
kfree_const(w->name);
kfree_const(w->sname);
@@ -4322,39 +4338,6 @@ static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,
snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);
}
-static int get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream)
-{
- /*
- * [Normal]
- *
- * Playback
- * CPU : SNDRV_PCM_STREAM_PLAYBACK
- * Codec: SNDRV_PCM_STREAM_PLAYBACK
- *
- * Playback
- * CPU : SNDRV_PCM_STREAM_CAPTURE
- * Codec: SNDRV_PCM_STREAM_CAPTURE
- */
- if (!dai_link->c2c_params)
- return stream;
-
- /*
- * [Codec2Codec]
- *
- * Playback
- * CPU : SNDRV_PCM_STREAM_CAPTURE
- * Codec: SNDRV_PCM_STREAM_PLAYBACK
- *
- * Capture
- * CPU : SNDRV_PCM_STREAM_PLAYBACK
- * Codec: SNDRV_PCM_STREAM_CAPTURE
- */
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
- return SNDRV_PCM_STREAM_PLAYBACK;
-
- return SNDRV_PCM_STREAM_CAPTURE;
-}
-
static void dapm_connect_dai_pair(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *codec_dai,
@@ -4372,7 +4355,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
for_each_pcm_streams(stream) {
int stream_cpu, stream_codec;
- stream_cpu = get_stream_cpu(dai_link, stream);
+ stream_cpu = snd_soc_get_stream_cpu(dai_link, stream);
stream_codec = stream;
/* connect BE DAI playback if widgets are valid */
@@ -4461,9 +4444,31 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
for_each_rtd_codec_dais(rtd, i, codec_dai)
dapm_connect_dai_pair(card, rtd, codec_dai,
asoc_rtd_to_cpu(rtd, i));
+ } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
+ int cpu_id;
+
+ if (!rtd->dai_link->codec_ch_maps) {
+ dev_err(card->dev, "%s: no codec channel mapping table provided\n",
+ __func__);
+ continue;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
+ if (cpu_id >= rtd->dai_link->num_cpus) {
+ dev_err(card->dev,
+ "%s: dai_link %s cpu_id %d too large, num_cpus is %d\n",
+ __func__, rtd->dai_link->name, cpu_id,
+ rtd->dai_link->num_cpus);
+ continue;
+ }
+ dapm_connect_dai_pair(card, rtd, codec_dai,
+ asoc_rtd_to_cpu(rtd, cpu_id));
+ }
} else {
dev_err(card->dev,
- "N cpus to M codecs link is not supported yet\n");
+ "%s: codec number %d < cpu number %d is not supported\n",
+ __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus);
}
}
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 4fb1ac8e1c4a..8896227e4fb7 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -14,7 +14,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/export.h>
@@ -1035,6 +1034,10 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
}
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ struct snd_pcm_hw_params cpu_params;
+ unsigned int ch_mask = 0;
+ int j;
+
/*
* Skip CPUs which don't support the current stream
* type. See soc_pcm_init_runtime_hw() for more details
@@ -1042,13 +1045,32 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
continue;
- ret = snd_soc_dai_hw_params(cpu_dai, substream, params);
+ /* copy params for each cpu */
+ cpu_params = *params;
+
+ if (!rtd->dai_link->codec_ch_maps)
+ goto hw_params;
+ /*
+ * construct cpu channel mask by combining ch_mask of each
+ * codec which maps to the cpu.
+ */
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == i)
+ ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask;
+ }
+
+ /* fixup cpu channel number */
+ if (ch_mask)
+ soc_pcm_codec_params_fixup(&cpu_params, ch_mask);
+
+hw_params:
+ ret = snd_soc_dai_hw_params(cpu_dai, substream, &cpu_params);
if (ret < 0)
goto out;
/* store the parameters for each DAI */
- soc_pcm_set_dai_params(cpu_dai, params);
- snd_soc_dapm_update_dai(substream, params, cpu_dai);
+ soc_pcm_set_dai_params(cpu_dai, &cpu_params);
+ snd_soc_dapm_update_dai(substream, &cpu_params, cpu_dai);
}
ret = snd_soc_pcm_component_hw_params(substream, params);
@@ -1072,49 +1094,67 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+#define TRIGGER_MAX 3
+static int (* const trigger[][TRIGGER_MAX])(struct snd_pcm_substream *substream, int cmd, int rollback) = {
+ [SND_SOC_TRIGGER_ORDER_DEFAULT] = {
+ snd_soc_link_trigger,
+ snd_soc_pcm_component_trigger,
+ snd_soc_pcm_dai_trigger,
+ },
+ [SND_SOC_TRIGGER_ORDER_LDC] = {
+ snd_soc_link_trigger,
+ snd_soc_pcm_dai_trigger,
+ snd_soc_pcm_component_trigger,
+ },
+};
+
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component;
- int ret = -EINVAL, _ret = 0, start_dma_last = 0, i;
+ int ret = 0, r = 0, i;
int rollback = 0;
+ int start = 0, stop = 0;
+ /*
+ * select START/STOP sequence
+ */
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->trigger_start)
+ start = component->driver->trigger_start;
+ if (component->driver->trigger_stop)
+ stop = component->driver->trigger_stop;
+ }
+ if (rtd->dai_link->trigger_start)
+ start = rtd->dai_link->trigger_start;
+ if (rtd->dai_link->trigger_stop)
+ stop = rtd->dai_link->trigger_stop;
+
+ if (start < 0 || start >= SND_SOC_TRIGGER_ORDER_MAX ||
+ stop < 0 || stop >= SND_SOC_TRIGGER_ORDER_MAX)
+ return -EINVAL;
+
+ /*
+ * START
+ */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- /* Do we need to start dma last? */
- for_each_rtd_components(rtd, i, component) {
- if (component->driver->start_dma_last) {
- start_dma_last = 1;
+ for (i = 0; i < TRIGGER_MAX; i++) {
+ r = trigger[start][i](substream, cmd, 0);
+ if (r < 0)
break;
- }
- }
-
- ret = snd_soc_link_trigger(substream, cmd, 0);
- if (ret < 0)
- goto start_err;
-
- if (start_dma_last) {
- ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
- if (ret < 0)
- goto start_err;
-
- ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
- } else {
- ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
- if (ret < 0)
- goto start_err;
-
- ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
}
-start_err:
- if (ret < 0)
- rollback = 1;
}
- if (rollback) {
- _ret = ret;
+ /*
+ * Rollback if START failed
+ * find correspond STOP command
+ */
+ if (r < 0) {
+ rollback = 1;
+ ret = r;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
cmd = SNDRV_PCM_TRIGGER_STOP;
@@ -1128,34 +1168,20 @@ start_err:
}
}
+ /*
+ * STOP
+ */
switch (cmd) {
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (rtd->dai_link->stop_dma_first) {
- ret = snd_soc_pcm_component_trigger(substream, cmd, rollback);
- if (ret < 0)
- break;
-
- ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback);
- if (ret < 0)
- break;
- } else {
- ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback);
- if (ret < 0)
- break;
-
- ret = snd_soc_pcm_component_trigger(substream, cmd, rollback);
- if (ret < 0)
- break;
+ for (i = TRIGGER_MAX; i > 0; i--) {
+ r = trigger[stop][i - 1](substream, cmd, rollback);
+ if (r < 0)
+ ret = r;
}
- ret = snd_soc_link_trigger(substream, cmd, rollback);
- break;
}
- if (_ret)
- ret = _ret;
-
return ret;
}
@@ -2734,48 +2760,50 @@ open_end:
static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
int *playback, int *capture)
{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *cpu_dai;
+ int has_playback = 0;
+ int has_capture = 0;
int i;
- if (rtd->dai_link->dynamic && rtd->dai_link->num_cpus > 1) {
- dev_err(rtd->dev,
- "DPCM doesn't support Multi CPU for Front-Ends yet\n");
+ if (dai_link->dynamic && dai_link->num_cpus > 1) {
+ dev_err(rtd->dev, "DPCM doesn't support Multi CPU for Front-Ends yet\n");
return -EINVAL;
}
- if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
+ if (dai_link->dynamic || dai_link->no_pcm) {
int stream;
- if (rtd->dai_link->dpcm_playback) {
+ if (dai_link->dpcm_playback) {
stream = SNDRV_PCM_STREAM_PLAYBACK;
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
if (snd_soc_dai_stream_valid(cpu_dai, stream)) {
- *playback = 1;
+ has_playback = 1;
break;
}
}
- if (!*playback) {
+ if (!has_playback) {
dev_err(rtd->card->dev,
"No CPU DAIs support playback for stream %s\n",
- rtd->dai_link->stream_name);
+ dai_link->stream_name);
return -EINVAL;
}
}
- if (rtd->dai_link->dpcm_capture) {
+ if (dai_link->dpcm_capture) {
stream = SNDRV_PCM_STREAM_CAPTURE;
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
if (snd_soc_dai_stream_valid(cpu_dai, stream)) {
- *capture = 1;
+ has_capture = 1;
break;
}
}
- if (!*capture) {
+ if (!has_capture) {
dev_err(rtd->card->dev,
"No CPU DAIs support capture for stream %s\n",
- rtd->dai_link->stream_name);
+ dai_link->stream_name);
return -EINVAL;
}
}
@@ -2783,41 +2811,58 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *codec_dai;
/* Adapt stream for codec2codec links */
- int cpu_capture = rtd->dai_link->c2c_params ?
- SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
- int cpu_playback = rtd->dai_link->c2c_params ?
- SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+ int cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE);
+ int cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK);
for_each_rtd_codec_dais(rtd, i, codec_dai) {
- if (rtd->dai_link->num_cpus == 1) {
+ if (dai_link->num_cpus == 1) {
cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- } else if (rtd->dai_link->num_cpus == rtd->dai_link->num_codecs) {
+ } else if (dai_link->num_cpus == dai_link->num_codecs) {
cpu_dai = asoc_rtd_to_cpu(rtd, i);
+ } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
+ int cpu_id;
+
+ if (!rtd->dai_link->codec_ch_maps) {
+ dev_err(rtd->card->dev, "%s: no codec channel mapping table provided\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
+ cpu_dai = asoc_rtd_to_cpu(rtd, cpu_id);
} else {
dev_err(rtd->card->dev,
- "N cpus to M codecs link is not supported yet\n");
+ "%s codec number %d < cpu number %d is not supported\n",
+ __func__, rtd->dai_link->num_codecs,
+ rtd->dai_link->num_cpus);
return -EINVAL;
}
if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
snd_soc_dai_stream_valid(cpu_dai, cpu_playback))
- *playback = 1;
+ has_playback = 1;
if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
snd_soc_dai_stream_valid(cpu_dai, cpu_capture))
- *capture = 1;
+ has_capture = 1;
}
}
- if (rtd->dai_link->playback_only) {
- *playback = 1;
- *capture = 0;
- }
+ if (dai_link->playback_only)
+ has_capture = 0;
+
+ if (dai_link->capture_only)
+ has_playback = 0;
+
+ if (!has_playback && !has_capture) {
+ dev_err(rtd->dev, "substream %s has no playback, no capture\n",
+ dai_link->stream_name);
- if (rtd->dai_link->capture_only) {
- *playback = 0;
- *capture = 1;
+ return -EINVAL;
}
+ *playback = has_playback;
+ *capture = has_capture;
+
return 0;
}
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index d0aca6b9058b..8add361e87c6 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -585,11 +585,15 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event);
static int soc_tplg_control_load(struct soc_tplg *tplg,
struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
{
+ int ret = 0;
+
if (tplg->ops && tplg->ops->control_load)
- return tplg->ops->control_load(tplg->comp, tplg->index, k,
- hdr);
+ ret = tplg->ops->control_load(tplg->comp, tplg->index, k, hdr);
- return 0;
+ if (ret)
+ dev_err(tplg->dev, "ASoC: failed to init %s\n", hdr->name);
+
+ return ret;
}
@@ -691,17 +695,13 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
/* pass control to driver for optional further init */
ret = soc_tplg_control_load(tplg, &kc, &be->hdr);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name);
+ if (ret < 0)
goto err;
- }
/* register control here */
ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to add %s\n", be->hdr.name);
+ if (ret < 0)
goto err;
- }
list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
@@ -776,17 +776,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
/* pass control to driver for optional further init */
ret = soc_tplg_control_load(tplg, &kc, &mc->hdr);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name);
+ if (ret < 0)
goto err;
- }
/* register control here */
ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to add %s\n", mc->hdr.name);
+ if (ret < 0)
goto err;
- }
list_add(&sm->dobj.list, &tplg->comp->dobj_list);
@@ -945,17 +941,13 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
/* pass control to driver for optional further init */
ret = soc_tplg_control_load(tplg, &kc, &ec->hdr);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name);
+ if (ret < 0)
goto err;
- }
/* register control here */
ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", ec->hdr.name);
+ if (ret < 0)
goto err;
- }
list_add(&se->dobj.list, &tplg->comp->dobj_list);
@@ -1162,11 +1154,8 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_
/* pass control to driver for optional further init */
err = soc_tplg_control_load(tplg, kc, &mc->hdr);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- mc->hdr.name);
+ if (err < 0)
return err;
- }
return 0;
}
@@ -1246,11 +1235,8 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k
/* pass control to driver for optional further init */
err = soc_tplg_control_load(tplg, kc, &ec->hdr);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- ec->hdr.name);
+ if (err < 0)
return err;
- }
return 0;
}
@@ -1298,11 +1284,8 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_
/* pass control to driver for optional further init */
err = soc_tplg_control_load(tplg, kc, &be->hdr);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- be->hdr.name);
+ if (err < 0)
return err;
- }
return 0;
}
@@ -1530,15 +1513,13 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
* If so, just return success.
*/
if (!snd_soc_card_is_instantiated(card)) {
- dev_warn(tplg->dev, "ASoC: Parent card not yet available,"
- " widget card binding deferred\n");
+ dev_warn(tplg->dev, "ASoC: Parent card not yet available, widget card binding deferred\n");
return 0;
}
ret = snd_soc_dapm_new_widgets(card);
if (ret < 0)
- dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n",
- ret);
+ dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", ret);
return ret;
}
@@ -1693,10 +1674,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
dlc = (struct snd_soc_dai_link_component *)(link + 1);
link->cpus = &dlc[0];
- link->codecs = &dlc[1];
-
link->num_cpus = 1;
- link->num_codecs = 1;
link->dobj.index = tplg->index;
link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
@@ -1721,16 +1699,19 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
}
}
- link->codecs->name = "snd-soc-dummy";
- link->codecs->dai_name = "snd-soc-dummy-dai";
-
/*
- * Many topology is assuming link has Platform.
- * This might be overwritten at soc_tplg_dai_link_load().
+ * Many topology are assuming link has Codec / Platform, and
+ * these might be overwritten at soc_tplg_dai_link_load().
+ * Don't use &asoc_dummy_dlc here.
*/
- link->platforms = &dlc[2];
- link->platforms->name = "snd-soc-dummy";
- link->num_platforms = 1;
+ link->codecs = &dlc[1]; /* Don't use &asoc_dummy_dlc here */
+ link->codecs->name = "snd-soc-dummy";
+ link->codecs->dai_name = "snd-soc-dummy-dai";
+ link->num_codecs = 1;
+
+ link->platforms = &dlc[2]; /* Don't use &asoc_dummy_dlc here */
+ link->platforms->name = "snd-soc-dummy";
+ link->num_platforms = 1;
/* enable DPCM */
link->dynamic = 1;
@@ -2049,11 +2030,11 @@ static struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
if (link->id != id)
continue;
- if (name && (!link->name || strcmp(name, link->name)))
+ if (name && (!link->name || !strstr(link->name, name)))
continue;
- if (stream_name && (!link->stream_name
- || strcmp(stream_name, link->stream_name)))
+ if (stream_name && (!link->stream_name ||
+ !strstr(link->stream_name, stream_name)))
continue;
return link;
@@ -2505,11 +2486,8 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
/* make sure header is valid before loading */
ret = soc_tplg_valid_header(tplg, hdr);
- if (ret < 0) {
- dev_err(tplg->dev,
- "ASoC: topology: invalid header: %d\n", ret);
+ if (ret < 0)
return ret;
- }
/* load the header object */
ret = soc_tplg_load_header(tplg, hdr);
@@ -2529,9 +2507,6 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
/* signal DAPM we are complete */
ret = soc_tplg_dapm_complete(tplg);
- if (ret < 0)
- dev_err(tplg->dev,
- "ASoC: failed to initialise DAPM from Firmware\n");
return ret;
}
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index a4dba0b751e7..11607c5f5d5a 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -224,6 +224,13 @@ int snd_soc_component_is_dummy(struct snd_soc_component *component)
(component->driver == &dummy_codec));
}
+struct snd_soc_dai_link_component asoc_dummy_dlc = {
+ .of_node = NULL,
+ .dai_name = "snd-soc-dummy-dai",
+ .name = "snd-soc-dummy",
+};
+EXPORT_SYMBOL_GPL(asoc_dummy_dlc);
+
static int snd_soc_dummy_probe(struct platform_device *pdev)
{
int ret;
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index a2725188f4ce..80361139a49a 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -236,6 +236,17 @@ config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR
Say Y if you want to enable the IPC message injector.
If unsure, select "N".
+config SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR
+ tristate "SOF enable IPC kernel injector"
+ depends on SND_SOC_SOF
+ select SND_SOC_SOF_CLIENT
+ help
+ This option enables the IPC kernel injector which can be used to send
+ crafted IPC messages to the kernel to test its robustness against
+ DSP messages.
+ Say Y if you want to enable the IPC kernel injector.
+ If unsure, select "N".
+
config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT
bool "SOF retain DSP context on any FW exceptions"
help
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 308d87639916..744d40bd8c8b 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -26,6 +26,7 @@ snd-sof-of-objs := sof-of-dev.o
snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o
snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o
+snd-sof-ipc-kernel-injector-objs := sof-client-ipc-kernel-injector.o
snd-sof-probes-objs := sof-client-probes.o
ifneq ($(CONFIG_SND_SOC_SOF_IPC3),)
snd-sof-probes-objs += sof-client-probes-ipc3.o
@@ -49,6 +50,7 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o
obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o
obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o
+obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) += snd-sof-ipc-kernel-injector.o
obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o
obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 749e856dc601..8a0fc635a997 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -130,6 +130,13 @@ static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else {
+ /*
+ * To support an IPC tx_message with a
+ * reply_size set to zero.
+ */
+ if (!msg->reply_size)
+ goto out;
+
/* reply correct size ? */
if (reply.hdr.size != msg->reply_size &&
!(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 1c535cc6c3a9..dc624f727aa3 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -55,6 +55,9 @@
#define ACP_DSP_TO_HOST_IRQ 0x04
+#define ACP_RN_PCI_ID 0x01
+#define ACP_RMB_PCI_ID 0x6F
+
#define HOST_BRIDGE_CZN 0x1630
#define HOST_BRIDGE_RMB 0x14B5
#define ACP_SHA_STAT 0x8000
diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c
index eaf70ea6e556..58b3092425f1 100644
--- a/sound/soc/sof/amd/pci-rmb.c
+++ b/sound/soc/sof/amd/pci-rmb.c
@@ -65,6 +65,9 @@ static int acp_pci_rmb_probe(struct pci_dev *pci, const struct pci_device_id *pc
{
unsigned int flag;
+ if (pci->revision != ACP_RMB_PCI_ID)
+ return -ENODEV;
+
flag = snd_amd_acp_find_config(pci);
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
return -ENODEV;
diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c
index 4809cb644152..7409e21ce5aa 100644
--- a/sound/soc/sof/amd/pci-rn.c
+++ b/sound/soc/sof/amd/pci-rn.c
@@ -65,6 +65,9 @@ static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci
{
unsigned int flag;
+ if (pci->revision != ACP_RN_PCI_ID)
+ return -ENODEV;
+
flag = snd_amd_acp_find_config(pci);
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
return -ENODEV;
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 9a9d82220fd0..30db685cc5f4 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -504,8 +504,10 @@ int snd_sof_device_shutdown(struct device *dev)
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
- if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
+ sof_fw_trace_free(sdev);
return snd_sof_shutdown(sdev);
+ }
return 0;
}
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index f4eeacf1f281..69c1a370d3b6 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -284,8 +284,6 @@ if SND_SOC_SOF_HDA_COMMON
config SND_SOC_SOF_HDA_LINK
bool "SOF support for HDA Links(HDA/HDMI)"
- depends on SND_SOC_SOF_NOCODEC_SUPPORT=n
- select SND_SOC_SOF_PROBE_WORK_QUEUE
help
This adds support for HDA links(HDA/HDMI) with Sound Open Firmware
for Intel(R) platforms.
@@ -295,6 +293,7 @@ config SND_SOC_SOF_HDA_LINK
config SND_SOC_SOF_HDA_AUDIO_CODEC
bool "SOF support for HDAudio codecs"
depends on SND_SOC_SOF_HDA_LINK
+ select SND_SOC_SOF_PROBE_WORK_QUEUE
help
This adds support for HDAudio codecs with Sound Open Firmware
for Intel(R) platforms.
diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c
index acb4b85868d0..fc63085d2d74 100644
--- a/sound/soc/sof/intel/hda-bus.c
+++ b/sound/soc/sof/intel/hda-bus.c
@@ -70,9 +70,14 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, sof_hda_ext_ops);
-#else /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
+#else
+ snd_hdac_ext_bus_init(bus, dev, NULL, NULL);
+#endif
+#else
+
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
@@ -87,12 +92,12 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev)
bus->idx = 0;
spin_lock_init(&bus->reg_lock);
-#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
+#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */
}
void sof_hda_bus_exit(struct snd_sof_dev *sdev)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
struct hdac_bus *bus = sof_to_bus(sdev);
snd_hdac_ext_bus_exit(bus);
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index 4b39cecacd68..f3513796c189 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -16,7 +16,7 @@
#include "hda.h"
/* These ops are only applicable for the HDA DAI's in their current form */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
/*
* This function checks if the host dma channel corresponding
* to the link DMA stream_tag argument is assigned to one
@@ -120,6 +120,26 @@ static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
return snd_soc_dai_get_dma_data(cpu_dai, substream);
}
+static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ /* mark pipeline so that it can be skipped during FE trigger */
+ pipeline->skip_during_fe_trigger = true;
+
+ return snd_soc_dai_get_dma_data(cpu_dai, substream);
+}
+
static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream)
@@ -155,20 +175,67 @@ static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stre
snd_hdac_ext_stream_reset(hext_stream);
}
+static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct hdac_stream *hstream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+ /* set the hdac_stream in the codec dai */
+ snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
+}
+
+static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ unsigned int link_bps;
+ unsigned int format_val;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ link_bps = codec_dai->driver->playback.sig_bits;
+ else
+ link_bps = codec_dai->driver->capture.sig_bits;
+
+ format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
+ params_format(params), link_bps, 0);
+
+ dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+ params_rate(params), params_channels(params), params_format(params));
+
+ return format_val;
+}
+
+static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct hdac_bus *bus = sof_to_bus(sdev);
+
+ return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
+}
+
static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream, int cmd)
{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
- int ret;
+ int ret = 0;
w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
swidget = w->dobj.private;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -179,16 +246,17 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
- return ret;
+ goto out;
pipeline->state = SOF_IPC4_PIPE_PAUSED;
break;
default:
dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
- return -EINVAL;
+ ret = -EINVAL;
}
-
- return 0;
+out:
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ return ret;
}
static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
@@ -217,64 +285,76 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream, int cmd)
{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
- int ret;
+ int ret = 0;
w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
swidget = w->dobj.private;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
- return ret;
+ goto out;
pipeline->state = SOF_IPC4_PIPE_PAUSED;
}
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_RUNNING);
if (ret < 0)
- return ret;
+ goto out;
pipeline->state = SOF_IPC4_PIPE_RUNNING;
+ swidget->spipe->started_count++;
break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RESET);
+ SOF_IPC4_PIPE_RUNNING);
if (ret < 0)
- return ret;
-
- pipeline->state = SOF_IPC4_PIPE_RESET;
+ goto out;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ /*
+ * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
+ * been stopped. So, clear the started_count so that the pipeline can be reset
+ */
+ swidget->spipe->started_count = 0;
break;
- }
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
break;
default:
dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
-
- return 0;
+out:
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ return ret;
}
static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
- .get_hext_stream = hda_get_hext_stream,
+ .get_hext_stream = hda_ipc4_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
.release_hext_stream = hda_release_hext_stream,
.setup_hext_stream = hda_setup_hext_stream,
.reset_hext_stream = hda_reset_hext_stream,
.pre_trigger = hda_ipc4_pre_trigger,
.trigger = hda_trigger,
- .post_trigger = hda_ipc4_post_trigger
+ .post_trigger = hda_ipc4_post_trigger,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
};
static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
@@ -284,6 +364,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
.setup_hext_stream = hda_setup_hext_stream,
.reset_hext_stream = hda_reset_hext_stream,
.trigger = hda_trigger,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
};
static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
@@ -317,6 +400,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
.reset_hext_stream = hda_reset_hext_stream,
.trigger = hda_trigger,
.post_trigger = hda_ipc3_post_trigger,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
};
static struct hdac_ext_stream *
@@ -343,6 +429,9 @@ static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
.get_hext_stream = hda_dspless_get_hext_stream,
.setup_hext_stream = hda_dspless_setup_hext_stream,
+ .codec_dai_set_stream = hda_codec_dai_set_stream,
+ .calc_stream_format = hda_calc_stream_format,
+ .get_hlink = hda_get_hlink,
};
#endif
@@ -350,7 +439,7 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
const struct hda_dai_widget_dma_ops *
hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
struct snd_sof_dai *sdai;
if (sdev->dspless_mode_selected)
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 44a5d94c5050..3297dea493aa 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -27,20 +27,26 @@ static bool hda_use_tplg_nhlt;
module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
+static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+
+ return snd_soc_component_get_drvdata(component);
+}
+
int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
struct snd_sof_dai_config_data *data)
{
struct snd_sof_widget *swidget = w->dobj.private;
const struct sof_ipc_tplg_ops *tplg_ops;
- struct snd_soc_component *component;
struct snd_sof_dev *sdev;
int ret;
if (!swidget)
return 0;
- component = swidget->scomp;
- sdev = snd_soc_component_get_drvdata(component);
+ sdev = widget_to_sdev(w);
tplg_ops = sof_ipc_get_ops(sdev, tplg);
if (tplg_ops && tplg_ops->dai_config) {
@@ -55,16 +61,26 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
return 0;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+
+static struct snd_sof_dev *dai_to_sdev(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+ return widget_to_sdev(w);
+}
static const struct hda_dai_widget_dma_ops *
hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dev *sdev;
struct snd_sof_dai *sdai;
+ sdev = widget_to_sdev(w);
+
/*
* The swidget parameter of hda_select_dai_widget_ops() is ignored in
* case of DSPless mode
@@ -93,18 +109,22 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai
static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
struct hdac_ext_stream *hext_stream,
- struct snd_soc_dai *cpu_dai,
- struct snd_soc_dai *codec_dai)
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
- struct hdac_stream *hstream = &hext_stream->hstream;
- struct hdac_bus *bus = hstream->bus;
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_link *hlink;
+ struct snd_sof_dev *sdev;
int stream_tag;
- hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
+ sdev = dai_to_sdev(substream, cpu_dai);
+
+ hlink = ops->get_hlink(sdev, substream);
if (!hlink)
return -EINVAL;
@@ -129,21 +149,20 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
{
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
struct hdac_ext_link *hlink;
struct snd_sof_dev *sdev;
- struct hdac_bus *bus;
- unsigned int format_val;
- unsigned int link_bps;
int stream_tag;
- sdev = snd_soc_component_get_drvdata(cpu_dai->component);
- bus = sof_to_bus(sdev);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
+ sdev = dai_to_sdev(substream, cpu_dai);
+
+ hlink = ops->get_hlink(sdev, substream);
if (!hlink)
return -EINVAL;
@@ -164,48 +183,32 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
/* set the hdac_stream in the codec dai */
- snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- link_bps = codec_dai->driver->playback.sig_bits;
- else
- link_bps = codec_dai->driver->capture.sig_bits;
+ if (ops->codec_dai_set_stream)
+ ops->codec_dai_set_stream(sdev, substream, hstream);
if (ops->reset_hext_stream)
ops->reset_hext_stream(sdev, hext_stream);
- format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
- params_format(params), link_bps, 0);
+ if (ops->calc_stream_format && ops->setup_hext_stream) {
+ unsigned int format_val = ops->calc_stream_format(sdev, substream, params);
- dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
- params_rate(params), params_channels(params), params_format(params));
-
- if (ops->setup_hext_stream)
ops->setup_hext_stream(sdev, hext_stream, format_val);
+ }
hext_stream->link_prepared = 1;
return 0;
}
-static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- int stream = substream->stream;
-
- return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
-}
-
-static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai);
if (!ops) {
- dev_err(sdev->dev, "DAI widget ops not set\n");
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
return -EINVAL;
}
@@ -213,19 +216,19 @@ static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_d
if (!hext_stream)
return 0;
- return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
+ return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
}
-static int hda_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
struct hdac_ext_stream *hext_stream;
struct snd_sof_dai_config_data data = { 0 };
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+ struct snd_sof_dev *sdev = widget_to_sdev(w);
int ret;
if (!ops) {
@@ -249,57 +252,32 @@ static int hda_dai_hw_params(struct snd_pcm_substream *substream,
return hda_dai_config(w, flags, &data);
}
-static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
-{
- struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
- struct hdac_ext_stream *hext_stream;
- struct snd_sof_dai_config_data data = { 0 };
- unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
- int ret;
-
- hext_stream = ops->get_hext_stream(sdev, dai, substream);
- if (hext_stream && hext_stream->link_prepared)
- return 0;
-
- dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
-
- ret = hda_link_dma_prepare(substream, dai);
- if (ret < 0)
- return ret;
-
- hext_stream = ops->get_hext_stream(sdev, dai, substream);
-
- flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
- data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
-
- return hda_dai_config(w, flags, &data);
-}
-
/*
* In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
* (over IPC channel) and DMA state change (direct host register changes).
*/
-static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
struct hdac_ext_stream *hext_stream;
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai *codec_dai;
+ struct snd_sof_dev *sdev;
int ret;
+ if (!ops) {
+ dev_err(dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
dai->name, substream->stream);
+ sdev = dai_to_sdev(substream, dai);
+
hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (!hext_stream)
return -EINVAL;
- rtd = asoc_substream_to_rtd(substream);
- codec_dai = asoc_rtd_to_codec(rtd, 0);
-
if (ops->pre_trigger) {
ret = ops->pre_trigger(sdev, dai, substream, cmd);
if (ret < 0)
@@ -320,7 +298,7 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct
switch (cmd) {
case SNDRV_PCM_TRIGGER_SUSPEND:
- ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai);
+ ret = hda_link_dma_cleanup(substream, hext_stream, dai);
if (ret < 0) {
dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
return ret;
@@ -333,6 +311,16 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct
return 0;
}
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+
+static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int stream = substream->stream;
+
+ return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai);
+}
+
static const struct snd_soc_dai_ops hda_dai_ops = {
.hw_params = hda_dai_hw_params,
.hw_free = hda_dai_hw_free,
@@ -340,6 +328,8 @@ static const struct snd_soc_dai_ops hda_dai_ops = {
.prepare = hda_dai_prepare,
};
+#endif
+
static int hda_dai_suspend(struct hdac_bus *bus)
{
struct snd_soc_pcm_runtime *rtd;
@@ -362,23 +352,21 @@ static int hda_dai_suspend(struct hdac_bus *bus)
const struct hda_dai_widget_dma_ops *ops;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
- struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
struct snd_sof_dev *sdev;
struct snd_sof_dai *sdai;
rtd = asoc_substream_to_rtd(hext_stream->link_substream);
cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- codec_dai = asoc_rtd_to_codec(rtd, 0);
w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
swidget = w->dobj.private;
- sdev = snd_soc_component_get_drvdata(swidget->scomp);
+ sdev = widget_to_sdev(w);
sdai = swidget->private;
ops = sdai->platform_private;
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
- cpu_dai, codec_dai);
+ cpu_dai);
if (ret < 0)
return ret;
@@ -588,7 +576,7 @@ int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
* Since the component suspend is called last, we can trap this corner case
* and force the DAIs to release their resources.
*/
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
int ret;
ret = hda_dai_suspend(sof_to_bus(sdev));
diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c
index 69fdef8f89ae..1e77ca936f80 100644
--- a/sound/soc/sof/intel/hda-loader-skl.c
+++ b/sound/soc/sof/intel/hda-loader-skl.c
@@ -15,7 +15,6 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include <sound/sof.h>
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index 981e7b699bdb..f23c72cdff48 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -33,7 +33,7 @@ static bool hda_always_enable_dmi_l1;
module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
-static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS);
+static bool hda_disable_rewinds;
module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444);
MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds");
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 8de422604ad5..b13acb959653 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -15,7 +15,6 @@
* Hardware interface for generic Intel audio DSP HDA IP
*/
-#include <linux/pm_runtime.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
#include <sound/sof.h>
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 3153e21f100a..ec31a5762ddf 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -222,6 +222,31 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
return 0;
}
+int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hdev;
+ struct sdw_intel_ctx *ctx;
+ struct hdac_bus *bus;
+ u32 slcount;
+
+ bus = sof_to_bus(sdev);
+
+ hdev = sdev->pdata->hw_pdata;
+ ctx = hdev->sdw;
+
+ slcount = hdac_bus_eml_get_count(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+
+ /* Check HW supported vs property value */
+ if (slcount < ctx->count) {
+ dev_err(sdev->dev,
+ "%s: BIOS master count %d is larger than hardware capabilities %d\n",
+ __func__, ctx->count, slcount);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int hda_sdw_check_lcount(struct snd_sof_dev *sdev)
{
const struct sof_intel_dsp_desc *chip;
@@ -1343,12 +1368,22 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev,
hda_mach->mach_params.dmic_num = dmic_num;
pdata->tplg_filename = tplg_filename;
- if (codec_num == 2) {
+ if (codec_num == 2 ||
+ (codec_num == 1 && !HDA_IDISP_CODEC(bus->codec_mask))) {
/*
* Prevent SoundWire links from starting when an external
* HDaudio codec is used
*/
hda_mach->mach_params.link_mask = 0;
+ } else {
+ /*
+ * Allow SoundWire links to start when no external HDaudio codec
+ * was detected. This will not create a SoundWire card but
+ * will help detect if any SoundWire codec reports as ATTACHED.
+ */
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
+ hda_mach->mach_params.link_mask = hdev->info.link_mask;
}
*mach = hda_mach;
@@ -1562,7 +1597,11 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
mach_params = &mach->mach_params;
mach_params->platform = dev_name(sdev->dev);
- mach_params->num_dai_drivers = desc->ops->num_drv;
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ mach_params->num_dai_drivers = SOF_SKL_NUM_DAIS_NOCODEC;
+ else
+ mach_params->num_dai_drivers = desc->ops->num_drv;
mach_params->dai_drivers = desc->ops->drv;
}
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index c4befacde23e..3f7c6fb05e5d 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -414,10 +414,12 @@
(HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl))
/* Number of DAIs */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#define SOF_SKL_NUM_DAIS_NOCODEC 8
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
#define SOF_SKL_NUM_DAIS 15
#else
-#define SOF_SKL_NUM_DAIS 8
+#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC
#endif
/* Intel HD Audio SRAM Window 0*/
@@ -779,6 +781,7 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev);
+int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev);
int hda_sdw_startup(struct snd_sof_dev *sdev);
void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable);
void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable);
@@ -792,6 +795,11 @@ static inline int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
return 0;
}
+static inline int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
static inline int hda_sdw_startup(struct snd_sof_dev *sdev)
{
return 0;
@@ -917,6 +925,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
* @pre_trigger: Function pointer for DAI DMA pre-trigger actions
* @trigger: Function pointer for DAI DMA trigger actions
* @post_trigger: Function pointer for DAI DMA post-trigger actions
+ * @codec_dai_set_stream: Function pointer to set codec-side stream information
+ * @calc_stream_format: Function pointer to determine stream format from hw_params and
+ * for HDaudio codec DAI from the .sig bits
+ * @get_hlink: Mandatory function pointer to retrieve hlink, mainly to program LOSIDV
+ * for legacy HDaudio links or program HDaudio Extended Link registers.
*/
struct hda_dai_widget_dma_ops {
struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev,
@@ -936,6 +949,14 @@ struct hda_dai_widget_dma_ops {
struct snd_pcm_substream *substream, int cmd);
int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream, int cmd);
+ void (*codec_dai_set_stream)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct hdac_stream *hstream);
+ unsigned int (*calc_stream_format)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+ struct hdac_ext_link * (*get_hlink)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream);
};
const struct hda_dai_widget_dma_ops *
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 46caf3ccde66..30fe77fd87bf 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -55,7 +55,7 @@ static void mtl_ipc_dsp_done(struct snd_sof_dev *sdev)
}
/* Check if an IPC IRQ occurred */
-static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
+bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
{
u32 irq_status;
u32 hfintipptr;
@@ -118,7 +118,7 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms
return 0;
}
-static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
+void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
@@ -132,7 +132,7 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE);
}
-static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev)
+void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
@@ -173,7 +173,7 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
enable ? "enable" : "disable");
}
-static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
+int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
{
u32 hfintipptr;
u32 irqinten;
@@ -361,11 +361,17 @@ static int mtl_dsp_core_power_up(struct snd_sof_dev *sdev, int core)
ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE, dspcxctl,
(dspcxctl & cpa) == cpa, HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_RESET_TIMEOUT_US);
- if (ret < 0)
+ if (ret < 0) {
dev_err(sdev->dev, "%s: timeout on MTL_DSP2CXCTL_PRIMARY_CORE read\n",
__func__);
+ return ret;
+ }
- return ret;
+ /* set primary core mask and refcount to 1 */
+ sdev->enabled_cores_mask = BIT(SOF_DSP_PRIMARY_CORE);
+ sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 1;
+
+ return 0;
}
static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core)
@@ -388,13 +394,18 @@ static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core)
!(dspcxctl & MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK),
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC);
- if (ret < 0)
+ if (ret < 0) {
dev_err(sdev->dev, "failed to power down primary core\n");
+ return ret;
+ }
- return ret;
+ sdev->enabled_cores_mask = 0;
+ sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 0;
+
+ return 0;
}
-static int mtl_power_down_dsp(struct snd_sof_dev *sdev)
+int mtl_power_down_dsp(struct snd_sof_dev *sdev)
{
u32 dsphfdsscs, cpa;
int ret;
@@ -421,7 +432,7 @@ static int mtl_power_down_dsp(struct snd_sof_dev *sdev)
HDA_DSP_RESET_TIMEOUT_US);
}
-static int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
+int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
@@ -613,6 +624,36 @@ static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev,
return ((u64)llp_u << 32) | llp_l;
}
+static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return mtl_dsp_core_power_up(sdev, SOF_DSP_PRIMARY_CORE);
+
+ if (pm_ops->set_core_state)
+ return pm_ops->set_core_state(sdev, core, true);
+
+ return 0;
+}
+
+static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
+{
+ const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ int ret;
+
+ if (pm_ops->set_core_state) {
+ ret = pm_ops->set_core_state(sdev, core, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (core == SOF_DSP_PRIMARY_CORE)
+ return mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE);
+
+ return 0;
+}
+
/* Meteorlake ops */
struct snd_sof_dsp_ops sof_mtl_ops;
EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -649,7 +690,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
sof_mtl_ops.parse_platform_ext_manifest = NULL;
/* dsp core get/put */
- /* TODO: add core_get and core_put */
+ sof_mtl_ops.core_get = mtl_dsp_core_get;
+ sof_mtl_ops.core_put = mtl_dsp_core_put;
sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
index 26418fb08807..2794fe6e8139 100644
--- a/sound/soc/sof/intel/mtl.h
+++ b/sound/soc/sof/intel/mtl.h
@@ -82,3 +82,10 @@
#define MTL_DSP_REG_HfIMRIS1 0x162088
#define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0)
+void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev);
+void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev);
+bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
+
+int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable);
+int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
+int mtl_power_down_dsp(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c
index 13efdb94d071..d24e64e71b58 100644
--- a/sound/soc/sof/intel/skl.c
+++ b/sound/soc/sof/intel/skl.c
@@ -19,7 +19,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/pm_runtime.h>
#include <sound/hdaudio_ext.h>
#include <sound/pcm_params.h>
#include <sound/sof.h>
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 2713b7dc7931..8e2b07e1612b 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -39,14 +39,18 @@ static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core)
static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core)
{
const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ int ret;
+
+ if (pm_ops->set_core_state) {
+ ret = pm_ops->set_core_state(sdev, core, false);
+ if (ret < 0)
+ return ret;
+ }
/* power down primary core and return */
if (core == SOF_DSP_PRIMARY_CORE)
return hda_dsp_core_reset_power_down(sdev, BIT(core));
- if (pm_ops->set_core_state)
- return pm_ops->set_core_state(sdev, core, false);
-
return 0;
}
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index ad040e7bb850..a8deec7dc021 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -96,6 +96,26 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,
cdata->elems_remaining = 0;
ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+ if (!set)
+ goto unlock;
+
+ /* It is a set-data operation, and we have a backup that we can restore */
+ if (ret < 0) {
+ if (!scontrol->old_ipc_control_data)
+ goto unlock;
+ /*
+ * Current ipc_control_data is not valid, we use the last known good
+ * configuration
+ */
+ memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
+ scontrol->max_size);
+ kfree(scontrol->old_ipc_control_data);
+ scontrol->old_ipc_control_data = NULL;
+ /* Send the last known good configuration to firmware */
+ ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+ if (ret < 0)
+ goto unlock;
+ }
unlock:
if (lock)
@@ -351,6 +371,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_ctl_tlv header;
+ int ret = -EINVAL;
/*
* The beginning of bytes data contains a header from where
@@ -381,31 +402,52 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
- if (copy_from_user(cdata->data, tlvd->tlv, header.length))
- return -EFAULT;
+ if (!scontrol->old_ipc_control_data) {
+ /* Create a backup of the current, valid bytes control */
+ scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,
+ scontrol->max_size, GFP_KERNEL);
+ if (!scontrol->old_ipc_control_data)
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(cdata->data, tlvd->tlv, header.length)) {
+ ret = -EFAULT;
+ goto err_restore;
+ }
if (cdata->data->magic != SOF_ABI_MAGIC) {
dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic);
- return -EINVAL;
+ goto err_restore;
}
if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n",
cdata->data->abi);
- return -EINVAL;
+ goto err_restore;
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");
- return -EINVAL;
+ goto err_restore;
}
/* notify DSP of byte control updates */
- if (pm_runtime_active(scomp->dev))
+ if (pm_runtime_active(scomp->dev)) {
+ /* Actually send the data to the DSP; this is an opportunity to validate the data */
return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
+ }
return 0;
+
+err_restore:
+ /* If we have an issue, we restore the old, valid bytes control data */
+ if (scontrol->old_ipc_control_data) {
+ memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size);
+ kfree(scontrol->old_ipc_control_data);
+ scontrol->old_ipc_control_data = NULL;
+ }
+ return ret;
}
static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h
index f5044202f3c5..0bbca418e67e 100644
--- a/sound/soc/sof/ipc3-priv.h
+++ b/sound/soc/sof/ipc3-priv.h
@@ -28,6 +28,8 @@ int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev);
/* dtrace position update */
int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
struct sof_ipc_dma_trace_posn *posn);
+/* RX handler backend */
+void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf);
/* dtrace platform callback wrappers */
static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index c67767742093..2c5aac31e8b0 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -223,6 +223,14 @@ static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd)
}
#endif
+static void sof_ipc3_dump_payload(struct snd_sof_dev *sdev,
+ void *ipc_data, size_t size)
+{
+ printk(KERN_DEBUG "Size of payload following the header: %zu\n", size);
+ print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET,
+ 16, 4, ipc_data, size, false);
+}
+
static int sof_ipc3_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
@@ -374,6 +382,29 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
+ if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) {
+ size_t payload_bytes, header_bytes;
+ char *payload = NULL;
+
+ /* payload is indicated by non zero msg/reply_bytes */
+ if (msg_bytes > sizeof(struct sof_ipc_cmd_hdr)) {
+ payload = msg_data;
+
+ header_bytes = sizeof(struct sof_ipc_cmd_hdr);
+ payload_bytes = msg_bytes - header_bytes;
+ } else if (reply_bytes > sizeof(struct sof_ipc_reply)) {
+ payload = reply_data;
+
+ header_bytes = sizeof(struct sof_ipc_reply);
+ payload_bytes = reply_bytes - header_bytes;
+ }
+
+ if (payload) {
+ payload += header_bytes;
+ sof_ipc3_dump_payload(sdev, payload, payload_bytes);
+ }
+ }
+
mutex_unlock(&ipc->tx_mutex);
return ret;
@@ -472,6 +503,14 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da
offset += payload_size;
}
+ if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) {
+ size_t header_bytes = sizeof(struct sof_ipc_reply);
+ char *payload = (char *)cdata;
+
+ payload += header_bytes;
+ sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes);
+ }
+
mutex_unlock(&sdev->ipc->tx_mutex);
kfree(cdata_chunk);
@@ -954,31 +993,21 @@ static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
}
}
-/* DSP firmware has sent host a message */
-static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
+void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf)
{
ipc3_rx_callback rx_callback = NULL;
- struct sof_ipc_cmd_hdr hdr;
- void *msg_buf;
u32 cmd;
int err;
- /* read back header */
- err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
- if (err < 0) {
- dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
- return;
- }
+ ipc3_log_header(sdev->dev, "ipc rx", hdr->cmd);
- if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) {
+ if (hdr->size < sizeof(hdr) || hdr->size > SOF_IPC_MSG_MAX_SIZE) {
dev_err(sdev->dev, "The received message size is invalid: %u\n",
- hdr.size);
+ hdr->size);
return;
}
- ipc3_log_header(sdev->dev, "ipc rx", hdr.cmd);
-
- cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
+ cmd = hdr->cmd & SOF_GLB_TYPE_MASK;
/* check message type */
switch (cmd) {
@@ -1016,6 +1045,36 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
break;
}
+ /* Call local handler for the message */
+ if (rx_callback)
+ rx_callback(sdev, msg_buf);
+
+ /* Notify registered clients */
+ sof_client_ipc_rx_dispatcher(sdev, msg_buf);
+
+ ipc3_log_header(sdev->dev, "ipc rx done", hdr->cmd);
+}
+EXPORT_SYMBOL(sof_ipc3_do_rx_work);
+
+/* DSP firmware has sent host a message */
+static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc_cmd_hdr hdr;
+ void *msg_buf;
+ int err;
+
+ /* read back header */
+ err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
+ if (err < 0) {
+ dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
+ return;
+ }
+
+ if (hdr.size < sizeof(hdr)) {
+ dev_err(sdev->dev, "The received message size is invalid\n");
+ return;
+ }
+
/* read the full message */
msg_buf = kmalloc(hdr.size, GFP_KERNEL);
if (!msg_buf)
@@ -1024,18 +1083,13 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
if (err < 0) {
dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
- } else {
- /* Call local handler for the message */
- if (rx_callback)
- rx_callback(sdev, msg_buf);
-
- /* Notify registered clients */
- sof_client_ipc_rx_dispatcher(sdev, msg_buf);
+ kfree(msg_buf);
+ return;
}
- kfree(msg_buf);
+ sof_ipc3_do_rx_work(sdev, &hdr, msg_buf);
- ipc3_log_header(sdev->dev, "ipc rx done", hdr.cmd);
+ kfree(msg_buf);
}
static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 6f0698be9451..c6d404d44097 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -54,6 +54,26 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
ret = iops->set_get_data(sdev, msg, msg->data_size, set);
+ if (!set)
+ goto unlock;
+
+ /* It is a set-data operation, and we have a valid backup that we can restore */
+ if (ret < 0) {
+ if (!scontrol->old_ipc_control_data)
+ goto unlock;
+ /*
+ * Current ipc_control_data is not valid, we use the last known good
+ * configuration
+ */
+ memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
+ scontrol->max_size);
+ kfree(scontrol->old_ipc_control_data);
+ scontrol->old_ipc_control_data = NULL;
+ /* Send the last known good configuration to firmware */
+ ret = iops->set_get_data(sdev, msg, msg->data_size, set);
+ if (ret < 0)
+ goto unlock;
+ }
unlock:
if (lock)
@@ -327,13 +347,24 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
+ if (!scontrol->old_ipc_control_data) {
+ /* Create a backup of the current, valid bytes control */
+ scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,
+ scontrol->max_size, GFP_KERNEL);
+ if (!scontrol->old_ipc_control_data)
+ return -ENOMEM;
+ }
+
/* Copy the whole binary data which includes the ABI header and the payload */
- if (copy_from_user(data, tlvd->tlv, header.length))
+ if (copy_from_user(data, tlvd->tlv, header.length)) {
+ memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
+ scontrol->max_size);
+ kfree(scontrol->old_ipc_control_data);
+ scontrol->old_ipc_control_data = NULL;
return -EFAULT;
+ }
- sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
-
- return 0;
+ return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
}
static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index 1321acc402fd..eaa04762eb11 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -112,16 +112,13 @@ static ssize_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev,
return -EINVAL;
}
- /* a module's config is always the same size */
- fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes;
+ fw_module->fw_mod_cfg = &fm_config[fm_entry->cfg_offset];
dev_dbg(sdev->dev,
"module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n",
fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count,
- fw_module->bss_size);
+ fm_config[fm_entry->cfg_offset].is_bytes);
} else {
- fw_module->bss_size = 0;
-
dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name,
&fm_entry->uuid);
}
@@ -426,6 +423,71 @@ int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev)
return ret;
}
+/**
+ * sof_ipc4_update_cpc_from_manifest - Update the cpc in base config from manifest
+ * @sdev: SOF device
+ * @fw_module: pointer struct sof_ipc4_fw_module to parse
+ * @basecfg: Pointer to the base_config to update
+ */
+void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev,
+ struct sof_ipc4_fw_module *fw_module,
+ struct sof_ipc4_base_module_cfg *basecfg)
+{
+ const struct sof_man4_module_config *fw_mod_cfg;
+ u32 cpc_pick = 0;
+ u32 max_cpc = 0;
+ const char *msg;
+ int i;
+
+ if (!fw_module->fw_mod_cfg) {
+ msg = "No mod_cfg available for CPC lookup in the firmware file's manifest";
+ goto no_cpc;
+ }
+
+ /*
+ * Find the best matching (highest) CPC value based on the module's
+ * IBS/OBS configuration inferred from the audio format selection.
+ *
+ * The CPC value in each module config entry has been measured and
+ * recorded as a IBS/OBS/CPC triplet and stored in the firmware file's
+ * manifest
+ */
+ fw_mod_cfg = fw_module->fw_mod_cfg;
+ for (i = 0; i < fw_module->man4_module_entry.cfg_count; i++) {
+ if (basecfg->obs == fw_mod_cfg[i].obs &&
+ basecfg->ibs == fw_mod_cfg[i].ibs &&
+ cpc_pick < fw_mod_cfg[i].cpc)
+ cpc_pick = fw_mod_cfg[i].cpc;
+
+ if (max_cpc < fw_mod_cfg[i].cpc)
+ max_cpc = fw_mod_cfg[i].cpc;
+ }
+
+ basecfg->cpc = cpc_pick;
+
+ /* We have a matching configuration for CPC */
+ if (basecfg->cpc)
+ return;
+
+ /*
+ * No matching IBS/OBS found, the firmware manifest is missing
+ * information in the module's module configuration table.
+ */
+ if (!max_cpc)
+ msg = "No CPC value available in the firmware file's manifest";
+ else if (!cpc_pick)
+ msg = "No CPC match in the firmware file's manifest";
+
+no_cpc:
+ dev_warn(sdev->dev, "%s (UUID: %pUL): %s (ibs/obs: %u/%u)\n",
+ fw_module->man4_module_entry.name,
+ &fw_module->man4_module_entry.uuid, msg, basecfg->ibs,
+ basecfg->obs);
+ dev_warn_once(sdev->dev, "Please try to update the firmware.\n");
+ dev_warn_once(sdev->dev, "If the issue persists, file a bug at\n");
+ dev_warn_once(sdev->dev, "https://github.com/thesofproject/sof/issues/\n");
+}
+
const struct sof_ipc_fw_loader_ops ipc4_loader_ops = {
.validate = sof_ipc4_validate_firmware,
.parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man,
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 9e2b6c45080d..0c905bd0fab4 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -69,7 +69,7 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
- if (pipeline->skip_during_fe_trigger)
+ if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET)
return;
switch (state) {
@@ -108,7 +108,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
int i;
- if (pipeline->skip_during_fe_trigger)
+ if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET)
return;
/* set state for pipeline if it was just triggered */
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index f461b8c70df3..a5d0b2eae464 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -28,14 +28,14 @@ enum sof_ipc4_mtrace_type {
/**
* struct sof_ipc4_fw_module - IPC4 module info
* @sof_man4_module: Module info
+ * @fw_mod_cfg: Pointer to the module config start of the module
* @m_ida: Module instance identifier
- * @bss_size: Module object size
* @private: Module private data
*/
struct sof_ipc4_fw_module {
struct sof_man4_module man4_module_entry;
+ const struct sof_man4_module_config *fw_mod_cfg;
struct ida m_ida;
- u32 bss_size;
void *private;
};
@@ -114,4 +114,10 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev);
int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev);
struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev,
const guid_t *uuid);
+
+struct sof_ipc4_base_module_cfg;
+void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev,
+ struct sof_ipc4_fw_module *fw_module,
+ struct sof_ipc4_base_module_cfg *basecfg);
+
#endif
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 5abe616a2054..a4e1a70b607d 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -39,8 +39,6 @@ static const struct sof_topology_token pipeline_tokens[] = {
};
static const struct sof_topology_token ipc4_comp_tokens[] = {
- {SOF_TKN_COMP_CPC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_base_module_cfg, cpc)},
{SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct sof_ipc4_base_module_cfg, is_pages)},
};
@@ -235,7 +233,7 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,
"Number of input audio formats: %d. Number of output audio formats: %d\n",
available_fmt->num_input_formats, available_fmt->num_output_formats);
- /* set cpc and is_pages in the module's base_config */
+ /* set is_pages in the module's base_config */
ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*module_base_cfg), 1);
if (ret) {
@@ -244,8 +242,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,
return ret;
}
- dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n",
- swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages);
+ dev_dbg(scomp->dev, "widget %s: is_pages: %d\n", swidget->widget->name,
+ module_base_cfg->is_pages);
if (available_fmt->num_input_formats) {
in_format = kcalloc(available_fmt->num_input_formats,
@@ -561,7 +559,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
strcmp(w->widget->sname, swidget->widget->sname))
continue;
- blob->alh_cfg.count++;
+ blob->alh_cfg.device_count++;
}
ipc4_copier->copier_config = (uint32_t *)blob;
@@ -723,9 +721,9 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
}
dev_dbg(scomp->dev,
- "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x, cpc %d\n",
+ "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x\n",
swidget->widget->name, gain->data.curve_type, gain->data.curve_duration_l,
- gain->data.init_val, gain->base_config.cpc);
+ gain->data.init_val);
ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg);
if (ret)
@@ -936,8 +934,8 @@ static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget)
}
static void
-sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
- struct sof_ipc4_base_module_cfg *base_config)
+sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ struct sof_ipc4_base_module_cfg *base_config)
{
struct sof_ipc4_fw_module *fw_module = swidget->module_info;
struct snd_sof_widget *pipe_widget;
@@ -968,6 +966,13 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage += total;
+
+ /* Update base_config->cpc from the module manifest */
+ sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config);
+
+ dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n",
+ swidget->widget->name, base_config->ibs, base_config->obs,
+ base_config->cpc);
}
static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
@@ -1028,47 +1033,125 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
return 0;
}
-static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
- struct snd_sof_widget *swidget,
- struct sof_ipc4_base_module_cfg *base_config,
- struct snd_pcm_hw_params *params,
- struct sof_ipc4_available_audio_format *available_fmt,
- struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
+static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev,
+ struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
{
- u32 valid_bits;
- u32 channels;
- u32 rate;
- int sample_valid_bits;
+ struct sof_ipc4_audio_format *fmt;
+ u32 rate, channels, valid_bits;
+ int i;
+
+ fmt = &pin_fmts[0].audio_fmt;
+ rate = fmt->sampling_frequency;
+ channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+ valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+
+ /* check if all output formats in topology are the same */
+ for (i = 1; i < pin_fmts_size; i++) {
+ u32 _rate, _channels, _valid_bits;
+
+ fmt = &pin_fmts[i].audio_fmt;
+ _rate = fmt->sampling_frequency;
+ _channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+ _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+
+ if (_rate != rate || _channels != channels || _valid_bits != valid_bits)
+ return false;
+ }
+
+ return true;
+}
+
+static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
+ struct sof_ipc4_base_module_cfg *base_config,
+ struct sof_ipc4_available_audio_format *available_fmt,
+ u32 out_ref_rate, u32 out_ref_channels,
+ u32 out_ref_valid_bits)
+{
+ struct sof_ipc4_audio_format *out_fmt;
+ bool single_format;
int i;
- if (!pin_fmts) {
- dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name);
+ if (!available_fmt->num_output_formats)
return -EINVAL;
+
+ single_format = sof_ipc4_is_single_format(sdev, available_fmt->output_pin_fmts,
+ available_fmt->num_output_formats);
+
+ /* pick the first format if there's only one available or if all formats are the same */
+ if (single_format) {
+ base_config->obs = available_fmt->output_pin_fmts[0].buffer_size;
+ return 0;
}
+ /*
+ * if there are multiple output formats, then choose the output format that matches
+ * the reference params
+ */
+ for (i = 0; i < available_fmt->num_output_formats; i++) {
+ u32 _out_rate, _out_channels, _out_valid_bits;
+
+ out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
+ _out_rate = out_fmt->sampling_frequency;
+ _out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg);
+ _out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
+
+ if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&
+ _out_valid_bits == out_ref_valid_bits) {
+ base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
+{
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- sample_valid_bits = 16;
- break;
+ return 16;
case SNDRV_PCM_FORMAT_S24_LE:
- sample_valid_bits = 24;
- break;
+ return 24;
case SNDRV_PCM_FORMAT_S32_LE:
- sample_valid_bits = 32;
- break;
+ return 32;
default:
dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));
return -EINVAL;
}
+}
+
+static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget,
+ struct sof_ipc4_base_module_cfg *base_config,
+ struct snd_pcm_hw_params *params,
+ struct sof_ipc4_available_audio_format *available_fmt)
+{
+ struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts;
+ u32 pin_fmts_size = available_fmt->num_input_formats;
+ u32 valid_bits;
+ u32 channels;
+ u32 rate;
+ bool single_format;
+ int sample_valid_bits;
+ int i = 0;
- if (!pin_fmts_size) {
- dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name);
+ if (!available_fmt->num_input_formats) {
+ dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name);
return -EINVAL;
}
+ single_format = sof_ipc4_is_single_format(sdev, available_fmt->input_pin_fmts,
+ available_fmt->num_input_formats);
+ if (single_format)
+ goto in_fmt;
+
+ sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params);
+ if (sample_valid_bits < 0)
+ return sample_valid_bits;
+
/*
- * Search supported audio formats with pin index 0 to match rate, channels ,and
- * sample_valid_bytes from runtime params
+ * Search supported input audio formats with pin index 0 to match rate, channels and
+ * sample_valid_bits from reference params
*/
for (i = 0; i < pin_fmts_size; i++) {
struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
@@ -1093,6 +1176,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
return -EINVAL;
}
+in_fmt:
/* copy input format */
if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) {
memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt,
@@ -1105,10 +1189,6 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1);
}
- if (available_fmt->num_output_formats && i < available_fmt->num_output_formats)
- base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
-
- /* Return the index of the matched format */
return i;
}
@@ -1145,7 +1225,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
unsigned int group_id;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
- if (blob->alh_cfg.count > 1) {
+ if (blob->alh_cfg.device_count > 1) {
group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) -
ALH_MULTI_GTW_BASE;
ida_free(&alh_group_ida, group_id);
@@ -1288,50 +1368,6 @@ static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth)
return 0;
}
-static int ipc4_copier_set_capture_fmt(struct snd_sof_dev *sdev,
- struct snd_pcm_hw_params *pipeline_params,
- struct snd_pcm_hw_params *fe_params,
- struct sof_ipc4_available_audio_format *available_fmt)
-{
- struct sof_ipc4_audio_format *audio_fmt;
- unsigned int sample_valid_bits;
- bool multiple_formats = false;
- bool fe_format_match = false;
- struct snd_mask *fmt;
- int i;
-
- for (i = 0; i < available_fmt->num_output_formats; i++) {
- unsigned int val;
-
- audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
- val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(audio_fmt->fmt_cfg);
-
- if (i == 0)
- sample_valid_bits = val;
- else if (sample_valid_bits != val)
- multiple_formats = true;
-
- if (snd_pcm_format_width(params_format(fe_params)) == val)
- fe_format_match = true;
- }
-
- fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
- snd_mask_none(fmt);
-
- if (multiple_formats) {
- if (fe_format_match) {
- /* multiple formats defined and one matches FE */
- snd_mask_set_format(fmt, params_format(fe_params));
- return 0;
- }
-
- dev_err(sdev->dev, "Multiple audio formats for single dai_out not supported\n");
- return -EINVAL;
- }
-
- return ipc4_set_fmt_mask(fmt, sample_valid_bits);
-}
-
static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
@@ -1341,19 +1377,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc4_pin_format *format_list_to_search;
struct sof_ipc4_copier_data *copier_data;
struct snd_pcm_hw_params *ref_params;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_dai *dai;
struct snd_mask *fmt;
int out_sample_valid_bits;
+ u32 gtw_cfg_config_length;
+ u32 dma_config_tlv_size = 0;
void **ipc_config_data;
int *ipc_config_size;
u32 **data;
- int ipc_size, ret;
+ int ipc_size, ret, out_ref_valid_bits;
+ u32 out_ref_rate, out_ref_channels;
u32 deep_buffer_dma_ms = 0;
- u32 format_list_count;
+ int output_fmt_index;
dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
@@ -1417,13 +1455,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts
* for capture.
*/
- if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
- format_list_to_search = available_fmt->input_pin_fmts;
- format_list_count = available_fmt->num_input_formats;
- } else {
- format_list_to_search = available_fmt->output_pin_fmts;
- format_list_count = available_fmt->num_output_formats;
- }
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ref_params = fe_params;
+ else
+ ref_params = pipeline_params;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |=
@@ -1431,7 +1466,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
/* set gateway attributes */
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
- ref_params = fe_params;
break;
}
case snd_soc_dapm_dai_in:
@@ -1448,20 +1482,17 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc4_copier = (struct sof_ipc4_copier *)dai->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
- if (dir == SNDRV_PCM_STREAM_CAPTURE) {
- format_list_to_search = available_fmt->output_pin_fmts;
- format_list_count = available_fmt->num_output_formats;
-
- ret = ipc4_copier_set_capture_fmt(sdev, pipeline_params, fe_params,
- available_fmt);
- if (ret < 0)
- return ret;
- } else {
- format_list_to_search = available_fmt->input_pin_fmts;
- format_list_count = available_fmt->num_input_formats;
- }
- ref_params = pipeline_params;
+ /*
+ * When there is format conversion within a pipeline, the number of supported
+ * output formats is typically limited to just 1 for the DAI copiers. But when there
+ * is no format conversion, the DAI copiers input format must match that of the
+ * FE hw_params for capture and the pipeline params for playback.
+ */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ref_params = pipeline_params;
+ else
+ ref_params = fe_params;
ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
ipc4_copier->dai_type, dir,
@@ -1477,10 +1508,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
-
- /* Use the input formats to match pcm params */
- format_list_to_search = available_fmt->input_pin_fmts;
- format_list_count = available_fmt->num_input_formats;
ref_params = pipeline_params;
break;
@@ -1492,11 +1519,51 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* set input and output audio formats */
- ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
- available_fmt, format_list_to_search, format_list_count);
+ ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
+ available_fmt);
if (ret < 0)
return ret;
+ /* set the reference params for output format selection */
+ switch (swidget->id) {
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_dai_out:
+ case snd_soc_dapm_buffer:
+ {
+ struct sof_ipc4_audio_format *in_fmt;
+
+ in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+ break;
+ }
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai_in:
+ out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
+ if (out_ref_valid_bits < 0)
+ return out_ref_valid_bits;
+
+ out_ref_rate = params_rate(fe_params);
+ out_ref_channels = params_channels(fe_params);
+ break;
+ default:
+ /*
+ * Unsupported type should be caught by the former switch default
+ * case, this should never happen in reality.
+ */
+ return -EINVAL;
+ }
+
+ output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config,
+ available_fmt, out_ref_rate,
+ out_ref_channels, out_ref_valid_bits);
+ if (output_fmt_index < 0) {
+ dev_err(sdev->dev, "Failed to initialize output format for %s",
+ swidget->widget->name);
+ return output_fmt_index;
+ }
+
/*
* Set the output format. Current topology defines pin 0 input and output formats in pairs.
* This assumes that the pin 0 formats are defined before all other pins.
@@ -1504,10 +1571,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* input format. This logic will need to be updated when the format definitions
* in topology change.
*/
- memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
+ memcpy(&copier_data->out_format,
+ &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,
sizeof(struct sof_ipc4_audio_format));
dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name);
- sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1);
+ sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[output_fmt_index], 1);
switch (swidget->id) {
case snd_soc_dapm_dai_in:
@@ -1515,7 +1583,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
{
/*
* Only SOF_DAI_INTEL_ALH needs copier_data to set blob.
- * That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt
+ * That's why only ALH dai's blob is set after sof_ipc4_init_input_audio_fmt
*/
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_alh_configuration_blob *blob;
@@ -1543,7 +1611,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ch_map >>= 4;
}
- step = ch_count / blob->alh_cfg.count;
+ step = ch_count / blob->alh_cfg.device_count;
mask = GENMASK(step - 1, 0);
/*
* Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
@@ -1558,7 +1626,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
dai = w->private;
alh_copier = (struct sof_ipc4_copier *)dai->private;
alh_data = &alh_copier->data;
- blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id;
+ blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id;
/*
* Set the same channel mask for playback as the audio data is
* duplicated for all speakers. For capture, split the channels
@@ -1577,7 +1645,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
i++;
}
- if (blob->alh_cfg.count > 1) {
+ if (blob->alh_cfg.device_count > 1) {
int group_id;
group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1,
@@ -1633,7 +1701,27 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc_config_data = &ipc4_copier->ipc_config_data;
/* config_length is DWORD based */
- ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4;
+ gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4;
+ ipc_size = sizeof(*copier_data) + gtw_cfg_config_length;
+
+ if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID &&
+ ipc4_copier->dma_config_tlv.length) {
+ dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) +
+ ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size;
+
+ /* paranoia check on TLV size/length */
+ if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length +
+ sizeof(uint32_t) * 2) {
+ dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n",
+ dma_config_tlv_size, ipc4_copier->dma_config_tlv.length);
+ return -EINVAL;
+ }
+
+ ipc_size += dma_config_tlv_size;
+
+ /* we also need to increase the size at the gtw level */
+ copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4;
+ }
dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size);
@@ -1645,12 +1733,18 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
/* copy IPC data */
memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data));
- if (copier_data->gtw_cfg.config_length)
+ if (gtw_cfg_config_length)
memcpy(*ipc_config_data + sizeof(*copier_data),
- *data, copier_data->gtw_cfg.config_length * 4);
+ *data, gtw_cfg_config_length);
+
+ /* add DMA Config TLV, if configured */
+ if (dma_config_tlv_size)
+ memcpy(*ipc_config_data + sizeof(*copier_data) +
+ gtw_cfg_config_length,
+ &ipc4_copier->dma_config_tlv, dma_config_tlv_size);
/* update pipeline memory usage */
- sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &copier_data->base_config);
+ sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config);
return 0;
}
@@ -1664,17 +1758,30 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_gain *gain = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
+ struct sof_ipc4_audio_format *in_fmt;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
int ret;
- ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config,
- pipeline_params, available_fmt,
- available_fmt->input_pin_fmts,
- available_fmt->num_input_formats);
+ ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->base_config,
+ pipeline_params, available_fmt);
if (ret < 0)
return ret;
+ in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+ ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->base_config, available_fmt,
+ out_ref_rate, out_ref_channels, out_ref_valid_bits);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to initialize output format for %s",
+ swidget->widget->name);
+ return ret;
+ }
+
/* update pipeline memory usage */
- sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config);
+ sof_ipc4_update_resource_usage(sdev, swidget, &gain->base_config);
return 0;
}
@@ -1688,17 +1795,30 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_mixer *mixer = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
+ struct sof_ipc4_audio_format *in_fmt;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
int ret;
- ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config,
- pipeline_params, available_fmt,
- available_fmt->input_pin_fmts,
- available_fmt->num_input_formats);
+ ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config,
+ pipeline_params, available_fmt);
if (ret < 0)
return ret;
+ in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+ ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt,
+ out_ref_rate, out_ref_channels, out_ref_valid_bits);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to initialize output format for %s",
+ swidget->widget->name);
+ return ret;
+ }
+
/* update pipeline memory usage */
- sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config);
+ sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config);
return 0;
}
@@ -1712,18 +1832,30 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_src *src = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
+ struct sof_ipc4_audio_format *in_fmt;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
struct snd_interval *rate;
int ret;
- ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config,
- pipeline_params, available_fmt,
- available_fmt->input_pin_fmts,
- available_fmt->num_input_formats);
+ ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config,
+ pipeline_params, available_fmt);
if (ret < 0)
return ret;
+ in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+ ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt,
+ out_ref_rate, out_ref_channels, out_ref_valid_bits);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to initialize output format for %s",
+ swidget->widget->name);
+ }
+
/* update pipeline memory usage */
- sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config);
+ sof_ipc4_update_resource_usage(sdev, swidget, &src->base_config);
/* update pipeline_params for sink widgets */
rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE);
@@ -1820,20 +1952,37 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_process *process = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
+ struct sof_ipc4_audio_format *in_fmt;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
void *cfg = process->ipc_config_data;
+ int output_fmt_index;
int ret;
- ret = sof_ipc4_init_audio_fmt(sdev, swidget, &process->base_config,
- pipeline_params, available_fmt,
- available_fmt->input_pin_fmts,
- available_fmt->num_input_formats);
+ ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config,
+ pipeline_params, available_fmt);
if (ret < 0)
return ret;
+ in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+ output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config,
+ available_fmt, out_ref_rate,
+ out_ref_channels, out_ref_valid_bits);
+ if (output_fmt_index < 0 && available_fmt->num_output_formats) {
+ dev_err(sdev->dev, "Failed to initialize output format for %s",
+ swidget->widget->name);
+ return output_fmt_index;
+ }
+
/* copy Pin 0 output format */
- if (available_fmt->num_output_formats && ret < available_fmt->num_output_formats &&
- !available_fmt->output_pin_fmts[ret].pin_index) {
- memcpy(&process->output_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
+ if (available_fmt->num_output_formats &&
+ output_fmt_index < available_fmt->num_output_formats &&
+ !available_fmt->output_pin_fmts[output_fmt_index].pin_index) {
+ memcpy(&process->output_format,
+ &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,
sizeof(struct sof_ipc4_audio_format));
/* modify the pipeline params with the pin 0 output format */
@@ -1843,7 +1992,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
}
/* update pipeline memory usage */
- sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &process->base_config);
+ sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config);
/* ipc_config_data is composed of the base_config followed by an optional extension */
memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg));
@@ -2271,11 +2420,11 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
int sink_id)
{
struct sof_ipc4_copier_config_set_sink_format format;
+ const struct sof_ipc_ops *iops = sdev->ipc->ops;
struct sof_ipc4_base_module_cfg *src_config;
const struct sof_ipc4_audio_format *pin_fmt;
struct sof_ipc4_fw_module *fw_module;
struct sof_ipc4_msg msg = {{ 0 }};
- u32 header, extension;
dev_dbg(sdev->dev, "%s set copier sink %d format\n",
src_widget->widget->name, sink_id);
@@ -2305,22 +2454,15 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
msg.data_size = sizeof(format);
msg.data_ptr = &format;
- header = fw_module->man4_module_entry.id;
- header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
- header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
- header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
- header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.primary = fw_module->man4_module_entry.id;
+ msg.primary |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
- extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size);
- extension |=
+ msg.extension =
SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);
- extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
- extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
-
- msg.primary = header;
- msg.extension = extension;
- return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, msg.data_size);
+ return iops->set_get_data(sdev, &msg, msg.data_size, true);
}
static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
@@ -2507,7 +2649,6 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
}
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
- pipeline->skip_during_fe_trigger = true;
fallthrough;
case SOF_DAI_INTEL_ALH:
/*
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index cf007282867b..6dcf14886e85 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -55,7 +55,7 @@
#define SOF_IPC4_GAIN_ALL_CHANNELS_MASK 0xffffffff
#define SOF_IPC4_VOL_ZERO_DB 0x7fffffff
-#define ALH_MAX_NUMBER_OF_GTW 16
+#define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16
#define SOF_IPC4_INVALID_NODE_ID 0xffffffff
@@ -220,18 +220,64 @@ struct sof_ipc4_gtw_attributes {
uint32_t rsvd : 30;
};
-/** struct sof_ipc4_alh_multi_gtw_cfg: ALH gateway cfg data
- * @count: Number of streams (valid items in mapping array)
- * @alh_id: ALH stream id of a single ALH stream aggregated
- * @channel_mask: Channel mask
- * @mapping: ALH streams
+/**
+ * struct sof_ipc4_dma_device_stream_ch_map: abstract representation of
+ * channel mapping to DMAs
+ * @device: representation of hardware device address or FIFO
+ * @channel_mask: channels handled by @device. Channels are expected to be
+ * contiguous
+ */
+struct sof_ipc4_dma_device_stream_ch_map {
+ uint32_t device;
+ uint32_t channel_mask;
+};
+
+/**
+ * struct sof_ipc4_dma_stream_ch_map: DMA configuration data
+ * @device_count: Number valid items in mapping array
+ * @mapping: device address and channel mask
+ */
+struct sof_ipc4_dma_stream_ch_map {
+ uint32_t device_count;
+ struct sof_ipc4_dma_device_stream_ch_map mapping[SOF_IPC4_DMA_DEVICE_MAX_COUNT];
+} __packed;
+
+#define SOF_IPC4_DMA_METHOD_HDA 1
+#define SOF_IPC4_DMA_METHOD_GPDMA 2 /* defined for consistency but not used */
+
+/**
+ * struct sof_ipc4_dma_config: DMA configuration
+ * @dma_method: HDAudio or GPDMA
+ * @pre_allocated_by_host: 1 if host driver allocates DMA channels, 0 otherwise
+ * @dma_channel_id: for HDaudio defined as @stream_id - 1
+ * @stream_id: HDaudio stream tag
+ * @dma_stream_channel_map: array of device/channel mappings
+ * @dma_priv_config_size: currently not used
+ * @dma_priv_config: currently not used
+ */
+struct sof_ipc4_dma_config {
+ uint8_t dma_method;
+ uint8_t pre_allocated_by_host;
+ uint16_t rsvd;
+ uint32_t dma_channel_id;
+ uint32_t stream_id;
+ struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map;
+ uint32_t dma_priv_config_size;
+ uint8_t dma_priv_config[];
+} __packed;
+
+#define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000
+
+/**
+ * struct sof_ipc4_dma_config: DMA configuration
+ * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID
+ * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size
+ * @dma_config: actual DMA configuration
*/
-struct sof_ipc4_alh_multi_gtw_cfg {
- uint32_t count;
- struct {
- uint32_t alh_id;
- uint32_t channel_mask;
- } mapping[ALH_MAX_NUMBER_OF_GTW];
+struct sof_ipc4_dma_config_tlv {
+ uint32_t type;
+ uint32_t length;
+ struct sof_ipc4_dma_config dma_config;
} __packed;
/** struct sof_ipc4_alh_configuration_blob: ALH blob
@@ -240,7 +286,7 @@ struct sof_ipc4_alh_multi_gtw_cfg {
*/
struct sof_ipc4_alh_configuration_blob {
struct sof_ipc4_gtw_attributes gw_attr;
- struct sof_ipc4_alh_multi_gtw_cfg alh_cfg;
+ struct sof_ipc4_dma_stream_ch_map alh_cfg;
};
/**
@@ -254,6 +300,7 @@ struct sof_ipc4_alh_configuration_blob {
* @gtw_attr: Gateway attributes for copier blob
* @dai_type: DAI type
* @dai_index: DAI index
+ * @dma_config_tlv: DMA configuration
*/
struct sof_ipc4_copier {
struct sof_ipc4_copier_data data;
@@ -266,6 +313,7 @@ struct sof_ipc4_copier {
struct sof_ipc4_gtw_attributes *gtw_attr;
u32 dai_type;
int dai_index;
+ struct sof_ipc4_dma_config_tlv dma_config_tlv;
};
/**
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 246b56d24a6f..ab6eddd91bb7 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -17,15 +17,6 @@
#include "ipc4-priv.h"
#include "ops.h"
-#ifdef DEBUG_VERBOSE
-#define sof_ipc4_dump_payload(sdev, ipc_data, size) \
- print_hex_dump_debug("Message payload: ", \
- DUMP_PREFIX_OFFSET, \
- 16, 4, ipc_data, size, false)
-#else
-#define sof_ipc4_dump_payload(sdev, ipc_data, size) do { } while (0)
-#endif
-
static const struct sof_ipc4_fw_status {
int status;
char *msg;
@@ -256,6 +247,13 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms
}
#endif
+static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev,
+ void *ipc_data, size_t size)
+{
+ print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET,
+ 16, 4, ipc_data, size, false);
+}
+
static int sof_ipc4_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
@@ -362,9 +360,6 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
void *reply_data, size_t reply_bytes, bool no_pm)
{
struct snd_sof_ipc *ipc = sdev->ipc;
-#ifdef DEBUG_VERBOSE
- struct sof_ipc4_msg *msg = NULL;
-#endif
int ret;
if (!msg_data)
@@ -386,18 +381,20 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
- mutex_unlock(&ipc->tx_mutex);
+ if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) {
+ struct sof_ipc4_msg *msg = NULL;
-#ifdef DEBUG_VERBOSE
- /* payload is indicated by non zero msg/reply_bytes */
- if (msg_bytes)
- msg = msg_data;
- else if (reply_bytes)
- msg = reply_data;
+ /* payload is indicated by non zero msg/reply_bytes */
+ if (msg_bytes)
+ msg = msg_data;
+ else if (reply_bytes)
+ msg = reply_data;
- if (msg)
- sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
-#endif
+ if (msg)
+ sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
+ }
+
+ mutex_unlock(&ipc->tx_mutex);
return ret;
}
@@ -516,7 +513,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
if (!set && payload_bytes != offset)
ipc4_msg->data_size = offset;
- sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
+ if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD))
+ sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
out:
mutex_unlock(&sdev->ipc->tx_mutex);
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c
index 2df3b7ae1c6f..cb2ab5884b8c 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c
@@ -8,7 +8,6 @@
// Hardware interface for mt8186 DSP clock
#include <linux/clk.h>
-#include <linux/pm_runtime.h>
#include <linux/io.h>
#include "../../sof-audio.h"
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index 597cb4476acb..3e0ea0e109e2 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -24,6 +24,7 @@
#include "../../sof-of-dev.h"
#include "../../sof-audio.h"
#include "../adsp_helper.h"
+#include "../mtk-adsp-common.h"
#include "mt8186.h"
#include "mt8186-clk.h"
@@ -48,47 +49,13 @@ static int mt8186_send_msg(struct snd_sof_dev *sdev,
return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ);
}
-static void mt8186_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply has correct size? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
static void mt8186_dsp_handle_reply(struct mtk_adsp_ipc *ipc)
{
struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
unsigned long flags;
spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
- mt8186_get_reply(priv->sdev);
- snd_sof_ipc_reply(priv->sdev, 0);
+ snd_sof_ipc_process_reply(priv->sdev, 0);
spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
@@ -507,6 +474,26 @@ static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev,
return pos;
}
+static void mt8186_adsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ u32 dbg_pc, dbg_data, dbg_inst, dbg_ls0stat, dbg_status, faultinfo;
+
+ /* dump debug registers */
+ dbg_pc = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGPC);
+ dbg_data = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGDATA);
+ dbg_inst = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGINST);
+ dbg_ls0stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS0STAT);
+ dbg_status = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGSTATUS);
+ faultinfo = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTINFO);
+
+ dev_info(sdev->dev, "adsp dump : pc %#x, data %#x, dbg_inst %#x,",
+ dbg_pc, dbg_data, dbg_inst);
+ dev_info(sdev->dev, "ls0stat %#x, status %#x, faultinfo %#x",
+ dbg_ls0stat, dbg_status, faultinfo);
+
+ mtk_adsp_dump(sdev, flags);
+}
+
static struct snd_soc_dai_driver mt8186_dai[] = {
{
.name = "SOF_DL1",
@@ -589,6 +576,7 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = {
.num_drv = ARRAY_SIZE(mt8186_dai),
/* Debug information */
+ .dbg_dump = mt8186_adsp_dump,
.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* PM */
@@ -628,7 +616,65 @@ static const struct sof_dev_desc sof_of_mt8186_desc = {
.ops = &sof_mt8186_ops,
};
+/*
+ * DL2, DL3, UL4, UL5 are registered as SOF FE, so creating the corresponding
+ * SOF BE to complete the pipeline.
+ */
+static struct snd_soc_dai_driver mt8188_dai[] = {
+{
+ .name = "SOF_DL2",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_DL3",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_UL4",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_UL5",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+};
+
+/* mt8188 ops */
+static struct snd_sof_dsp_ops sof_mt8188_ops;
+
+static int sof_mt8188_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_mt8188_ops, &sof_mt8186_ops, sizeof(sof_mt8188_ops));
+
+ sof_mt8188_ops.drv = mt8188_dai;
+ sof_mt8188_ops.num_drv = ARRAY_SIZE(mt8188_dai);
+
+ return 0;
+}
+
+static struct snd_sof_of_mach sof_mt8188_machs[] = {
+ {
+ .compatible = "mediatek,mt8188",
+ .sof_tplg_filename = "sof-mt8188.tplg",
+ },
+ {}
+};
+
static const struct sof_dev_desc sof_of_mt8188_desc = {
+ .of_machines = sof_mt8188_machs,
.ipc_supported_mask = BIT(SOF_IPC),
.ipc_default = SOF_IPC,
.default_fw_path = {
@@ -641,7 +687,8 @@ static const struct sof_dev_desc sof_of_mt8188_desc = {
[SOF_IPC] = "sof-mt8188.ri",
},
.nocodec_tplg_filename = "sof-mt8188-nocodec.tplg",
- .ops = &sof_mt8186_ops,
+ .ops = &sof_mt8188_ops,
+ .ops_init = sof_mt8188_ops_init,
};
static const struct of_device_id sof_of_mt8186_ids[] = {
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h
index 5b521c60b4e3..91323f492a1e 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.h
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.h
@@ -38,6 +38,11 @@ struct snd_sof_dev;
#define DSP_MBOX3_IRQ_EN BIT(3)
#define DSP_MBOX4_IRQ_EN BIT(4)
#define DSP_PDEBUGPC 0x013C
+#define DSP_PDEBUGDATA 0x0140
+#define DSP_PDEBUGINST 0x0144
+#define DSP_PDEBUGLS0STAT 0x0148
+#define DSP_PDEBUGSTATUS 0x014C
+#define DSP_PFAULTINFO 0x0150
#define ADSP_CK_EN 0x1000
#define CORE_CLK_EN BIT(0)
#define COREDBG_EN BIT(1)
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
index 9ef08e43aa38..7cffcad00f9b 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
@@ -7,7 +7,6 @@
// Hardware interface for mt8195 DSP clock
#include <linux/clk.h>
-#include <linux/pm_runtime.h>
#include <linux/io.h>
#include "mt8195.h"
#include "mt8195-clk.h"
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index 42bae574c87a..7d6a568556ea 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -49,47 +49,13 @@ static int mt8195_send_msg(struct snd_sof_dev *sdev,
return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ);
}
-static void mt8195_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply has correct size? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
static void mt8195_dsp_handle_reply(struct mtk_adsp_ipc *ipc)
{
struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
unsigned long flags;
spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
- mt8195_get_reply(priv->sdev);
- snd_sof_ipc_reply(priv->sdev, 0);
+ snd_sof_ipc_process_reply(priv->sdev, 0);
spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index b13bfdeb2b70..7c5bb9badb6c 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -32,7 +32,7 @@ static int sof_nocodec_bes_setup(struct device *dev,
/* set up BE dai_links */
for (i = 0; i < link_num; i++) {
- dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
+ dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL);
if (!dlc)
return -ENOMEM;
@@ -44,8 +44,8 @@ static int sof_nocodec_bes_setup(struct device *dev,
links[i].stream_name = links[i].name;
links[i].cpus = &dlc[0];
- links[i].codecs = &dlc[1];
- links[i].platforms = &dlc[2];
+ links[i].codecs = &asoc_dummy_dlc;
+ links[i].platforms = &dlc[1];
links[i].num_cpus = 1;
links[i].num_codecs = 1;
@@ -55,8 +55,6 @@ static int sof_nocodec_bes_setup(struct device *dev,
links[i].no_pcm = 1;
links[i].cpus->dai_name = drv[i].name;
links[i].platforms->name = dev_name(dev->parent);
- links[i].codecs->dai_name = "snd-soc-dummy-dai";
- links[i].codecs->name = "snd-soc-dummy";
if (drv[i].playback.channels_min)
links[i].dpcm_playback = 1;
if (drv[i].capture.channels_min)
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index d0ab6f390734..d778717cab10 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -369,7 +369,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
/* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */
- if (!pcm_ops || (pcm_ops && !pcm_ops->platform_stop_during_hw_free))
+ if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
break;
default:
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 2b232442e84b..704b21413c71 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -234,20 +234,16 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
pm_state.event = target_state;
- /* Skip to platform-specific suspend if DSP is entering D0 */
- if (target_state == SOF_DSP_PM_D0) {
- sof_fw_trace_suspend(sdev, pm_state);
- /* Notify clients not managed by pm framework about core suspend */
- sof_suspend_clients(sdev, pm_state);
- goto suspend;
- }
-
/* suspend DMA trace */
sof_fw_trace_suspend(sdev, pm_state);
/* Notify clients not managed by pm framework about core suspend */
sof_suspend_clients(sdev, pm_state);
+ /* Skip to platform-specific suspend if DSP is entering D0 */
+ if (target_state == SOF_DSP_PM_D0)
+ goto suspend;
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
/* cache debugfs contents during runtime suspend */
if (runtime_suspend)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 1cbda595c518..e7ef77012c35 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -14,6 +14,20 @@
#include "sof-of-dev.h"
#include "ops.h"
+static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+ const char *func)
+{
+ switch (widget->id) {
+ case snd_soc_dapm_out_drv:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_input:
+ dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name);
+ return true;
+ default:
+ return false;
+ }
+}
+
static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
{
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
@@ -231,23 +245,9 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
bool route_found = false;
/* ignore routes involving virtual widgets in topology */
- switch (src_widget->id) {
- case snd_soc_dapm_out_drv:
- case snd_soc_dapm_output:
- case snd_soc_dapm_input:
+ if (is_virtual_widget(sdev, src_widget->widget, __func__) ||
+ is_virtual_widget(sdev, sink_widget->widget, __func__))
return 0;
- default:
- break;
- }
-
- switch (sink_widget->id) {
- case snd_soc_dapm_out_drv:
- case snd_soc_dapm_output:
- case snd_soc_dapm_input:
- return 0;
- default:
- break;
- }
/* find route matching source and sink widgets */
list_for_each_entry(sroute, &sdev->route_list, list)
@@ -396,6 +396,9 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_dapm_path *p;
+ if (is_virtual_widget(sdev, widget, __func__))
+ return;
+
/* skip if the widget is in use or if it is already unprepared */
if (!swidget || !swidget->prepared || swidget->use_count > 0)
goto sink_unprepare;
@@ -433,6 +436,9 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
struct snd_soc_dapm_path *p;
int ret;
+ if (is_virtual_widget(sdev, widget, __func__))
+ return 0;
+
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
if (!widget_ops)
return 0;
@@ -488,6 +494,9 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
int err;
int ret = 0;
+ if (is_virtual_widget(sdev, widget, __func__))
+ return 0;
+
if (widget->dobj.private) {
err = sof_widget_free(sdev, widget->dobj.private);
if (err < 0)
@@ -527,6 +536,9 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
struct snd_soc_dapm_path *p;
int ret;
+ if (is_virtual_widget(sdev, widget, __func__))
+ return 0;
+
if (swidget) {
int i;
@@ -592,6 +604,9 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
for_each_dapm_widgets(list, i, widget) {
+ if (is_virtual_widget(sdev, widget, __func__))
+ continue;
+
/* starting widget for playback is AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
continue;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index a090a9eb4828..5d5eeb1a1a6f 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -362,6 +362,7 @@ struct snd_sof_control {
size_t priv_size; /* size of private data */
size_t max_size;
void *ipc_control_data;
+ void *old_ipc_control_data;
int max; /* applicable to volume controls */
u32 size; /* cdata size */
u32 *volume_table; /* volume table computed from tlv data*/
diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c
new file mode 100644
index 000000000000..ad0ed2d570a9
--- /dev/null
+++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2023 Google Inc. All rights reserved.
+//
+// Author: Curtis Malainey <cujomalainey@chromium.org>
+//
+
+#include <linux/auxiliary_bus.h>
+#include <linux/debugfs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/sof/header.h>
+
+#include "sof-client.h"
+
+#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000
+
+struct sof_msg_inject_priv {
+ struct dentry *kernel_dfs_file;
+ size_t max_msg_size;
+
+ void *kernel_buffer;
+};
+
+static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file)
+{
+ int ret = debugfs_file_get(file->f_path.dentry);
+
+ if (unlikely(ret))
+ return ret;
+
+ ret = simple_open(inode, file);
+ if (ret)
+ debugfs_file_put(file->f_path.dentry);
+
+ return ret;
+}
+
+static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_msg_inject_priv *priv = cdev->data;
+ struct sof_ipc_cmd_hdr *hdr = priv->kernel_buffer;
+ struct device *dev = &cdev->auxdev.dev;
+ ssize_t size;
+ int ret;
+
+ if (*ppos)
+ return 0;
+
+ size = simple_write_to_buffer(priv->kernel_buffer, priv->max_msg_size,
+ ppos, buffer, count);
+ if (size < 0)
+ return size;
+ if (size != count)
+ return -EFAULT;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
+ return ret;
+ }
+
+ sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
+
+ pm_runtime_mark_last_busy(dev);
+ ret = pm_runtime_put_autosuspend(dev);
+ if (ret < 0)
+ dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret);
+
+ return count;
+};
+
+static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
+{
+ debugfs_file_put(file->f_path.dentry);
+
+ return 0;
+}
+
+static const struct file_operations sof_kernel_msg_inject_fops = {
+ .open = sof_msg_inject_dfs_open,
+ .write = sof_kernel_msg_inject_dfs_write,
+ .release = sof_msg_inject_dfs_release,
+
+ .owner = THIS_MODULE,
+};
+
+static int sof_msg_inject_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
+ struct device *dev = &auxdev->dev;
+ struct sof_msg_inject_priv *priv;
+ size_t alloc_size;
+
+ /* allocate memory for client data */
+ priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
+ alloc_size = priv->max_msg_size;
+ priv->kernel_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL);
+
+ if (!priv->kernel_buffer)
+ return -ENOMEM;
+
+ cdev->data = priv;
+
+ priv->kernel_dfs_file = debugfs_create_file("kernel_ipc_msg_inject", 0644,
+ debugfs_root, cdev,
+ &sof_kernel_msg_inject_fops);
+
+ /* enable runtime PM */
+ pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void sof_msg_inject_remove(struct auxiliary_device *auxdev)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct sof_msg_inject_priv *priv = cdev->data;
+
+ pm_runtime_disable(&auxdev->dev);
+
+ debugfs_remove(priv->kernel_dfs_file);
+}
+
+static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = {
+ { .name = "snd_sof.kernel_injector" },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table);
+
+/*
+ * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
+ * type are enough to ensure that the parent SOF device resumes to bring the DSP
+ * back to D0.
+ * Driver name will be set based on KBUILD_MODNAME.
+ */
+static struct auxiliary_driver sof_msg_inject_client_drv = {
+ .probe = sof_msg_inject_probe,
+ .remove = sof_msg_inject_remove,
+
+ .id_table = sof_msg_inject_client_id_table,
+};
+
+module_auxiliary_driver(sof_msg_inject_client_drv);
+
+MODULE_DESCRIPTION("SOF IPC Kernel Injector Client Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index d6b7caa0cf03..284de96e779c 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -16,6 +16,7 @@
#include "ops.h"
#include "sof-client.h"
#include "sof-priv.h"
+#include "ipc3-priv.h"
#include "ipc4-priv.h"
/**
@@ -126,6 +127,29 @@ static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {}
#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR)
+static int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev)
+{
+ /* Only IPC3 supported right now */
+ if (sdev->pdata->ipc_type != SOF_IPC)
+ return 0;
+
+ return sof_client_dev_register(sdev, "kernel_injector", 0, NULL, 0);
+}
+
+static void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev)
+{
+ sof_client_dev_unregister(sdev, "kernel_injector", 0);
+}
+#else
+static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {}
+#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */
+
int sof_register_clients(struct snd_sof_dev *sdev)
{
int ret;
@@ -146,6 +170,12 @@ int sof_register_clients(struct snd_sof_dev *sdev)
goto err_msg_injector;
}
+ ret = sof_register_ipc_kernel_injector(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "IPC kernel injector client registration failed\n");
+ goto err_kernel_injector;
+ }
+
/* Platform depndent client device registration */
if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients)
@@ -154,6 +184,9 @@ int sof_register_clients(struct snd_sof_dev *sdev)
if (!ret)
return 0;
+ sof_unregister_ipc_kernel_injector(sdev);
+
+err_kernel_injector:
sof_unregister_ipc_msg_injector(sdev);
err_msg_injector:
@@ -167,6 +200,7 @@ void sof_unregister_clients(struct snd_sof_dev *sdev)
if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients)
sof_ops(sdev)->unregister_ipc_clients(sdev);
+ sof_unregister_ipc_kernel_injector(sdev);
sof_unregister_ipc_msg_injector(sdev);
sof_unregister_ipc_flood_test(sdev);
}
@@ -269,6 +303,24 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
}
EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
+int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf)
+{
+ if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
+ struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+
+ if (hdr->size < sizeof(hdr)) {
+ dev_err(cdev->sdev->dev, "The received message size is invalid\n");
+ return -EINVAL;
+ }
+
+ sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf);
+ return 0;
+ }
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, SND_SOC_SOF_CLIENT);
+
int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
bool set)
{
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index 10571d1ea9a7..b6ccc2cd69e5 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -75,5 +75,6 @@ int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
sof_client_fw_state_callback callback);
void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev);
enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev);
+int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf);
#endif /* __SOC_SOF_CLIENT_H */
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index cd4f6ac126ec..d4f6702e93dc 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -48,6 +48,9 @@ struct snd_sof_pcm_stream;
#define SOF_DBG_FORCE_NOCODEC BIT(10) /* ignore all codec-related
* configurations
*/
+#define SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD BIT(11) /* On top of the IPC message header
+ * dump the message payload also
+ */
#define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */
/* Flag definitions used for controlling the DSP dump behavior */
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index f160dc454b44..698129dccc7d 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1077,7 +1077,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
- strcmp(w->sname, rtd->dai_link->stream_name))
+ !strstr(rtd->dai_link->stream_name, w->sname))
continue;
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig
new file mode 100644
index 000000000000..fafb681f8c0a
--- /dev/null
+++ b/sound/soc/starfive/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SOC_STARFIVE
+ tristate "Audio support for StarFive SoC"
+ depends on COMPILE_TEST || ARCH_STARFIVE
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Starfive SoCs' Audio interfaces. You will also need to
+ select the audio interfaces to support below.
+
+config SND_SOC_JH7110_TDM
+ tristate "JH7110 TDM device driver"
+ depends on HAVE_CLK && SND_SOC_STARFIVE
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for StarFive TDM driver.
diff --git a/sound/soc/starfive/Makefile b/sound/soc/starfive/Makefile
new file mode 100644
index 000000000000..f7d960211d72
--- /dev/null
+++ b/sound/soc/starfive/Makefile
@@ -0,0 +1,2 @@
+# StarFive Platform Support
+obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c
new file mode 100644
index 000000000000..5f5a6ca7dbda
--- /dev/null
+++ b/sound/soc/starfive/jh7110_tdm.c
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * jh7110_tdm.c -- StarFive JH7110 TDM driver
+ *
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ *
+ * Author: Walker Chen <walker.chen@starfivetech.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define TDM_PCMGBCR 0x00
+ #define PCMGBCR_ENABLE BIT(0)
+ #define CLKPOL_BIT 5
+ #define ELM_BIT 3
+ #define SYNCM_BIT 2
+ #define MS_BIT 1
+#define TDM_PCMTXCR 0x04
+ #define PCMTXCR_TXEN BIT(0)
+ #define IFL_BIT 11
+ #define WL_BIT 8
+ #define SSCALE_BIT 4
+ #define SL_BIT 2
+ #define LRJ_BIT 1
+#define TDM_PCMRXCR 0x08
+ #define PCMRXCR_RXEN BIT(0)
+#define TDM_PCMDIV 0x0c
+
+#define JH7110_TDM_FIFO 0x170c0000
+#define JH7110_TDM_FIFO_DEPTH 32
+
+enum TDM_MASTER_SLAVE_MODE {
+ TDM_AS_MASTER = 0,
+ TDM_AS_SLAVE,
+};
+
+enum TDM_CLKPOL {
+ /* tx raising and rx falling */
+ TDM_TX_RASING_RX_FALLING = 0,
+ /* tx falling and rx raising */
+ TDM_TX_FALLING_RX_RASING,
+};
+
+enum TDM_ELM {
+ /* only work while SYNCM=0 */
+ TDM_ELM_LATE = 0,
+ TDM_ELM_EARLY,
+};
+
+enum TDM_SYNCM {
+ /* short frame sync */
+ TDM_SYNCM_SHORT = 0,
+ /* long frame sync */
+ TDM_SYNCM_LONG,
+};
+
+enum TDM_IFL {
+ /* FIFO to send or received : half-1/2, Quarter-1/4 */
+ TDM_FIFO_HALF = 0,
+ TDM_FIFO_QUARTER,
+};
+
+enum TDM_WL {
+ /* send or received word length */
+ TDM_8BIT_WORD_LEN = 0,
+ TDM_16BIT_WORD_LEN,
+ TDM_20BIT_WORD_LEN,
+ TDM_24BIT_WORD_LEN,
+ TDM_32BIT_WORD_LEN,
+};
+
+enum TDM_SL {
+ /* send or received slot length */
+ TDM_8BIT_SLOT_LEN = 0,
+ TDM_16BIT_SLOT_LEN,
+ TDM_32BIT_SLOT_LEN,
+};
+
+enum TDM_LRJ {
+ /* left-justify or right-justify */
+ TDM_RIGHT_JUSTIFY = 0,
+ TDM_LEFT_JUSTIFT,
+};
+
+struct tdm_chan_cfg {
+ enum TDM_IFL ifl;
+ enum TDM_WL wl;
+ unsigned char sscale;
+ enum TDM_SL sl;
+ enum TDM_LRJ lrj;
+ unsigned char enable;
+};
+
+struct jh7110_tdm_dev {
+ void __iomem *tdm_base;
+ struct device *dev;
+ struct clk_bulk_data clks[6];
+ struct reset_control *resets;
+
+ enum TDM_CLKPOL clkpolity;
+ enum TDM_ELM elm;
+ enum TDM_SYNCM syncm;
+ enum TDM_MASTER_SLAVE_MODE ms_mode;
+
+ struct tdm_chan_cfg tx;
+ struct tdm_chan_cfg rx;
+
+ u16 syncdiv;
+ u32 samplerate;
+ u32 pcmclk;
+
+ /* data related to DMA transfers b/w tdm and DMAC */
+ struct snd_dmaengine_dai_dma_data play_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ u32 saved_pcmgbcr;
+ u32 saved_pcmtxcr;
+ u32 saved_pcmrxcr;
+ u32 saved_pcmdiv;
+};
+
+static inline u32 jh7110_tdm_readl(struct jh7110_tdm_dev *tdm, u16 reg)
+{
+ return readl_relaxed(tdm->tdm_base + reg);
+}
+
+static inline void jh7110_tdm_writel(struct jh7110_tdm_dev *tdm, u16 reg, u32 val)
+{
+ writel_relaxed(val, tdm->tdm_base + reg);
+}
+
+static void jh7110_tdm_save_context(struct jh7110_tdm_dev *tdm,
+ struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tdm->saved_pcmtxcr = jh7110_tdm_readl(tdm, TDM_PCMTXCR);
+ else
+ tdm->saved_pcmrxcr = jh7110_tdm_readl(tdm, TDM_PCMRXCR);
+}
+
+static void jh7110_tdm_start(struct jh7110_tdm_dev *tdm,
+ struct snd_pcm_substream *substream)
+{
+ u32 data;
+
+ data = jh7110_tdm_readl(tdm, TDM_PCMGBCR);
+ jh7110_tdm_writel(tdm, TDM_PCMGBCR, data | PCMGBCR_ENABLE);
+
+ /* restore context */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ jh7110_tdm_writel(tdm, TDM_PCMTXCR, tdm->saved_pcmtxcr | PCMTXCR_TXEN);
+ else
+ jh7110_tdm_writel(tdm, TDM_PCMRXCR, tdm->saved_pcmrxcr | PCMRXCR_RXEN);
+}
+
+static void jh7110_tdm_stop(struct jh7110_tdm_dev *tdm,
+ struct snd_pcm_substream *substream)
+{
+ unsigned int val;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val = jh7110_tdm_readl(tdm, TDM_PCMTXCR);
+ val &= ~PCMTXCR_TXEN;
+ jh7110_tdm_writel(tdm, TDM_PCMTXCR, val);
+ } else {
+ val = jh7110_tdm_readl(tdm, TDM_PCMRXCR);
+ val &= ~PCMRXCR_RXEN;
+ jh7110_tdm_writel(tdm, TDM_PCMRXCR, val);
+ }
+}
+
+static int jh7110_tdm_syncdiv(struct jh7110_tdm_dev *tdm)
+{
+ u32 sl, sscale, syncdiv;
+
+ if (tdm->rx.sl >= tdm->tx.sl)
+ sl = tdm->rx.sl;
+ else
+ sl = tdm->tx.sl;
+
+ if (tdm->rx.sscale >= tdm->tx.sscale)
+ sscale = tdm->rx.sscale;
+ else
+ sscale = tdm->tx.sscale;
+
+ syncdiv = tdm->pcmclk / tdm->samplerate - 1;
+
+ if ((syncdiv + 1) < (sl * sscale)) {
+ dev_err(tdm->dev, "Failed to set syncdiv!\n");
+ return -EINVAL;
+ }
+
+ if (tdm->syncm == TDM_SYNCM_LONG &&
+ (tdm->rx.sscale <= 1 || tdm->tx.sscale <= 1) &&
+ ((syncdiv + 1) <= sl)) {
+ dev_err(tdm->dev, "Wrong syncdiv! It must be (syncdiv+1) > max[tx.sl, rx.sl]\n");
+ return -EINVAL;
+ }
+
+ jh7110_tdm_writel(tdm, TDM_PCMDIV, syncdiv);
+ return 0;
+}
+
+static int jh7110_tdm_config(struct jh7110_tdm_dev *tdm,
+ struct snd_pcm_substream *substream)
+{
+ u32 datarx, datatx;
+ int ret;
+
+ ret = jh7110_tdm_syncdiv(tdm);
+ if (ret)
+ return ret;
+
+ datarx = (tdm->rx.ifl << IFL_BIT) |
+ (tdm->rx.wl << WL_BIT) |
+ (tdm->rx.sscale << SSCALE_BIT) |
+ (tdm->rx.sl << SL_BIT) |
+ (tdm->rx.lrj << LRJ_BIT);
+
+ datatx = (tdm->tx.ifl << IFL_BIT) |
+ (tdm->tx.wl << WL_BIT) |
+ (tdm->tx.sscale << SSCALE_BIT) |
+ (tdm->tx.sl << SL_BIT) |
+ (tdm->tx.lrj << LRJ_BIT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ jh7110_tdm_writel(tdm, TDM_PCMTXCR, datatx);
+ else
+ jh7110_tdm_writel(tdm, TDM_PCMRXCR, datarx);
+
+ return 0;
+}
+
+static void jh7110_tdm_clk_disable(struct jh7110_tdm_dev *tdm)
+{
+ clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks);
+}
+
+static int jh7110_tdm_clk_enable(struct jh7110_tdm_dev *tdm)
+{
+ int ret;
+
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(tdm->clks), tdm->clks);
+ if (ret) {
+ dev_err(tdm->dev, "Failed to enable tdm clocks\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(tdm->resets);
+ if (ret) {
+ dev_err(tdm->dev, "Failed to deassert tdm resets\n");
+ goto dis_tdm_clk;
+ }
+
+ /* select tdm_ext clock as the clock source for tdm */
+ ret = clk_set_parent(tdm->clks[5].clk, tdm->clks[4].clk);
+ if (ret) {
+ dev_err(tdm->dev, "Can't set extern clock source for clk_tdm\n");
+ goto dis_tdm_clk;
+ }
+
+ return 0;
+
+dis_tdm_clk:
+ clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks);
+
+ return ret;
+}
+
+static int jh7110_tdm_runtime_suspend(struct device *dev)
+{
+ struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
+
+ jh7110_tdm_clk_disable(tdm);
+ return 0;
+}
+
+static int jh7110_tdm_runtime_resume(struct device *dev)
+{
+ struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
+
+ return jh7110_tdm_clk_enable(tdm);
+}
+
+static int jh7110_tdm_system_suspend(struct device *dev)
+{
+ struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
+
+ /* save context */
+ tdm->saved_pcmgbcr = jh7110_tdm_readl(tdm, TDM_PCMGBCR);
+ tdm->saved_pcmdiv = jh7110_tdm_readl(tdm, TDM_PCMDIV);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int jh7110_tdm_system_resume(struct device *dev)
+{
+ struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
+
+ /* restore context */
+ jh7110_tdm_writel(tdm, TDM_PCMGBCR, tdm->saved_pcmgbcr);
+ jh7110_tdm_writel(tdm, TDM_PCMDIV, tdm->saved_pcmdiv);
+
+ return pm_runtime_force_resume(dev);
+}
+
+static const struct snd_soc_component_driver jh7110_tdm_component = {
+ .name = "jh7110-tdm",
+};
+
+static int jh7110_tdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
+
+ return 0;
+}
+
+static int jh7110_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai);
+ int chan_wl, chan_sl, chan_nr;
+ unsigned int data_width;
+ unsigned int dma_bus_width;
+ struct snd_dmaengine_dai_dma_data *dma_data = NULL;
+ int ret;
+
+ data_width = params_width(params);
+
+ tdm->samplerate = params_rate(params);
+ tdm->pcmclk = params_channels(params) * tdm->samplerate * data_width;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ chan_wl = TDM_16BIT_WORD_LEN;
+ chan_sl = TDM_16BIT_SLOT_LEN;
+ dma_bus_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ chan_wl = TDM_32BIT_WORD_LEN;
+ chan_sl = TDM_32BIT_SLOT_LEN;
+ dma_bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+
+ default:
+ dev_err(tdm->dev, "tdm: unsupported PCM fmt");
+ return -EINVAL;
+ }
+
+ chan_nr = params_channels(params);
+ switch (chan_nr) {
+ case 1:
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ break;
+ default:
+ dev_err(tdm->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tdm->tx.wl = chan_wl;
+ tdm->tx.sl = chan_sl;
+ tdm->tx.sscale = chan_nr;
+ tdm->play_dma_data.addr_width = dma_bus_width;
+ dma_data = &tdm->play_dma_data;
+ } else {
+ tdm->rx.wl = chan_wl;
+ tdm->rx.sl = chan_sl;
+ tdm->rx.sscale = chan_nr;
+ tdm->capture_dma_data.addr_width = dma_bus_width;
+ dma_data = &tdm->capture_dma_data;
+ }
+
+ snd_soc_dai_set_dma_data(dai, substream, dma_data);
+
+ ret = jh7110_tdm_config(tdm, substream);
+ if (ret)
+ return ret;
+
+ jh7110_tdm_save_context(tdm, substream);
+ return 0;
+}
+
+static int jh7110_tdm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct jh7110_tdm_dev *tdm = 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:
+ jh7110_tdm_start(tdm, substream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ jh7110_tdm_stop(tdm, substream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int jh7110_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int gbcr;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ /* cpu is master */
+ tdm->ms_mode = TDM_AS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ /* codec is master */
+ tdm->ms_mode = TDM_AS_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ return -EINVAL;
+ default:
+ dev_dbg(tdm->dev, "dwc : Invalid clock provider format\n");
+ return -EINVAL;
+ }
+
+ gbcr = (tdm->clkpolity << CLKPOL_BIT) |
+ (tdm->elm << ELM_BIT) |
+ (tdm->syncm << SYNCM_BIT) |
+ (tdm->ms_mode << MS_BIT);
+ jh7110_tdm_writel(tdm, TDM_PCMGBCR, gbcr);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = {
+ .startup = jh7110_tdm_startup,
+ .hw_params = jh7110_tdm_hw_params,
+ .trigger = jh7110_tdm_trigger,
+ .set_fmt = jh7110_tdm_set_dai_fmt,
+};
+
+static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &tdm->play_dma_data, &tdm->capture_dma_data);
+ snd_soc_dai_set_drvdata(dai, tdm);
+ return 0;
+}
+
+#define JH7110_TDM_RATES SNDRV_PCM_RATE_8000_48000
+
+#define JH7110_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver jh7110_tdm_dai = {
+ .name = "sf_tdm",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = JH7110_TDM_RATES,
+ .formats = JH7110_TDM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = JH7110_TDM_RATES,
+ .formats = JH7110_TDM_FORMATS,
+ },
+ .ops = &jh7110_tdm_dai_ops,
+ .probe = jh7110_tdm_dai_probe,
+ .symmetric_rate = 1,
+};
+
+static const struct snd_pcm_hardware jh7110_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ .buffer_bytes_max = 192512,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 48,
+ .fifo_size = 16,
+};
+
+static const struct snd_dmaengine_pcm_config jh7110_dmaengine_pcm_config = {
+ .pcm_hardware = &jh7110_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .prealloc_buffer_size = 192512,
+};
+
+static void jh7110_tdm_init_params(struct jh7110_tdm_dev *tdm)
+{
+ tdm->clkpolity = TDM_TX_RASING_RX_FALLING;
+ tdm->elm = TDM_ELM_LATE;
+ tdm->syncm = TDM_SYNCM_SHORT;
+
+ tdm->rx.ifl = TDM_FIFO_HALF;
+ tdm->tx.ifl = TDM_FIFO_HALF;
+ tdm->rx.wl = TDM_16BIT_WORD_LEN;
+ tdm->tx.wl = TDM_16BIT_WORD_LEN;
+ tdm->rx.sscale = 2;
+ tdm->tx.sscale = 2;
+ tdm->rx.lrj = TDM_LEFT_JUSTIFT;
+ tdm->tx.lrj = TDM_LEFT_JUSTIFT;
+
+ tdm->play_dma_data.addr = JH7110_TDM_FIFO;
+ tdm->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ tdm->play_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2;
+ tdm->play_dma_data.maxburst = 16;
+
+ tdm->capture_dma_data.addr = JH7110_TDM_FIFO;
+ tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ tdm->capture_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2;
+ tdm->capture_dma_data.maxburst = 8;
+}
+
+static int jh7110_tdm_clk_reset_get(struct platform_device *pdev,
+ struct jh7110_tdm_dev *tdm)
+{
+ int ret;
+
+ tdm->clks[0].id = "mclk_inner";
+ tdm->clks[1].id = "tdm_ahb";
+ tdm->clks[2].id = "tdm_apb";
+ tdm->clks[3].id = "tdm_internal";
+ tdm->clks[4].id = "tdm_ext";
+ tdm->clks[5].id = "tdm";
+
+ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to get tdm clocks\n");
+ return ret;
+ }
+
+ tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev);
+ if (IS_ERR(tdm->resets)) {
+ dev_err(&pdev->dev, "Failed to get tdm resets\n");
+ return PTR_ERR(tdm->resets);
+ }
+
+ return 0;
+}
+
+static int jh7110_tdm_probe(struct platform_device *pdev)
+{
+ struct jh7110_tdm_dev *tdm;
+ int ret;
+
+ tdm = devm_kzalloc(&pdev->dev, sizeof(*tdm), GFP_KERNEL);
+ if (!tdm)
+ return -ENOMEM;
+
+ tdm->tdm_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(tdm->tdm_base))
+ return PTR_ERR(tdm->tdm_base);
+
+ tdm->dev = &pdev->dev;
+
+ ret = jh7110_tdm_clk_reset_get(pdev, tdm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n");
+ return ret;
+ }
+
+ jh7110_tdm_init_params(tdm);
+
+ dev_set_drvdata(&pdev->dev, tdm);
+ ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component,
+ &jh7110_tdm_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register dai\n");
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+ &jh7110_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register pcm: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = jh7110_tdm_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int jh7110_tdm_dev_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id jh7110_tdm_of_match[] = {
+ { .compatible = "starfive,jh7110-tdm", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, jh7110_tdm_of_match);
+
+static const struct dev_pm_ops jh7110_tdm_pm_ops = {
+ RUNTIME_PM_OPS(jh7110_tdm_runtime_suspend,
+ jh7110_tdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(jh7110_tdm_system_suspend,
+ jh7110_tdm_system_resume)
+};
+
+static struct platform_driver jh7110_tdm_driver = {
+ .driver = {
+ .name = "jh7110-tdm",
+ .of_match_table = jh7110_tdm_of_match,
+ .pm = pm_ptr(&jh7110_tdm_pm_ops),
+ },
+ .probe = jh7110_tdm_probe,
+ .remove = jh7110_tdm_dev_remove,
+};
+module_platform_driver(jh7110_tdm_driver);
+
+MODULE_DESCRIPTION("StarFive JH7110 TDM ASoC Driver");
+MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index f6695dee353b..271ec5b3378d 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -1485,12 +1485,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
if (ret < 0)
return ret;
} else {
- sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK");
- if (IS_ERR(sai->sai_mclk)) {
- if (PTR_ERR(sai->sai_mclk) != -ENOENT)
- return PTR_ERR(sai->sai_mclk);
- sai->sai_mclk = NULL;
- }
+ sai->sai_mclk = devm_clk_get_optional(&pdev->dev, "MCLK");
+ if (IS_ERR(sai->sai_mclk))
+ return PTR_ERR(sai->sai_mclk);
}
return 0;
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
index e016a6a7f7c4..208e2fcefcf2 100644
--- a/sound/soc/tegra/tegra186_asrc.c
+++ b/sound/soc/tegra/tegra186_asrc.c
@@ -1024,8 +1024,8 @@ static void tegra186_asrc_platform_remove(struct platform_device *pdev)
static const struct dev_pm_ops tegra186_asrc_pm_ops = {
SET_RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend,
tegra186_asrc_runtime_resume, NULL)
- SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
static struct platform_driver tegra186_asrc_driver = {
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index c498145e76e0..a4073a746ae3 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -19,7 +19,6 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index e1a0f50969c1..d38b58305c6b 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -273,13 +273,12 @@ static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai = rule->private;
struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev);
struct clk *parent = clk_get_parent(i2s->clk_i2s);
- long i, parent_rate, valid_rates = 0;
+ unsigned long i, parent_rate, valid_rates = 0;
parent_rate = clk_get_rate(parent);
- if (parent_rate <= 0) {
- dev_err(dai->dev, "Can't get parent clock rate: %ld\n",
- parent_rate);
- return parent_rate ?: -EINVAL;
+ if (!parent_rate) {
+ dev_err(dai->dev, "Can't get parent clock rate\n");
+ return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) {
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index 86bef54dfdf2..d034803695a0 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -187,13 +187,12 @@ static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params,
struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
struct clk *parent = clk_get_parent(spdif->clk_spdif_out);
static const unsigned int rates[] = { 32000, 44100, 48000 };
- long i, parent_rate, valid_rates = 0;
+ unsigned long i, parent_rate, valid_rates = 0;
parent_rate = clk_get_rate(parent);
- if (parent_rate <= 0) {
- dev_err(dai->dev, "Can't get parent clock rate: %ld\n",
- parent_rate);
- return parent_rate ?: -EINVAL;
+ if (!parent_rate) {
+ dev_err(dai->dev, "Can't get parent clock rate\n");
+ return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(rates); i++) {
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
index 41117c1d61fb..bd0b10c70c4c 100644
--- a/sound/soc/tegra/tegra210_adx.c
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -109,7 +109,7 @@ static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev)
static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
unsigned int channels,
- unsigned int format,
+ snd_pcm_format_t format,
unsigned int reg)
{
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index 8c00c09eeefb..3f114a2adfce 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -712,11 +712,6 @@ MUX_ENUM_CTRL_DECL_234(t234_asrc15_tx, 0x68);
MUX_ENUM_CTRL_DECL_234(t234_asrc16_tx, 0x69);
MUX_ENUM_CTRL_DECL_234(t234_asrc17_tx, 0x6a);
-/*
- * The number of entries in, and order of, this array is closely tied to the
- * calculation of tegra210_ahub_codec.num_dapm_widgets near the end of
- * tegra210_ahub_probe()
- */
static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = {
WIDGETS("ADMAIF1", t210_admaif1_tx),
WIDGETS("ADMAIF2", t210_admaif2_tx),
@@ -1092,11 +1087,6 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
{ name " XBAR-Capture", NULL, name " XBAR-TX" }, \
{ name " Capture", NULL, name " XBAR-Capture" },
-/*
- * The number of entries in, and order of, this array is closely tied to the
- * calculation of tegra210_ahub_codec.num_dapm_routes near the end of
- * tegra210_ahub_probe()
- */
static const struct snd_soc_dapm_route tegra210_ahub_routes[] = {
TEGRA_FE_ROUTES("ADMAIF1")
TEGRA_FE_ROUTES("ADMAIF2")
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index c0892be2992b..172fea764a31 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -1328,15 +1328,16 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params,
struct davinci_mcasp_ruledata *rd = rule->private;
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_mask nfmt;
- int i, slot_width;
+ int slot_width;
+ snd_pcm_format_t i;
snd_mask_none(&nfmt);
slot_width = rd->mcasp->slot_width;
- for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
- if (snd_mask_test(fmt, i)) {
+ pcm_for_each_format(i) {
+ if (snd_mask_test_format(fmt, i)) {
if (snd_pcm_format_width(i) <= slot_width) {
- snd_mask_set(&nfmt, i);
+ snd_mask_set_format(&nfmt, i);
}
}
}
@@ -1350,15 +1351,16 @@ static int davinci_mcasp_hw_rule_format_width(struct snd_pcm_hw_params *params,
struct davinci_mcasp_ruledata *rd = rule->private;
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_mask nfmt;
- int i, format_width;
+ int format_width;
+ snd_pcm_format_t i;
snd_mask_none(&nfmt);
format_width = rd->mcasp->max_format_width;
- for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
- if (snd_mask_test(fmt, i)) {
+ pcm_for_each_format(i) {
+ if (snd_mask_test_format(fmt, i)) {
if (snd_pcm_format_width(i) == format_width) {
- snd_mask_set(&nfmt, i);
+ snd_mask_set_format(&nfmt, i);
}
}
}
@@ -1431,12 +1433,13 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_mask nfmt;
int rate = params_rate(params);
int slots = rd->mcasp->tdm_slots;
- int i, count = 0;
+ int count = 0;
+ snd_pcm_format_t i;
snd_mask_none(&nfmt);
- for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
- if (snd_mask_test(fmt, i)) {
+ pcm_for_each_format(i) {
+ if (snd_mask_test_format(fmt, i)) {
uint sbits = snd_pcm_format_width(i);
unsigned int sysclk_freq;
int ppm;
@@ -1454,7 +1457,7 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
sbits * slots * rate,
false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
- snd_mask_set(&nfmt, i);
+ snd_mask_set_format(&nfmt, i);
count++;
}
}
diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c
index 96c3569d7643..a3663ab065ac 100644
--- a/sound/soc/ti/omap-hdmi.c
+++ b/sound/soc/ti/omap-hdmi.c
@@ -365,19 +365,17 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
if (!card->dai_link)
return -ENOMEM;
- compnent = devm_kzalloc(dev, 2 * sizeof(*compnent), GFP_KERNEL);
+ compnent = devm_kzalloc(dev, sizeof(*compnent), GFP_KERNEL);
if (!compnent)
return -ENOMEM;
- card->dai_link->cpus = &compnent[0];
+ card->dai_link->cpus = compnent;
card->dai_link->num_cpus = 1;
- card->dai_link->codecs = &compnent[1];
+ card->dai_link->codecs = &asoc_dummy_dlc;
card->dai_link->num_codecs = 1;
card->dai_link->name = card->name;
card->dai_link->stream_name = card->name;
card->dai_link->cpus->dai_name = dev_name(ad->dssdev);
- card->dai_link->codecs->name = "snd-soc-dummy";
- card->dai_link->codecs->dai_name = "snd-soc-dummy-dai";
card->num_links = 1;
card->dev = dev;
diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c
index 8163f453bf36..b047add5d887 100644
--- a/sound/soc/ti/omap-mcbsp-st.c
+++ b/sound/soc/ti/omap-mcbsp-st.c
@@ -19,7 +19,6 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/pm_runtime.h>
#include "omap-mcbsp.h"
#include "omap-mcbsp-priv.h"
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 4f6911274d56..d81fed1c1226 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -23,9 +23,6 @@ static inline int init_oss_soundcore(void) { return 0; }
static inline void cleanup_oss_soundcore(void) { }
#endif
-struct class *sound_class;
-EXPORT_SYMBOL(sound_class);
-
MODULE_DESCRIPTION("Core sound module");
MODULE_AUTHOR("Alan Cox");
MODULE_LICENSE("GPL");
@@ -37,6 +34,12 @@ static char *sound_devnode(const struct device *dev, umode_t *mode)
return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}
+const struct class sound_class = {
+ .name = "sound",
+ .devnode = sound_devnode,
+};
+EXPORT_SYMBOL(sound_class);
+
static int __init init_soundcore(void)
{
int rc;
@@ -45,21 +48,19 @@ static int __init init_soundcore(void)
if (rc)
return rc;
- sound_class = class_create("sound");
- if (IS_ERR(sound_class)) {
+ rc = class_register(&sound_class);
+ if (rc) {
cleanup_oss_soundcore();
- return PTR_ERR(sound_class);
+ return rc;
}
- sound_class->devnode = sound_devnode;
-
return 0;
}
static void __exit cleanup_soundcore(void)
{
cleanup_oss_soundcore();
- class_destroy(sound_class);
+ class_unregister(&sound_class);
}
subsys_initcall(init_soundcore);
@@ -276,7 +277,7 @@ retry:
}
}
- device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
+ device_create(&sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
NULL, "%s", s->name+6);
return s->unit_minor;
@@ -302,7 +303,7 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
if (!preclaim_oss)
__unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,
p->name);
- device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
+ device_destroy(&sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
kfree(p);
}
}
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index a5385efcedb6..075358a533a0 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -845,7 +845,8 @@ calc_pitch(struct snd_emux_voice *vp)
/* 0xe000: root pitch */
offset += 0xe000 + vp->reg.rate_offset;
- offset += vp->emu->pitch_shift;
+ if (vp->emu->ops.get_pitch_shift)
+ offset += vp->emu->ops.get_pitch_shift(vp->emu);
LIMITVALUE(offset, 0, 0xffff);
if (offset == vp->apitch)
return 0; /* unchanged */
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 059242f15d75..4a9569a3a39a 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -15,6 +15,7 @@ config SND_USB_AUDIO
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
+ select SND_UMP if SND_USB_AUDIO_MIDI_V2
select BITREVERSE
select SND_USB_AUDIO_USE_MEDIA_CONTROLLER if MEDIA_CONTROLLER && (MEDIA_SUPPORT=y || MEDIA_SUPPORT=SND_USB_AUDIO)
help
@@ -24,6 +25,16 @@ config SND_USB_AUDIO
To compile this driver as a module, choose M here: the module
will be called snd-usb-audio.
+config SND_USB_AUDIO_MIDI_V2
+ bool "MIDI 2.0 support by USB Audio driver"
+ depends on SND_USB_AUDIO
+ help
+ Say Y here to include the support for MIDI 2.0 by USB Audio driver.
+ When the config is set, the driver tries to probe MIDI 2.0 interface
+ at first, then falls back to MIDI 1.0 interface as default.
+ The MIDI 2.0 support can be disabled dynamically via midi2_enable
+ module option, too.
+
config SND_USB_AUDIO_USE_MEDIA_CONTROLLER
bool
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 9ccb21a4ff8a..db5ff76d0e61 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -22,6 +22,7 @@ snd-usb-audio-objs := card.o \
stream.o \
validate.o
+snd-usb-audio-$(CONFIG_SND_USB_AUDIO_MIDI_V2) += midi2.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
snd-usbmidi-lib-objs := midi.o
diff --git a/sound/usb/card.c b/sound/usb/card.c
index f6e99ced8068..1b2edc0fd2e9 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -44,6 +44,7 @@
#include "usbaudio.h"
#include "card.h"
#include "midi.h"
+#include "midi2.h"
#include "mixer.h"
#include "proc.h"
#include "quirks.h"
@@ -178,9 +179,8 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
- int err = __snd_usbmidi_create(chip->card, iface,
- &chip->midi_list, NULL,
- chip->usb_id);
+ int err = snd_usb_midi_v2_create(chip, iface, NULL,
+ chip->usb_id);
if (err < 0) {
dev_err(&dev->dev,
"%u:%d: cannot create sequencer device\n",
@@ -485,6 +485,7 @@ static void snd_usb_audio_free(struct snd_card *card)
struct snd_usb_audio *chip = card->private_data;
snd_usb_endpoint_free_all(chip);
+ snd_usb_midi_v2_free_all(chip);
mutex_destroy(&chip->mutex);
if (!atomic_read(&chip->shutdown))
@@ -644,6 +645,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
INIT_LIST_HEAD(&chip->iface_ref_list);
INIT_LIST_HEAD(&chip->clock_ref_list);
INIT_LIST_HEAD(&chip->midi_list);
+ INIT_LIST_HEAD(&chip->midi_v2_list);
INIT_LIST_HEAD(&chip->mixer_list);
if (quirk_flags[idx])
@@ -968,6 +970,7 @@ static void usb_audio_disconnect(struct usb_interface *intf)
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
+ snd_usb_midi_v2_disconnect_all(chip);
/*
* Nice to check quirk && quirk->shares_media_device and
* then call the snd_media_device_delete(). Don't have
@@ -1079,6 +1082,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
snd_usbmidi_suspend(p);
list_for_each_entry(mixer, &chip->mixer_list, list)
snd_usb_mixer_suspend(mixer);
+ snd_usb_midi_v2_suspend_all(chip);
}
if (!PMSG_IS_AUTO(message) && !chip->system_suspend) {
@@ -1124,6 +1128,8 @@ static int usb_audio_resume(struct usb_interface *intf)
snd_usbmidi_resume(p);
}
+ snd_usb_midi_v2_resume_all(chip);
+
out:
if (chip->num_suspended_intf == chip->system_suspend) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 2839f6b6f09b..6b0993258e03 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -2461,7 +2461,8 @@ int __snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
const struct snd_usb_audio_quirk *quirk,
- unsigned int usb_id)
+ unsigned int usb_id,
+ unsigned int *num_rawmidis)
{
struct snd_usb_midi *umidi;
struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -2476,6 +2477,8 @@ int __snd_usbmidi_create(struct snd_card *card,
umidi->iface = iface;
umidi->quirk = quirk;
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
+ if (num_rawmidis)
+ umidi->next_midi_device = *num_rawmidis;
spin_lock_init(&umidi->disc_lock);
init_rwsem(&umidi->disc_rwsem);
mutex_init(&umidi->mutex);
@@ -2595,6 +2598,8 @@ int __snd_usbmidi_create(struct snd_card *card,
usb_autopm_get_interface_no_resume(umidi->iface);
list_add_tail(&umidi->list, midi_list);
+ if (num_rawmidis)
+ *num_rawmidis = umidi->next_midi_device;
return 0;
free_midi:
diff --git a/sound/usb/midi.h b/sound/usb/midi.h
index 3f153195c841..2100f1486b03 100644
--- a/sound/usb/midi.h
+++ b/sound/usb/midi.h
@@ -46,14 +46,15 @@ int __snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
const struct snd_usb_audio_quirk *quirk,
- unsigned int usb_id);
+ unsigned int usb_id,
+ unsigned int *num_rawmidis);
static inline int snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
const struct snd_usb_audio_quirk *quirk)
{
- return __snd_usbmidi_create(card, iface, midi_list, quirk, 0);
+ return __snd_usbmidi_create(card, iface, midi_list, quirk, 0, NULL);
}
void snd_usbmidi_input_stop(struct list_head *p);
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
new file mode 100644
index 000000000000..ee2835741479
--- /dev/null
+++ b/sound/usb/midi2.c
@@ -0,0 +1,1230 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIDI 2.0 support
+ */
+
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+#include <linux/usb/midi-v2.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/ump.h>
+#include "usbaudio.h"
+#include "midi.h"
+#include "midi2.h"
+#include "helper.h"
+
+static bool midi2_enable = true;
+module_param(midi2_enable, bool, 0444);
+MODULE_PARM_DESC(midi2_enable, "Enable MIDI 2.0 support.");
+
+static bool midi2_ump_probe = true;
+module_param(midi2_ump_probe, bool, 0444);
+MODULE_PARM_DESC(midi2_ump_probe, "Probe UMP v1.1 support at first.");
+
+/* stream direction; just shorter names */
+enum {
+ STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT,
+ STR_IN = SNDRV_RAWMIDI_STREAM_INPUT
+};
+
+#define NUM_URBS 8
+
+struct snd_usb_midi2_urb;
+struct snd_usb_midi2_endpoint;
+struct snd_usb_midi2_ump;
+struct snd_usb_midi2_interface;
+
+/* URB context */
+struct snd_usb_midi2_urb {
+ struct urb *urb;
+ struct snd_usb_midi2_endpoint *ep;
+ unsigned int index; /* array index */
+};
+
+/* A USB MIDI input/output endpoint */
+struct snd_usb_midi2_endpoint {
+ struct usb_device *dev;
+ const struct usb_ms20_endpoint_descriptor *ms_ep; /* reference to EP descriptor */
+ struct snd_usb_midi2_endpoint *pair; /* bidirectional pair EP */
+ struct snd_usb_midi2_ump *rmidi; /* assigned UMP EP pair */
+ struct snd_ump_endpoint *ump; /* assigned UMP EP */
+ int direction; /* direction (STR_IN/OUT) */
+ unsigned int endpoint; /* EP number */
+ unsigned int pipe; /* URB pipe */
+ unsigned int packets; /* packet buffer size in bytes */
+ unsigned int interval; /* interval for INT EP */
+ wait_queue_head_t wait; /* URB waiter */
+ spinlock_t lock; /* URB locking */
+ struct snd_rawmidi_substream *substream; /* NULL when closed */
+ unsigned int num_urbs; /* number of allocated URBs */
+ unsigned long urb_free; /* bitmap for free URBs */
+ unsigned long urb_free_mask; /* bitmask for free URBs */
+ atomic_t running; /* running status */
+ atomic_t suspended; /* saved running status for suspend */
+ bool disconnected; /* shadow of umidi->disconnected */
+ struct list_head list; /* list to umidi->ep_list */
+ struct snd_usb_midi2_urb urbs[NUM_URBS];
+};
+
+/* A UMP endpoint - one or two USB MIDI endpoints are assigned */
+struct snd_usb_midi2_ump {
+ struct usb_device *dev;
+ struct snd_usb_midi2_interface *umidi; /* reference to MIDI iface */
+ struct snd_ump_endpoint *ump; /* assigned UMP EP object */
+ struct snd_usb_midi2_endpoint *eps[2]; /* USB MIDI endpoints */
+ int index; /* rawmidi device index */
+ unsigned char usb_block_id; /* USB GTB id used for finding a pair */
+ bool ump_parsed; /* Parsed UMP 1.1 EP/FB info*/
+ struct list_head list; /* list to umidi->rawmidi_list */
+};
+
+/* top-level instance per USB MIDI interface */
+struct snd_usb_midi2_interface {
+ struct snd_usb_audio *chip; /* assigned USB-audio card */
+ struct usb_interface *iface; /* assigned USB interface */
+ struct usb_host_interface *hostif;
+ const char *blk_descs; /* group terminal block descriptors */
+ unsigned int blk_desc_size; /* size of GTB descriptors */
+ bool disconnected;
+ struct list_head ep_list; /* list of endpoints */
+ struct list_head rawmidi_list; /* list of UMP rawmidis */
+ struct list_head list; /* list to chip->midi_v2_list */
+};
+
+/* submit URBs as much as possible; used for both input and output */
+static void do_submit_urbs_locked(struct snd_usb_midi2_endpoint *ep,
+ int (*prepare)(struct snd_usb_midi2_endpoint *,
+ struct urb *))
+{
+ struct snd_usb_midi2_urb *ctx;
+ int index, err = 0;
+
+ if (ep->disconnected)
+ return;
+
+ while (ep->urb_free) {
+ index = find_first_bit(&ep->urb_free, ep->num_urbs);
+ if (index >= ep->num_urbs)
+ return;
+ ctx = &ep->urbs[index];
+ err = prepare(ep, ctx->urb);
+ if (err < 0)
+ return;
+ if (!ctx->urb->transfer_buffer_length)
+ return;
+ ctx->urb->dev = ep->dev;
+ err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+ if (err < 0) {
+ dev_dbg(&ep->dev->dev,
+ "usb_submit_urb error %d\n", err);
+ return;
+ }
+ clear_bit(index, &ep->urb_free);
+ }
+}
+
+/* prepare for output submission: copy from rawmidi buffer to urb packet */
+static int prepare_output_urb(struct snd_usb_midi2_endpoint *ep,
+ struct urb *urb)
+{
+ int count;
+
+ count = snd_ump_transmit(ep->ump, urb->transfer_buffer,
+ ep->packets);
+ if (count < 0) {
+ dev_dbg(&ep->dev->dev, "rawmidi transmit error %d\n", count);
+ return count;
+ }
+ cpu_to_le32_array((u32 *)urb->transfer_buffer, count >> 2);
+ urb->transfer_buffer_length = count;
+ return 0;
+}
+
+static void submit_output_urbs_locked(struct snd_usb_midi2_endpoint *ep)
+{
+ do_submit_urbs_locked(ep, prepare_output_urb);
+}
+
+/* URB completion for output; re-filling and re-submit */
+static void output_urb_complete(struct urb *urb)
+{
+ struct snd_usb_midi2_urb *ctx = urb->context;
+ struct snd_usb_midi2_endpoint *ep = ctx->ep;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ set_bit(ctx->index, &ep->urb_free);
+ if (urb->status >= 0 && atomic_read(&ep->running))
+ submit_output_urbs_locked(ep);
+ if (ep->urb_free == ep->urb_free_mask)
+ wake_up(&ep->wait);
+ spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/* prepare for input submission: just set the buffer length */
+static int prepare_input_urb(struct snd_usb_midi2_endpoint *ep,
+ struct urb *urb)
+{
+ urb->transfer_buffer_length = ep->packets;
+ return 0;
+}
+
+static void submit_input_urbs_locked(struct snd_usb_midi2_endpoint *ep)
+{
+ do_submit_urbs_locked(ep, prepare_input_urb);
+}
+
+/* URB completion for input; copy into rawmidi buffer and resubmit */
+static void input_urb_complete(struct urb *urb)
+{
+ struct snd_usb_midi2_urb *ctx = urb->context;
+ struct snd_usb_midi2_endpoint *ep = ctx->ep;
+ unsigned long flags;
+ int len;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ if (ep->disconnected || urb->status < 0)
+ goto dequeue;
+ len = urb->actual_length;
+ len &= ~3; /* align UMP */
+ if (len > ep->packets)
+ len = ep->packets;
+ if (len > 0) {
+ le32_to_cpu_array((u32 *)urb->transfer_buffer, len >> 2);
+ snd_ump_receive(ep->ump, (u32 *)urb->transfer_buffer, len);
+ }
+ dequeue:
+ set_bit(ctx->index, &ep->urb_free);
+ submit_input_urbs_locked(ep);
+ if (ep->urb_free == ep->urb_free_mask)
+ wake_up(&ep->wait);
+ spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/* URB submission helper; for both direction */
+static void submit_io_urbs(struct snd_usb_midi2_endpoint *ep)
+{
+ unsigned long flags;
+
+ if (!ep)
+ return;
+ spin_lock_irqsave(&ep->lock, flags);
+ if (ep->direction == STR_IN)
+ submit_input_urbs_locked(ep);
+ else
+ submit_output_urbs_locked(ep);
+ spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/* kill URBs for close, suspend and disconnect */
+static void kill_midi_urbs(struct snd_usb_midi2_endpoint *ep, bool suspending)
+{
+ int i;
+
+ if (!ep)
+ return;
+ if (suspending)
+ ep->suspended = ep->running;
+ atomic_set(&ep->running, 0);
+ for (i = 0; i < ep->num_urbs; i++) {
+ if (!ep->urbs[i].urb)
+ break;
+ usb_kill_urb(ep->urbs[i].urb);
+ }
+}
+
+/* wait until all URBs get freed */
+static void drain_urb_queue(struct snd_usb_midi2_endpoint *ep)
+{
+ if (!ep)
+ return;
+ spin_lock_irq(&ep->lock);
+ atomic_set(&ep->running, 0);
+ wait_event_lock_irq_timeout(ep->wait,
+ ep->disconnected ||
+ ep->urb_free == ep->urb_free_mask,
+ ep->lock, msecs_to_jiffies(500));
+ spin_unlock_irq(&ep->lock);
+}
+
+/* release URBs for an EP */
+static void free_midi_urbs(struct snd_usb_midi2_endpoint *ep)
+{
+ struct snd_usb_midi2_urb *ctx;
+ int i;
+
+ if (!ep)
+ return;
+ for (i = 0; i < ep->num_urbs; ++i) {
+ ctx = &ep->urbs[i];
+ if (!ctx->urb)
+ break;
+ usb_free_coherent(ep->dev, ep->packets,
+ ctx->urb->transfer_buffer,
+ ctx->urb->transfer_dma);
+ usb_free_urb(ctx->urb);
+ ctx->urb = NULL;
+ }
+ ep->num_urbs = 0;
+}
+
+/* allocate URBs for an EP */
+static int alloc_midi_urbs(struct snd_usb_midi2_endpoint *ep)
+{
+ struct snd_usb_midi2_urb *ctx;
+ void (*comp)(struct urb *urb);
+ void *buffer;
+ int i, err;
+ int endpoint, len;
+
+ endpoint = ep->endpoint;
+ len = ep->packets;
+ if (ep->direction == STR_IN)
+ comp = input_urb_complete;
+ else
+ comp = output_urb_complete;
+
+ ep->num_urbs = 0;
+ ep->urb_free = ep->urb_free_mask = 0;
+ for (i = 0; i < NUM_URBS; i++) {
+ ctx = &ep->urbs[i];
+ ctx->index = i;
+ ctx->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ctx->urb) {
+ dev_err(&ep->dev->dev, "URB alloc failed\n");
+ return -ENOMEM;
+ }
+ ctx->ep = ep;
+ buffer = usb_alloc_coherent(ep->dev, len, GFP_KERNEL,
+ &ctx->urb->transfer_dma);
+ if (!buffer) {
+ dev_err(&ep->dev->dev,
+ "URB buffer alloc failed (size %d)\n", len);
+ return -ENOMEM;
+ }
+ if (ep->interval)
+ usb_fill_int_urb(ctx->urb, ep->dev, ep->pipe,
+ buffer, len, comp, ctx, ep->interval);
+ else
+ usb_fill_bulk_urb(ctx->urb, ep->dev, ep->pipe,
+ buffer, len, comp, ctx);
+ err = usb_urb_ep_type_check(ctx->urb);
+ if (err < 0) {
+ dev_err(&ep->dev->dev, "invalid MIDI EP %x\n",
+ endpoint);
+ return err;
+ }
+ ctx->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ ep->num_urbs++;
+ }
+ ep->urb_free = ep->urb_free_mask = GENMASK(ep->num_urbs - 1, 0);
+ return 0;
+}
+
+static struct snd_usb_midi2_endpoint *
+ump_to_endpoint(struct snd_ump_endpoint *ump, int dir)
+{
+ struct snd_usb_midi2_ump *rmidi = ump->private_data;
+
+ return rmidi->eps[dir];
+}
+
+/* ump open callback */
+static int snd_usb_midi_v2_open(struct snd_ump_endpoint *ump, int dir)
+{
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
+ int err = 0;
+
+ if (!ep || !ep->endpoint)
+ return -ENODEV;
+ if (ep->disconnected)
+ return -EIO;
+ if (ep->direction == STR_OUT) {
+ err = alloc_midi_urbs(ep);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/* ump close callback */
+static void snd_usb_midi_v2_close(struct snd_ump_endpoint *ump, int dir)
+{
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
+
+ if (ep->direction == STR_OUT) {
+ kill_midi_urbs(ep, false);
+ drain_urb_queue(ep);
+ free_midi_urbs(ep);
+ }
+}
+
+/* ump trigger callback */
+static void snd_usb_midi_v2_trigger(struct snd_ump_endpoint *ump, int dir,
+ int up)
+{
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
+
+ atomic_set(&ep->running, up);
+ if (up && ep->direction == STR_OUT && !ep->disconnected)
+ submit_io_urbs(ep);
+}
+
+/* ump drain callback */
+static void snd_usb_midi_v2_drain(struct snd_ump_endpoint *ump, int dir)
+{
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
+
+ drain_urb_queue(ep);
+}
+
+/* allocate and start all input streams */
+static int start_input_streams(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_endpoint *ep;
+ int err;
+
+ list_for_each_entry(ep, &umidi->ep_list, list) {
+ if (ep->direction == STR_IN) {
+ err = alloc_midi_urbs(ep);
+ if (err < 0)
+ goto error;
+ }
+ }
+
+ list_for_each_entry(ep, &umidi->ep_list, list) {
+ if (ep->direction == STR_IN)
+ submit_io_urbs(ep);
+ }
+
+ return 0;
+
+ error:
+ list_for_each_entry(ep, &umidi->ep_list, list) {
+ if (ep->direction == STR_IN)
+ free_midi_urbs(ep);
+ }
+
+ return err;
+}
+
+static const struct snd_ump_ops snd_usb_midi_v2_ump_ops = {
+ .open = snd_usb_midi_v2_open,
+ .close = snd_usb_midi_v2_close,
+ .trigger = snd_usb_midi_v2_trigger,
+ .drain = snd_usb_midi_v2_drain,
+};
+
+/* create a USB MIDI 2.0 endpoint object */
+static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi,
+ struct usb_host_endpoint *hostep,
+ const struct usb_ms20_endpoint_descriptor *ms_ep)
+{
+ struct snd_usb_midi2_endpoint *ep;
+ int endpoint, dir;
+
+ usb_audio_dbg(umidi->chip, "Creating an EP 0x%02x, #GTB=%d\n",
+ hostep->desc.bEndpointAddress,
+ ms_ep->bNumGrpTrmBlock);
+
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ spin_lock_init(&ep->lock);
+ init_waitqueue_head(&ep->wait);
+ ep->dev = umidi->chip->dev;
+ endpoint = hostep->desc.bEndpointAddress;
+ dir = (endpoint & USB_DIR_IN) ? STR_IN : STR_OUT;
+
+ ep->endpoint = endpoint;
+ ep->direction = dir;
+ ep->ms_ep = ms_ep;
+ if (usb_endpoint_xfer_int(&hostep->desc))
+ ep->interval = hostep->desc.bInterval;
+ else
+ ep->interval = 0;
+ if (dir == STR_IN) {
+ if (ep->interval)
+ ep->pipe = usb_rcvintpipe(ep->dev, endpoint);
+ else
+ ep->pipe = usb_rcvbulkpipe(ep->dev, endpoint);
+ } else {
+ if (ep->interval)
+ ep->pipe = usb_sndintpipe(ep->dev, endpoint);
+ else
+ ep->pipe = usb_sndbulkpipe(ep->dev, endpoint);
+ }
+ ep->packets = usb_maxpacket(ep->dev, ep->pipe);
+ list_add_tail(&ep->list, &umidi->ep_list);
+
+ return 0;
+}
+
+/* destructor for endpoint; from snd_usb_midi_v2_free() */
+static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep)
+{
+ list_del(&ep->list);
+ free_midi_urbs(ep);
+ kfree(ep);
+}
+
+/* call all endpoint destructors */
+static void free_all_midi2_endpoints(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_endpoint *ep;
+
+ while (!list_empty(&umidi->ep_list)) {
+ ep = list_first_entry(&umidi->ep_list,
+ struct snd_usb_midi2_endpoint, list);
+ free_midi2_endpoint(ep);
+ }
+}
+
+/* find a MIDI STREAMING descriptor with a given subtype */
+static void *find_usb_ms_endpoint_descriptor(struct usb_host_endpoint *hostep,
+ unsigned char subtype)
+{
+ unsigned char *extra = hostep->extra;
+ int extralen = hostep->extralen;
+
+ while (extralen > 3) {
+ struct usb_ms_endpoint_descriptor *ms_ep =
+ (struct usb_ms_endpoint_descriptor *)extra;
+
+ if (ms_ep->bLength > 3 &&
+ ms_ep->bDescriptorType == USB_DT_CS_ENDPOINT &&
+ ms_ep->bDescriptorSubtype == subtype)
+ return ms_ep;
+ if (!extra[0])
+ break;
+ extralen -= extra[0];
+ extra += extra[0];
+ }
+ return NULL;
+}
+
+/* get the full group terminal block descriptors and return the size */
+static int get_group_terminal_block_descs(struct snd_usb_midi2_interface *umidi)
+{
+ struct usb_host_interface *hostif = umidi->hostif;
+ struct usb_device *dev = umidi->chip->dev;
+ struct usb_ms20_gr_trm_block_header_descriptor header = { 0 };
+ unsigned char *data;
+ int err, size;
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_STANDARD | USB_DIR_IN,
+ USB_DT_CS_GR_TRM_BLOCK << 8 | hostif->desc.bAlternateSetting,
+ hostif->desc.bInterfaceNumber,
+ &header, sizeof(header));
+ if (err < 0)
+ return err;
+ size = __le16_to_cpu(header.wTotalLength);
+ if (!size) {
+ dev_err(&dev->dev, "Failed to get GTB descriptors for %d:%d\n",
+ hostif->desc.bInterfaceNumber, hostif->desc.bAlternateSetting);
+ return -EINVAL;
+ }
+
+ data = kzalloc(size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_STANDARD | USB_DIR_IN,
+ USB_DT_CS_GR_TRM_BLOCK << 8 | hostif->desc.bAlternateSetting,
+ hostif->desc.bInterfaceNumber, data, size);
+ if (err < 0) {
+ kfree(data);
+ return err;
+ }
+
+ umidi->blk_descs = data;
+ umidi->blk_desc_size = size;
+ return 0;
+}
+
+/* find the corresponding group terminal block descriptor */
+static const struct usb_ms20_gr_trm_block_descriptor *
+find_group_terminal_block(struct snd_usb_midi2_interface *umidi, int id)
+{
+ const unsigned char *data = umidi->blk_descs;
+ int size = umidi->blk_desc_size;
+ const struct usb_ms20_gr_trm_block_descriptor *desc;
+
+ size -= sizeof(struct usb_ms20_gr_trm_block_header_descriptor);
+ data += sizeof(struct usb_ms20_gr_trm_block_header_descriptor);
+ while (size > 0 && *data && *data <= size) {
+ desc = (const struct usb_ms20_gr_trm_block_descriptor *)data;
+ if (desc->bLength >= sizeof(*desc) &&
+ desc->bDescriptorType == USB_DT_CS_GR_TRM_BLOCK &&
+ desc->bDescriptorSubtype == USB_MS_GR_TRM_BLOCK &&
+ desc->bGrpTrmBlkID == id)
+ return desc;
+ size -= *data;
+ data += *data;
+ }
+
+ return NULL;
+}
+
+/* fill up the information from GTB */
+static int parse_group_terminal_block(struct snd_usb_midi2_ump *rmidi,
+ const struct usb_ms20_gr_trm_block_descriptor *desc)
+{
+ struct snd_ump_endpoint *ump = rmidi->ump;
+ unsigned int protocol, protocol_caps;
+
+ /* set default protocol */
+ switch (desc->bMIDIProtocol) {
+ case USB_MS_MIDI_PROTO_1_0_64:
+ case USB_MS_MIDI_PROTO_1_0_64_JRTS:
+ case USB_MS_MIDI_PROTO_1_0_128:
+ case USB_MS_MIDI_PROTO_1_0_128_JRTS:
+ protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ break;
+ case USB_MS_MIDI_PROTO_2_0:
+ case USB_MS_MIDI_PROTO_2_0_JRTS:
+ protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ break;
+ default:
+ return 0;
+ }
+
+ if (ump->info.protocol && ump->info.protocol != protocol)
+ usb_audio_info(rmidi->umidi->chip,
+ "Overriding preferred MIDI protocol in GTB %d: %x -> %x\n",
+ rmidi->usb_block_id, ump->info.protocol,
+ protocol);
+ ump->info.protocol = protocol;
+
+ protocol_caps = protocol;
+ switch (desc->bMIDIProtocol) {
+ case USB_MS_MIDI_PROTO_1_0_64_JRTS:
+ case USB_MS_MIDI_PROTO_1_0_128_JRTS:
+ case USB_MS_MIDI_PROTO_2_0_JRTS:
+ protocol_caps |= SNDRV_UMP_EP_INFO_PROTO_JRTS_TX |
+ SNDRV_UMP_EP_INFO_PROTO_JRTS_RX;
+ break;
+ }
+
+ if (ump->info.protocol_caps && ump->info.protocol_caps != protocol_caps)
+ usb_audio_info(rmidi->umidi->chip,
+ "Overriding MIDI protocol caps in GTB %d: %x -> %x\n",
+ rmidi->usb_block_id, ump->info.protocol_caps,
+ protocol_caps);
+ ump->info.protocol_caps = protocol_caps;
+
+ return 0;
+}
+
+/* allocate and parse for each assigned group terminal block */
+static int parse_group_terminal_blocks(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_ump *rmidi;
+ const struct usb_ms20_gr_trm_block_descriptor *desc;
+ int err;
+
+ err = get_group_terminal_block_descs(umidi);
+ if (err < 0)
+ return err;
+ if (!umidi->blk_descs)
+ return 0;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ desc = find_group_terminal_block(umidi, rmidi->usb_block_id);
+ if (!desc)
+ continue;
+ err = parse_group_terminal_block(rmidi, desc);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* parse endpoints included in the given interface and create objects */
+static int parse_midi_2_0_endpoints(struct snd_usb_midi2_interface *umidi)
+{
+ struct usb_host_interface *hostif = umidi->hostif;
+ struct usb_host_endpoint *hostep;
+ struct usb_ms20_endpoint_descriptor *ms_ep;
+ int i, err;
+
+ for (i = 0; i < hostif->desc.bNumEndpoints; i++) {
+ hostep = &hostif->endpoint[i];
+ if (!usb_endpoint_xfer_bulk(&hostep->desc) &&
+ !usb_endpoint_xfer_int(&hostep->desc))
+ continue;
+ ms_ep = find_usb_ms_endpoint_descriptor(hostep, USB_MS_GENERAL_2_0);
+ if (!ms_ep)
+ continue;
+ if (ms_ep->bLength <= sizeof(*ms_ep))
+ continue;
+ if (!ms_ep->bNumGrpTrmBlock)
+ continue;
+ if (ms_ep->bLength < sizeof(*ms_ep) + ms_ep->bNumGrpTrmBlock)
+ continue;
+ err = create_midi2_endpoint(umidi, hostep, ms_ep);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_ump *rmidi;
+
+ while (!list_empty(&umidi->rawmidi_list)) {
+ rmidi = list_first_entry(&umidi->rawmidi_list,
+ struct snd_usb_midi2_ump, list);
+ list_del(&rmidi->list);
+ kfree(rmidi);
+ }
+}
+
+static int create_midi2_ump(struct snd_usb_midi2_interface *umidi,
+ struct snd_usb_midi2_endpoint *ep_in,
+ struct snd_usb_midi2_endpoint *ep_out,
+ int blk_id)
+{
+ struct snd_usb_midi2_ump *rmidi;
+ struct snd_ump_endpoint *ump;
+ int input, output;
+ char idstr[16];
+ int err;
+
+ rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
+ if (!rmidi)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&rmidi->list);
+ rmidi->dev = umidi->chip->dev;
+ rmidi->umidi = umidi;
+ rmidi->usb_block_id = blk_id;
+
+ rmidi->index = umidi->chip->num_rawmidis;
+ snprintf(idstr, sizeof(idstr), "UMP %d", rmidi->index);
+ input = ep_in ? 1 : 0;
+ output = ep_out ? 1 : 0;
+ err = snd_ump_endpoint_new(umidi->chip->card, idstr, rmidi->index,
+ output, input, &ump);
+ if (err < 0) {
+ usb_audio_dbg(umidi->chip, "Failed to create a UMP object\n");
+ kfree(rmidi);
+ return err;
+ }
+
+ rmidi->ump = ump;
+ umidi->chip->num_rawmidis++;
+
+ ump->private_data = rmidi;
+ ump->ops = &snd_usb_midi_v2_ump_ops;
+
+ rmidi->eps[STR_IN] = ep_in;
+ rmidi->eps[STR_OUT] = ep_out;
+ if (ep_in) {
+ ep_in->pair = ep_out;
+ ep_in->rmidi = rmidi;
+ ep_in->ump = ump;
+ }
+ if (ep_out) {
+ ep_out->pair = ep_in;
+ ep_out->rmidi = rmidi;
+ ep_out->ump = ump;
+ }
+
+ list_add_tail(&rmidi->list, &umidi->rawmidi_list);
+ return 0;
+}
+
+/* find the UMP EP with the given USB block id */
+static struct snd_usb_midi2_ump *
+find_midi2_ump(struct snd_usb_midi2_interface *umidi, int blk_id)
+{
+ struct snd_usb_midi2_ump *rmidi;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ if (rmidi->usb_block_id == blk_id)
+ return rmidi;
+ }
+ return NULL;
+}
+
+/* look for the matching output endpoint and create UMP object if found */
+static int find_matching_ep_partner(struct snd_usb_midi2_interface *umidi,
+ struct snd_usb_midi2_endpoint *ep,
+ int blk_id)
+{
+ struct snd_usb_midi2_endpoint *pair_ep;
+ int blk;
+
+ usb_audio_dbg(umidi->chip, "Looking for a pair for EP-in 0x%02x\n",
+ ep->endpoint);
+ list_for_each_entry(pair_ep, &umidi->ep_list, list) {
+ if (pair_ep->direction != STR_OUT)
+ continue;
+ if (pair_ep->pair)
+ continue; /* already paired */
+ for (blk = 0; blk < pair_ep->ms_ep->bNumGrpTrmBlock; blk++) {
+ if (pair_ep->ms_ep->baAssoGrpTrmBlkID[blk] == blk_id) {
+ usb_audio_dbg(umidi->chip,
+ "Found a match with EP-out 0x%02x blk %d\n",
+ pair_ep->endpoint, blk);
+ return create_midi2_ump(umidi, ep, pair_ep, blk_id);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Call UMP helper to parse UMP endpoints;
+ * this needs to be called after starting the input streams for bi-directional
+ * communications
+ */
+static int parse_ump_endpoints(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_ump *rmidi;
+ int err;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ if (!rmidi->ump ||
+ !(rmidi->ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
+ continue;
+ err = snd_ump_parse_endpoint(rmidi->ump);
+ if (!err) {
+ rmidi->ump_parsed = true;
+ } else {
+ if (err == -ENOMEM)
+ return err;
+ /* fall back to GTB later */
+ }
+ }
+ return 0;
+}
+
+/* create a UMP block from a GTB entry */
+static int create_gtb_block(struct snd_usb_midi2_ump *rmidi, int dir, int blk)
+{
+ struct snd_usb_midi2_interface *umidi = rmidi->umidi;
+ const struct usb_ms20_gr_trm_block_descriptor *desc;
+ struct snd_ump_block *fb;
+ int type, err;
+
+ desc = find_group_terminal_block(umidi, blk);
+ if (!desc)
+ return 0;
+
+ usb_audio_dbg(umidi->chip,
+ "GTB %d: type=%d, group=%d/%d, protocol=%d, in bw=%d, out bw=%d\n",
+ blk, desc->bGrpTrmBlkType, desc->nGroupTrm,
+ desc->nNumGroupTrm, desc->bMIDIProtocol,
+ __le16_to_cpu(desc->wMaxInputBandwidth),
+ __le16_to_cpu(desc->wMaxOutputBandwidth));
+
+ /* assign the direction */
+ switch (desc->bGrpTrmBlkType) {
+ case USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL:
+ type = SNDRV_UMP_DIR_BIDIRECTION;
+ break;
+ case USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY:
+ type = SNDRV_UMP_DIR_INPUT;
+ break;
+ case USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY:
+ type = SNDRV_UMP_DIR_OUTPUT;
+ break;
+ default:
+ usb_audio_dbg(umidi->chip, "Unsupported GTB type %d\n",
+ desc->bGrpTrmBlkType);
+ return 0; /* unsupported */
+ }
+
+ /* guess work: set blk-1 as the (0-based) block ID */
+ err = snd_ump_block_new(rmidi->ump, blk - 1, type,
+ desc->nGroupTrm, desc->nNumGroupTrm,
+ &fb);
+ if (err == -EBUSY)
+ return 0; /* already present */
+ else if (err)
+ return err;
+
+ if (desc->iBlockItem)
+ usb_string(rmidi->dev, desc->iBlockItem,
+ fb->info.name, sizeof(fb->info.name));
+
+ if (__le16_to_cpu(desc->wMaxInputBandwidth) == 1 ||
+ __le16_to_cpu(desc->wMaxOutputBandwidth) == 1)
+ fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1 |
+ SNDRV_UMP_BLOCK_IS_LOWSPEED;
+
+ usb_audio_dbg(umidi->chip,
+ "Created a UMP block %d from GTB, name=%s\n",
+ blk, fb->info.name);
+ return 0;
+}
+
+/* Create UMP blocks for each UMP EP */
+static int create_blocks_from_gtb(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_ump *rmidi;
+ int i, blk, err, dir;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ if (!rmidi->ump)
+ continue;
+ /* Blocks have been already created? */
+ if (rmidi->ump_parsed || rmidi->ump->info.num_blocks)
+ continue;
+ /* GTB is static-only */
+ rmidi->ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
+ /* loop over GTBs */
+ for (dir = 0; dir < 2; dir++) {
+ if (!rmidi->eps[dir])
+ continue;
+ for (i = 0; i < rmidi->eps[dir]->ms_ep->bNumGrpTrmBlock; i++) {
+ blk = rmidi->eps[dir]->ms_ep->baAssoGrpTrmBlkID[i];
+ err = create_gtb_block(rmidi, dir, blk);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* attach legacy rawmidis */
+static int attach_legacy_rawmidi(struct snd_usb_midi2_interface *umidi)
+{
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ struct snd_usb_midi2_ump *rmidi;
+ int err;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ err = snd_ump_attach_legacy_rawmidi(rmidi->ump,
+ "Legacy MIDI",
+ umidi->chip->num_rawmidis);
+ if (err < 0)
+ return err;
+ umidi->chip->num_rawmidis++;
+ }
+#endif
+ return 0;
+}
+
+static void snd_usb_midi_v2_free(struct snd_usb_midi2_interface *umidi)
+{
+ free_all_midi2_endpoints(umidi);
+ free_all_midi2_umps(umidi);
+ list_del(&umidi->list);
+ kfree(umidi->blk_descs);
+ kfree(umidi);
+}
+
+/* parse the interface for MIDI 2.0 */
+static int parse_midi_2_0(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_endpoint *ep;
+ int blk, id, err;
+
+ /* First, create an object for each USB MIDI Endpoint */
+ err = parse_midi_2_0_endpoints(umidi);
+ if (err < 0)
+ return err;
+ if (list_empty(&umidi->ep_list)) {
+ usb_audio_warn(umidi->chip, "No MIDI endpoints found\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Next, look for EP I/O pairs that are found in group terminal blocks
+ * A UMP object is created for each EP I/O pair as bidirecitonal
+ * UMP EP
+ */
+ list_for_each_entry(ep, &umidi->ep_list, list) {
+ /* only input in this loop; output is matched in find_midi_ump() */
+ if (ep->direction != STR_IN)
+ continue;
+ for (blk = 0; blk < ep->ms_ep->bNumGrpTrmBlock; blk++) {
+ id = ep->ms_ep->baAssoGrpTrmBlkID[blk];
+ err = find_matching_ep_partner(umidi, ep, id);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /*
+ * For the remaining EPs, treat as singles, create a UMP object with
+ * unidirectional EP
+ */
+ list_for_each_entry(ep, &umidi->ep_list, list) {
+ if (ep->rmidi)
+ continue; /* already paired */
+ for (blk = 0; blk < ep->ms_ep->bNumGrpTrmBlock; blk++) {
+ id = ep->ms_ep->baAssoGrpTrmBlkID[blk];
+ if (find_midi2_ump(umidi, id))
+ continue;
+ usb_audio_dbg(umidi->chip,
+ "Creating a unidirection UMP for EP=0x%02x, blk=%d\n",
+ ep->endpoint, id);
+ if (ep->direction == STR_IN)
+ err = create_midi2_ump(umidi, ep, NULL, id);
+ else
+ err = create_midi2_ump(umidi, NULL, ep, id);
+ if (err < 0)
+ return err;
+ break;
+ }
+ }
+
+ return attach_legacy_rawmidi(umidi);
+}
+
+/* is the given interface for MIDI 2.0? */
+static bool is_midi2_altset(struct usb_host_interface *hostif)
+{
+ struct usb_ms_header_descriptor *ms_header =
+ (struct usb_ms_header_descriptor *)hostif->extra;
+
+ if (hostif->extralen < 7 ||
+ ms_header->bLength < 7 ||
+ ms_header->bDescriptorType != USB_DT_CS_INTERFACE ||
+ ms_header->bDescriptorSubtype != UAC_HEADER)
+ return false;
+
+ return le16_to_cpu(ms_header->bcdMSC) == USB_MS_REV_MIDI_2_0;
+}
+
+/* change the altsetting */
+static int set_altset(struct snd_usb_midi2_interface *umidi)
+{
+ usb_audio_dbg(umidi->chip, "Setting host iface %d:%d\n",
+ umidi->hostif->desc.bInterfaceNumber,
+ umidi->hostif->desc.bAlternateSetting);
+ return usb_set_interface(umidi->chip->dev,
+ umidi->hostif->desc.bInterfaceNumber,
+ umidi->hostif->desc.bAlternateSetting);
+}
+
+/* fill UMP Endpoint name string from USB descriptor */
+static void fill_ump_ep_name(struct snd_ump_endpoint *ump,
+ struct usb_device *dev, int id)
+{
+ int len;
+
+ usb_string(dev, id, ump->info.name, sizeof(ump->info.name));
+
+ /* trim superfluous "MIDI" suffix */
+ len = strlen(ump->info.name);
+ if (len > 5 && !strcmp(ump->info.name + len - 5, " MIDI"))
+ ump->info.name[len - 5] = 0;
+}
+
+/* fill the fallback name string for each rawmidi instance */
+static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi)
+{
+ struct usb_device *dev = umidi->chip->dev;
+ struct snd_usb_midi2_ump *rmidi;
+ struct snd_ump_endpoint *ump;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ ump = rmidi->ump;
+ /* fill UMP EP name from USB descriptors */
+ if (!*ump->info.name && umidi->hostif->desc.iInterface)
+ fill_ump_ep_name(ump, dev, umidi->hostif->desc.iInterface);
+ else if (!*ump->info.name && dev->descriptor.iProduct)
+ fill_ump_ep_name(ump, dev, dev->descriptor.iProduct);
+ /* fill fallback name */
+ if (!*ump->info.name)
+ sprintf(ump->info.name, "USB MIDI %d", rmidi->index);
+ /* copy as rawmidi name if not set */
+ if (!*ump->core.name)
+ strscpy(ump->core.name, ump->info.name,
+ sizeof(ump->core.name));
+ /* use serial number string as unique UMP product id */
+ if (!*ump->info.product_id && dev->descriptor.iSerialNumber)
+ usb_string(dev, dev->descriptor.iSerialNumber,
+ ump->info.product_id,
+ sizeof(ump->info.product_id));
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ if (ump->legacy_rmidi && !*ump->legacy_rmidi->name)
+ snprintf(ump->legacy_rmidi->name,
+ sizeof(ump->legacy_rmidi->name),
+ "%s (MIDI 1.0)", ump->info.name);
+#endif
+ }
+}
+
+/* create MIDI interface; fallback to MIDI 1.0 if needed */
+int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id)
+{
+ struct snd_usb_midi2_interface *umidi;
+ struct usb_host_interface *hostif;
+ int err;
+
+ usb_audio_dbg(chip, "Parsing interface %d...\n",
+ iface->altsetting[0].desc.bInterfaceNumber);
+
+ /* fallback to MIDI 1.0? */
+ if (!midi2_enable) {
+ usb_audio_info(chip, "Falling back to MIDI 1.0 by module option\n");
+ goto fallback_to_midi1;
+ }
+ if ((quirk && quirk->type != QUIRK_MIDI_STANDARD_INTERFACE) ||
+ iface->num_altsetting < 2) {
+ usb_audio_info(chip, "Quirk or no altest; falling back to MIDI 1.0\n");
+ goto fallback_to_midi1;
+ }
+ hostif = &iface->altsetting[1];
+ if (!is_midi2_altset(hostif)) {
+ usb_audio_info(chip, "No MIDI 2.0 at altset 1, falling back to MIDI 1.0\n");
+ goto fallback_to_midi1;
+ }
+ if (!hostif->desc.bNumEndpoints) {
+ usb_audio_info(chip, "No endpoint at altset 1, falling back to MIDI 1.0\n");
+ goto fallback_to_midi1;
+ }
+
+ usb_audio_dbg(chip, "Creating a MIDI 2.0 instance for %d:%d\n",
+ hostif->desc.bInterfaceNumber,
+ hostif->desc.bAlternateSetting);
+
+ umidi = kzalloc(sizeof(*umidi), GFP_KERNEL);
+ if (!umidi)
+ return -ENOMEM;
+ umidi->chip = chip;
+ umidi->iface = iface;
+ umidi->hostif = hostif;
+ INIT_LIST_HEAD(&umidi->rawmidi_list);
+ INIT_LIST_HEAD(&umidi->ep_list);
+
+ list_add_tail(&umidi->list, &chip->midi_v2_list);
+
+ err = set_altset(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to set altset\n");
+ goto error;
+ }
+
+ /* assume only altset 1 corresponding to MIDI 2.0 interface */
+ err = parse_midi_2_0(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to parse MIDI 2.0 interface\n");
+ goto error;
+ }
+
+ /* parse USB group terminal blocks */
+ err = parse_group_terminal_blocks(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to parse GTB\n");
+ goto error;
+ }
+
+ err = start_input_streams(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to start input streams\n");
+ goto error;
+ }
+
+ if (midi2_ump_probe) {
+ err = parse_ump_endpoints(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to parse UMP endpoint\n");
+ goto error;
+ }
+ }
+
+ err = create_blocks_from_gtb(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to create GTB blocks\n");
+ goto error;
+ }
+
+ set_fallback_rawmidi_names(umidi);
+ return 0;
+
+ error:
+ snd_usb_midi_v2_free(umidi);
+ return err;
+
+ fallback_to_midi1:
+ return __snd_usbmidi_create(chip->card, iface, &chip->midi_list,
+ quirk, usb_id, &chip->num_rawmidis);
+}
+
+static void suspend_midi2_endpoint(struct snd_usb_midi2_endpoint *ep)
+{
+ kill_midi_urbs(ep, true);
+ drain_urb_queue(ep);
+}
+
+void snd_usb_midi_v2_suspend_all(struct snd_usb_audio *chip)
+{
+ struct snd_usb_midi2_interface *umidi;
+ struct snd_usb_midi2_endpoint *ep;
+
+ list_for_each_entry(umidi, &chip->midi_v2_list, list) {
+ list_for_each_entry(ep, &umidi->ep_list, list)
+ suspend_midi2_endpoint(ep);
+ }
+}
+
+static void resume_midi2_endpoint(struct snd_usb_midi2_endpoint *ep)
+{
+ ep->running = ep->suspended;
+ if (ep->direction == STR_IN)
+ submit_io_urbs(ep);
+ /* FIXME: does it all? */
+}
+
+void snd_usb_midi_v2_resume_all(struct snd_usb_audio *chip)
+{
+ struct snd_usb_midi2_interface *umidi;
+ struct snd_usb_midi2_endpoint *ep;
+
+ list_for_each_entry(umidi, &chip->midi_v2_list, list) {
+ set_altset(umidi);
+ list_for_each_entry(ep, &umidi->ep_list, list)
+ resume_midi2_endpoint(ep);
+ }
+}
+
+void snd_usb_midi_v2_disconnect_all(struct snd_usb_audio *chip)
+{
+ struct snd_usb_midi2_interface *umidi;
+ struct snd_usb_midi2_endpoint *ep;
+
+ list_for_each_entry(umidi, &chip->midi_v2_list, list) {
+ umidi->disconnected = 1;
+ list_for_each_entry(ep, &umidi->ep_list, list) {
+ ep->disconnected = 1;
+ kill_midi_urbs(ep, false);
+ drain_urb_queue(ep);
+ }
+ }
+}
+
+/* release the MIDI instance */
+void snd_usb_midi_v2_free_all(struct snd_usb_audio *chip)
+{
+ struct snd_usb_midi2_interface *umidi, *next;
+
+ list_for_each_entry_safe(umidi, next, &chip->midi_v2_list, list)
+ snd_usb_midi_v2_free(umidi);
+}
diff --git a/sound/usb/midi2.h b/sound/usb/midi2.h
new file mode 100644
index 000000000000..94a65fcbd58b
--- /dev/null
+++ b/sound/usb/midi2.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef __USB_AUDIO_MIDI2_H
+#define __USB_AUDIO_MIDI2_H
+
+#include "midi.h"
+
+#if IS_ENABLED(CONFIG_SND_USB_AUDIO_MIDI_V2)
+int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id);
+void snd_usb_midi_v2_suspend_all(struct snd_usb_audio *chip);
+void snd_usb_midi_v2_resume_all(struct snd_usb_audio *chip);
+void snd_usb_midi_v2_disconnect_all(struct snd_usb_audio *chip);
+void snd_usb_midi_v2_free_all(struct snd_usb_audio *chip);
+#else /* CONFIG_SND_USB_AUDIO_MIDI_V2 */
+/* fallback to MIDI 1.0 creation */
+static inline int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id)
+{
+ return __snd_usbmidi_create(chip->card, iface, &chip->midi_list,
+ quirk, usb_id, &chip->num_rawmidis);
+}
+
+static inline void snd_usb_midi_v2_suspend_all(struct snd_usb_audio *chip) {}
+static inline void snd_usb_midi_v2_resume_all(struct snd_usb_audio *chip) {}
+static inline void snd_usb_midi_v2_disconnect_all(struct snd_usb_audio *chip) {}
+static inline void snd_usb_midi_v2_free_all(struct snd_usb_audio *chip) {}
+#endif /* CONFIG_SND_USB_AUDIO_MIDI_V2 */
+
+#endif /* __USB_AUDIO_MIDI2_H */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 6cf55b7f7a04..30bcb80b1cb8 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -19,6 +19,7 @@
#include "mixer.h"
#include "mixer_quirks.h"
#include "midi.h"
+#include "midi2.h"
#include "quirks.h"
#include "helper.h"
#include "endpoint.h"
@@ -80,7 +81,7 @@ static int create_any_midi_quirk(struct snd_usb_audio *chip,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
- return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
+ return snd_usb_midi_v2_create(chip, intf, quirk, 0);
}
/*
@@ -436,8 +437,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
chip->usb_id == USB_ID(0x0582, 0x002b)
? &ua700_quirk : &uaxx_quirk;
return __snd_usbmidi_create(chip->card, iface,
- &chip->midi_list, quirk,
- chip->usb_id);
+ &chip->midi_list, quirk,
+ chip->usb_id,
+ &chip->num_rawmidis);
}
if (altsd->bNumEndpoints != 1)
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 38a85b2c9a73..43d4029edab4 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -49,7 +49,9 @@ struct snd_usb_audio {
struct list_head clock_ref_list; /* list of clock refcounts */
int pcm_devs;
+ unsigned int num_rawmidis; /* number of created rawmidi devices */
struct list_head midi_list; /* list of midi interfaces */
+ struct list_head midi_v2_list; /* list of MIDI 2 interfaces */
struct list_head mixer_list; /* list of mixer interfaces */
diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile
index 901949db80ad..5af9ba8a4645 100644
--- a/tools/testing/selftests/alsa/Makefile
+++ b/tools/testing/selftests/alsa/Makefile
@@ -12,7 +12,7 @@ LDLIBS+=-lpthread
OVERRIDE_TARGETS = 1
-TEST_GEN_PROGS := mixer-test pcm-test
+TEST_GEN_PROGS := mixer-test pcm-test test-pcmtest-driver
TEST_GEN_PROGS_EXTENDED := libatest.so
diff --git a/tools/testing/selftests/alsa/test-pcmtest-driver.c b/tools/testing/selftests/alsa/test-pcmtest-driver.c
new file mode 100644
index 000000000000..71931b240a83
--- /dev/null
+++ b/tools/testing/selftests/alsa/test-pcmtest-driver.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This is the test which covers PCM middle layer data transferring using
+ * the virtual pcm test driver (snd-pcmtest).
+ *
+ * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com>
+ */
+#include <string.h>
+#include <alsa/asoundlib.h>
+#include "../kselftest_harness.h"
+
+#define CH_NUM 4
+
+struct pattern_buf {
+ char buf[1024];
+ int len;
+};
+
+struct pattern_buf patterns[CH_NUM];
+
+struct pcmtest_test_params {
+ unsigned long buffer_size;
+ unsigned long period_size;
+ unsigned long channels;
+ unsigned int rate;
+ snd_pcm_access_t access;
+ size_t sec_buf_len;
+ size_t sample_size;
+ int time;
+ snd_pcm_format_t format;
+};
+
+static int read_patterns(void)
+{
+ FILE *fp, *fpl;
+ int i;
+ char pf[64];
+ char plf[64];
+
+ for (i = 0; i < CH_NUM; i++) {
+ sprintf(plf, "/sys/kernel/debug/pcmtest/fill_pattern%d_len", i);
+ fpl = fopen(plf, "r");
+ if (!fpl)
+ return -1;
+ fscanf(fpl, "%u", &patterns[i].len);
+ fclose(fpl);
+
+ sprintf(pf, "/sys/kernel/debug/pcmtest/fill_pattern%d", i);
+ fp = fopen(pf, "r");
+ if (!fp) {
+ fclose(fpl);
+ return -1;
+ }
+ fread(patterns[i].buf, 1, patterns[i].len, fp);
+ fclose(fp);
+ }
+
+ return 0;
+}
+
+static int get_test_results(char *debug_name)
+{
+ int result;
+ FILE *f;
+ char fname[128];
+
+ sprintf(fname, "/sys/kernel/debug/pcmtest/%s", debug_name);
+
+ f = fopen(fname, "r");
+ if (!f) {
+ printf("Failed to open file\n");
+ return -1;
+ }
+ fscanf(f, "%d", &result);
+ fclose(f);
+
+ return result;
+}
+
+static size_t get_sec_buf_len(unsigned int rate, unsigned long channels, snd_pcm_format_t format)
+{
+ return rate * channels * snd_pcm_format_physical_width(format) / 8;
+}
+
+static int setup_handle(snd_pcm_t **handle, snd_pcm_sw_params_t *swparams,
+ snd_pcm_hw_params_t *hwparams, struct pcmtest_test_params *params,
+ int card, snd_pcm_stream_t stream)
+{
+ char pcm_name[32];
+ int err;
+
+ sprintf(pcm_name, "hw:%d,0,0", card);
+ err = snd_pcm_open(handle, pcm_name, stream, 0);
+ if (err < 0)
+ return err;
+ snd_pcm_hw_params_any(*handle, hwparams);
+ snd_pcm_hw_params_set_rate_resample(*handle, hwparams, 0);
+ snd_pcm_hw_params_set_access(*handle, hwparams, params->access);
+ snd_pcm_hw_params_set_format(*handle, hwparams, params->format);
+ snd_pcm_hw_params_set_channels(*handle, hwparams, params->channels);
+ snd_pcm_hw_params_set_rate_near(*handle, hwparams, &params->rate, 0);
+ snd_pcm_hw_params_set_period_size_near(*handle, hwparams, &params->period_size, 0);
+ snd_pcm_hw_params_set_buffer_size_near(*handle, hwparams, &params->buffer_size);
+ snd_pcm_hw_params(*handle, hwparams);
+ snd_pcm_sw_params_current(*handle, swparams);
+
+ snd_pcm_hw_params_set_rate_resample(*handle, hwparams, 0);
+ snd_pcm_sw_params_set_avail_min(*handle, swparams, params->period_size);
+ snd_pcm_hw_params_set_buffer_size_near(*handle, hwparams, &params->buffer_size);
+ snd_pcm_hw_params_set_period_size_near(*handle, hwparams, &params->period_size, 0);
+ snd_pcm_sw_params(*handle, swparams);
+ snd_pcm_hw_params(*handle, hwparams);
+
+ return 0;
+}
+
+FIXTURE(pcmtest) {
+ int card;
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_hw_params_t *hwparams;
+ struct pcmtest_test_params params;
+};
+
+FIXTURE_TEARDOWN(pcmtest) {
+}
+
+FIXTURE_SETUP(pcmtest) {
+ char *card_name;
+ int err;
+
+ if (geteuid())
+ SKIP(exit(-1), "This test needs root to run!");
+
+ err = read_patterns();
+ if (err)
+ SKIP(exit(-1), "Can't read patterns. Probably, module isn't loaded");
+
+ card_name = malloc(127);
+ ASSERT_NE(card_name, NULL);
+ self->params.buffer_size = 16384;
+ self->params.period_size = 4096;
+ self->params.channels = CH_NUM;
+ self->params.rate = 8000;
+ self->params.access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ self->params.format = SND_PCM_FORMAT_S16_LE;
+ self->card = -1;
+ self->params.sample_size = snd_pcm_format_physical_width(self->params.format) / 8;
+
+ self->params.sec_buf_len = get_sec_buf_len(self->params.rate, self->params.channels,
+ self->params.format);
+ self->params.time = 4;
+
+ while (snd_card_next(&self->card) >= 0) {
+ if (self->card == -1)
+ break;
+ snd_card_get_name(self->card, &card_name);
+ if (!strcmp(card_name, "PCM-Test"))
+ break;
+ }
+ free(card_name);
+ ASSERT_NE(self->card, -1);
+}
+
+/*
+ * Here we are trying to send the looped monotonically increasing sequence of bytes to the driver.
+ * If our data isn't corrupted, the driver will set the content of 'pc_test' debugfs file to '1'
+ */
+TEST_F(pcmtest, playback) {
+ snd_pcm_t *handle;
+ unsigned char *it;
+ size_t write_res;
+ int test_results;
+ int i, cur_ch, pos_in_ch;
+ void *samples;
+ struct pcmtest_test_params *params = &self->params;
+
+ samples = calloc(self->params.sec_buf_len * self->params.time, 1);
+ ASSERT_NE(samples, NULL);
+
+ snd_pcm_sw_params_alloca(&self->swparams);
+ snd_pcm_hw_params_alloca(&self->hwparams);
+
+ ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, params,
+ self->card, SND_PCM_STREAM_PLAYBACK), 0);
+ snd_pcm_format_set_silence(params->format, samples,
+ params->rate * params->channels * params->time);
+ it = samples;
+ for (i = 0; i < self->params.sec_buf_len * params->time; i++) {
+ cur_ch = (i / params->sample_size) % CH_NUM;
+ pos_in_ch = i / params->sample_size / CH_NUM * params->sample_size
+ + (i % params->sample_size);
+ it[i] = patterns[cur_ch].buf[pos_in_ch % patterns[cur_ch].len];
+ }
+ write_res = snd_pcm_writei(handle, samples, params->rate * params->time);
+ ASSERT_GE(write_res, 0);
+
+ snd_pcm_close(handle);
+ free(samples);
+ test_results = get_test_results("pc_test");
+ ASSERT_EQ(test_results, 1);
+}
+
+/*
+ * Here we test that the virtual alsa driver returns looped and monotonically increasing sequence
+ * of bytes. In the interleaved mode the buffer will contain samples in the following order:
+ * C0, C1, C2, C3, C0, C1, ...
+ */
+TEST_F(pcmtest, capture) {
+ snd_pcm_t *handle;
+ unsigned char *it;
+ size_t read_res;
+ int i, cur_ch, pos_in_ch;
+ void *samples;
+ struct pcmtest_test_params *params = &self->params;
+
+ samples = calloc(self->params.sec_buf_len * self->params.time, 1);
+ ASSERT_NE(samples, NULL);
+
+ snd_pcm_sw_params_alloca(&self->swparams);
+ snd_pcm_hw_params_alloca(&self->hwparams);
+
+ ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams,
+ params, self->card, SND_PCM_STREAM_CAPTURE), 0);
+ snd_pcm_format_set_silence(params->format, samples,
+ params->rate * params->channels * params->time);
+ read_res = snd_pcm_readi(handle, samples, params->rate * params->time);
+ ASSERT_GE(read_res, 0);
+ snd_pcm_close(handle);
+ it = (unsigned char *)samples;
+ for (i = 0; i < self->params.sec_buf_len * self->params.time; i++) {
+ cur_ch = (i / params->sample_size) % CH_NUM;
+ pos_in_ch = i / params->sample_size / CH_NUM * params->sample_size
+ + (i % params->sample_size);
+ ASSERT_EQ(it[i], patterns[cur_ch].buf[pos_in_ch % patterns[cur_ch].len]);
+ }
+ free(samples);
+}
+
+// Test capture in the non-interleaved access mode. The are buffers for each recorded channel
+TEST_F(pcmtest, ni_capture) {
+ snd_pcm_t *handle;
+ struct pcmtest_test_params params = self->params;
+ char **chan_samples;
+ size_t i, j, read_res;
+
+ chan_samples = calloc(CH_NUM, sizeof(*chan_samples));
+ ASSERT_NE(chan_samples, NULL);
+
+ snd_pcm_sw_params_alloca(&self->swparams);
+ snd_pcm_hw_params_alloca(&self->hwparams);
+
+ params.access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
+
+ ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams,
+ &params, self->card, SND_PCM_STREAM_CAPTURE), 0);
+
+ for (i = 0; i < CH_NUM; i++)
+ chan_samples[i] = calloc(params.sec_buf_len * params.time, 1);
+
+ for (i = 0; i < 1; i++) {
+ read_res = snd_pcm_readn(handle, (void **)chan_samples, params.rate * params.time);
+ ASSERT_GE(read_res, 0);
+ }
+ snd_pcm_close(handle);
+
+ for (i = 0; i < CH_NUM; i++) {
+ for (j = 0; j < params.rate * params.time; j++)
+ ASSERT_EQ(chan_samples[i][j], patterns[i].buf[j % patterns[i].len]);
+ free(chan_samples[i]);
+ }
+ free(chan_samples);
+}
+
+TEST_F(pcmtest, ni_playback) {
+ snd_pcm_t *handle;
+ struct pcmtest_test_params params = self->params;
+ char **chan_samples;
+ size_t i, j, read_res;
+ int test_res;
+
+ chan_samples = calloc(CH_NUM, sizeof(*chan_samples));
+ ASSERT_NE(chan_samples, NULL);
+
+ snd_pcm_sw_params_alloca(&self->swparams);
+ snd_pcm_hw_params_alloca(&self->hwparams);
+
+ params.access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
+
+ ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams,
+ &params, self->card, SND_PCM_STREAM_PLAYBACK), 0);
+
+ for (i = 0; i < CH_NUM; i++) {
+ chan_samples[i] = calloc(params.sec_buf_len * params.time, 1);
+ for (j = 0; j < params.sec_buf_len * params.time; j++)
+ chan_samples[i][j] = patterns[i].buf[j % patterns[i].len];
+ }
+
+ for (i = 0; i < 1; i++) {
+ read_res = snd_pcm_writen(handle, (void **)chan_samples, params.rate * params.time);
+ ASSERT_GE(read_res, 0);
+ }
+
+ snd_pcm_close(handle);
+ test_res = get_test_results("pc_test");
+ ASSERT_EQ(test_res, 1);
+
+ for (i = 0; i < CH_NUM; i++)
+ free(chan_samples[i]);
+ free(chan_samples);
+}
+
+/*
+ * Here we are testing the custom ioctl definition inside the virtual driver. If it triggers
+ * successfully, the driver sets the content of 'ioctl_test' debugfs file to '1'.
+ */
+TEST_F(pcmtest, reset_ioctl) {
+ snd_pcm_t *handle;
+ unsigned char *it;
+ int test_res;
+ struct pcmtest_test_params *params = &self->params;
+
+ snd_pcm_sw_params_alloca(&self->swparams);
+ snd_pcm_hw_params_alloca(&self->hwparams);
+
+ ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, params,
+ self->card, SND_PCM_STREAM_CAPTURE), 0);
+ snd_pcm_reset(handle);
+ test_res = get_test_results("ioctl_test");
+ ASSERT_EQ(test_res, 1);
+ snd_pcm_close(handle);
+}
+
+TEST_HARNESS_MAIN