summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/jack.c34
-rw-r--r--sound/core/pcm_memory.c3
-rw-r--r--sound/core/seq/seq_ports.c2
-rw-r--r--sound/drivers/Kconfig18
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/serial-generic.c374
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c2
-rw-r--r--sound/pci/ctxfi/ctatc.c2
-rw-r--r--sound/pci/ctxfi/cthardware.h3
-rw-r--r--sound/pci/ctxfi/cthw20k1.c2
-rw-r--r--sound/pci/echoaudio/midi.c1
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c5
-rw-r--r--sound/pci/hda/Kconfig2
-rw-r--r--sound/pci/hda/cs35l41_hda.c438
-rw-r--r--sound/pci/hda/cs35l41_hda.h31
-rw-r--r--sound/pci/hda/cs35l41_hda_spi.c2
-rw-r--r--sound/pci/hda/hda_codec.c11
-rw-r--r--sound/pci/hda/hda_component.h2
-rw-r--r--sound/pci/hda/hda_tegra.c2
-rw-r--r--sound/pci/hda/patch_conexant.c1
-rw-r--r--sound/pci/hda/patch_cs8409-tables.c346
-rw-r--r--sound/pci/hda/patch_cs8409.c361
-rw-r--r--sound/pci/hda/patch_cs8409.h14
-rw-r--r--sound/pci/hda/patch_realtek.c64
-rw-r--r--sound/pci/lola/lola_pcm.c3
-rw-r--r--sound/pci/rme9652/hdsp.c2
-rw-r--r--sound/soc/Kconfig9
-rw-r--r--sound/soc/Makefile5
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c4
-rw-r--r--sound/soc/amd/acp-rt5645.c2
-rw-r--r--sound/soc/amd/acp/Kconfig6
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c22
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c29
-rw-r--r--sound/soc/amd/acp/acp-mach.h9
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c22
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c2
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c10
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c42
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c9
-rw-r--r--sound/soc/atmel/Kconfig4
-rw-r--r--sound/soc/atmel/atmel-classd.c1
-rw-r--r--sound/soc/atmel/atmel-pdmic.c1
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c16
-rw-r--r--sound/soc/au1x/Kconfig2
-rw-r--r--sound/soc/codecs/Kconfig67
-rw-r--r--sound/soc/codecs/Makefile16
-rw-r--r--sound/soc/codecs/ad193x-i2c.c6
-rw-r--r--sound/soc/codecs/adau1372-i2c.c4
-rw-r--r--sound/soc/codecs/adau1372.c1
-rw-r--r--sound/soc/codecs/adau1373.c5
-rw-r--r--sound/soc/codecs/adau1701.c5
-rw-r--r--sound/soc/codecs/adau1761-i2c.c8
-rw-r--r--sound/soc/codecs/adau1761.c86
-rw-r--r--sound/soc/codecs/adau1781-i2c.c8
-rw-r--r--sound/soc/codecs/adau17x1.c20
-rw-r--r--sound/soc/codecs/adau17x1.h1
-rw-r--r--sound/soc/codecs/adau1977-i2c.c8
-rw-r--r--sound/soc/codecs/adau7118-i2c.c5
-rw-r--r--sound/soc/codecs/adav803.c5
-rw-r--r--sound/soc/codecs/ak4118.c5
-rw-r--r--sound/soc/codecs/ak4535.c5
-rw-r--r--sound/soc/codecs/ak4613.c377
-rw-r--r--sound/soc/codecs/ak4641.c5
-rw-r--r--sound/soc/codecs/ak4642.c8
-rw-r--r--sound/soc/codecs/ak4671.c5
-rw-r--r--sound/soc/codecs/alc5623.c24
-rw-r--r--sound/soc/codecs/alc5632.c20
-rw-r--r--sound/soc/codecs/cros_ec_codec.c7
-rw-r--r--sound/soc/codecs/cs35l32.c5
-rw-r--r--sound/soc/codecs/cs35l33.c5
-rw-r--r--sound/soc/codecs/cs35l34.c5
-rw-r--r--sound/soc/codecs/cs35l35.c5
-rw-r--r--sound/soc/codecs/cs35l36.c7
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c9
-rw-r--r--sound/soc/codecs/cs35l41-lib.c319
-rw-r--r--sound/soc/codecs/cs35l41-spi.c4
-rw-r--r--sound/soc/codecs/cs35l41.c285
-rw-r--r--sound/soc/codecs/cs35l41.h23
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c74
-rw-r--r--sound/soc/codecs/cs35l45-spi.c74
-rw-r--r--sound/soc/codecs/cs35l45-tables.c202
-rw-r--r--sound/soc/codecs/cs35l45.c690
-rw-r--r--sound/soc/codecs/cs35l45.h217
-rw-r--r--sound/soc/codecs/cs4234.c5
-rw-r--r--sound/soc/codecs/cs4265.c5
-rw-r--r--sound/soc/codecs/cs4270.c20
-rw-r--r--sound/soc/codecs/cs4271-i2c.c5
-rw-r--r--sound/soc/codecs/cs42l42.c5
-rw-r--r--sound/soc/codecs/cs42l42.h826
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c5
-rw-r--r--sound/soc/codecs/cs42l51.c7
-rw-r--r--sound/soc/codecs/cs42l52.c5
-rw-r--r--sound/soc/codecs/cs42l56.c7
-rw-r--r--sound/soc/codecs/cs42l73.c5
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c5
-rw-r--r--sound/soc/codecs/cs43130.c23
-rw-r--r--sound/soc/codecs/cs43130.h151
-rw-r--r--sound/soc/codecs/cs4341.c5
-rw-r--r--sound/soc/codecs/cs4349.c14
-rw-r--r--sound/soc/codecs/cs53l30.c5
-rw-r--r--sound/soc/codecs/cx2072x.c6
-rw-r--r--sound/soc/codecs/da7210.c5
-rw-r--r--sound/soc/codecs/da7213.c5
-rw-r--r--sound/soc/codecs/da7218.c19
-rw-r--r--sound/soc/codecs/da7219.c5
-rw-r--r--sound/soc/codecs/da732x.c5
-rw-r--r--sound/soc/codecs/da9055.c5
-rw-r--r--sound/soc/codecs/dmic.c5
-rw-r--r--sound/soc/codecs/es8316.c5
-rw-r--r--sound/soc/codecs/es8328-i2c.c5
-rw-r--r--sound/soc/codecs/hdac_hda.c15
-rw-r--r--sound/soc/codecs/hdmi-codec.c15
-rw-r--r--sound/soc/codecs/isabelle.c5
-rw-r--r--sound/soc/codecs/lm4857.c5
-rw-r--r--sound/soc/codecs/lm49453.c5
-rw-r--r--sound/soc/codecs/lochnagar-sc.c5
-rw-r--r--sound/soc/codecs/lpass-macro-common.c35
-rw-r--r--sound/soc/codecs/max9768.c5
-rw-r--r--sound/soc/codecs/max98088.c21
-rw-r--r--sound/soc/codecs/max98090.c33
-rw-r--r--sound/soc/codecs/max98095.c19
-rw-r--r--sound/soc/codecs/max98371.c5
-rw-r--r--sound/soc/codecs/max98373-i2c.c5
-rw-r--r--sound/soc/codecs/max98390.c17
-rw-r--r--sound/soc/codecs/max98396.c1637
-rw-r--r--sound/soc/codecs/max98396.h305
-rw-r--r--sound/soc/codecs/max9850.c5
-rw-r--r--sound/soc/codecs/max98504.c6
-rw-r--r--sound/soc/codecs/max98520.c4
-rw-r--r--sound/soc/codecs/max9867.c5
-rw-r--r--sound/soc/codecs/max9877.c5
-rw-r--r--sound/soc/codecs/max98925.c5
-rw-r--r--sound/soc/codecs/max98926.c5
-rw-r--r--sound/soc/codecs/max98927.c5
-rw-r--r--sound/soc/codecs/ml26124.c5
-rw-r--r--sound/soc/codecs/mt6351.c10
-rw-r--r--sound/soc/codecs/mt6358.c10
-rw-r--r--sound/soc/codecs/mt6359.c10
-rw-r--r--sound/soc/codecs/mt6660.c6
-rw-r--r--sound/soc/codecs/nau8540.c5
-rw-r--r--sound/soc/codecs/nau8810.c5
-rw-r--r--sound/soc/codecs/nau8821.c5
-rw-r--r--sound/soc/codecs/nau8822.c5
-rw-r--r--sound/soc/codecs/nau8824.c5
-rw-r--r--sound/soc/codecs/nau8825.c5
-rw-r--r--sound/soc/codecs/pcm1681.c5
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c9
-rw-r--r--sound/soc/codecs/pcm1789.c4
-rw-r--r--sound/soc/codecs/pcm1789.h2
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c5
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c24
-rw-r--r--sound/soc/codecs/pcm186x.c7
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c5
-rw-r--r--sound/soc/codecs/pcm3060.c1
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c5
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c5
-rw-r--r--sound/soc/codecs/rk3328_codec.c2
-rw-r--r--sound/soc/codecs/rt1011.c5
-rw-r--r--sound/soc/codecs/rt1015.c5
-rw-r--r--sound/soc/codecs/rt1016.c5
-rw-r--r--sound/soc/codecs/rt1019.c8
-rw-r--r--sound/soc/codecs/rt1305.c5
-rw-r--r--sound/soc/codecs/rt1308-sdw.c1
-rw-r--r--sound/soc/codecs/rt1308-sdw.h1
-rw-r--r--sound/soc/codecs/rt1308.c5
-rw-r--r--sound/soc/codecs/rt1316-sdw.c1
-rw-r--r--sound/soc/codecs/rt274.c5
-rw-r--r--sound/soc/codecs/rt286.c5
-rw-r--r--sound/soc/codecs/rt298.c5
-rw-r--r--sound/soc/codecs/rt5514.c7
-rw-r--r--sound/soc/codecs/rt5616.c5
-rw-r--r--sound/soc/codecs/rt5631.c5
-rw-r--r--sound/soc/codecs/rt5640.c5
-rw-r--r--sound/soc/codecs/rt5645.c16
-rw-r--r--sound/soc/codecs/rt5651.c5
-rw-r--r--sound/soc/codecs/rt5659.c5
-rw-r--r--sound/soc/codecs/rt5660.c5
-rw-r--r--sound/soc/codecs/rt5663.c5
-rw-r--r--sound/soc/codecs/rt5665.c5
-rw-r--r--sound/soc/codecs/rt5668.c5
-rw-r--r--sound/soc/codecs/rt5670.c5
-rw-r--r--sound/soc/codecs/rt5682-i2c.c5
-rw-r--r--sound/soc/codecs/rt5682s.c32
-rw-r--r--sound/soc/codecs/rt5682s.h6
-rw-r--r--sound/soc/codecs/rt700.c1
-rw-r--r--sound/soc/codecs/rt711-sdca.c1
-rw-r--r--sound/soc/codecs/rt711.c1
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt715-sdca.c1
-rw-r--r--sound/soc/codecs/rt715.c1
-rw-r--r--sound/soc/codecs/rt9120.c114
-rw-r--r--sound/soc/codecs/sdw-mockup.c1
-rw-r--r--sound/soc/codecs/sgtl5000.c5
-rw-r--r--sound/soc/codecs/ssm2518.c5
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c8
-rw-r--r--sound/soc/codecs/ssm4567.c5
-rw-r--r--sound/soc/codecs/sta32x.c14
-rw-r--r--sound/soc/codecs/sta350.c14
-rw-r--r--sound/soc/codecs/sta529.c5
-rw-r--r--sound/soc/codecs/tas2552.c5
-rw-r--r--sound/soc/codecs/tas2562.c25
-rw-r--r--sound/soc/codecs/tas2764.c5
-rw-r--r--sound/soc/codecs/tas2770.c5
-rw-r--r--sound/soc/codecs/tas5086.c5
-rw-r--r--sound/soc/codecs/tas571x.c17
-rw-r--r--sound/soc/codecs/tas5720.c21
-rw-r--r--sound/soc/codecs/tas6424.c17
-rw-r--r--sound/soc/codecs/tda7419.c5
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c32
-rw-r--r--sound/soc/codecs/tlv320adcx140.c16
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c5
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c32
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c11
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c25
-rw-r--r--sound/soc/codecs/tlv320dac33.c5
-rw-r--r--sound/soc/codecs/tpa6130a2.c21
-rw-r--r--sound/soc/codecs/ts3a227e.c5
-rw-r--r--sound/soc/codecs/tscs42xx.c5
-rw-r--r--sound/soc/codecs/tscs454.c17
-rw-r--r--sound/soc/codecs/uda1380.c5
-rw-r--r--sound/soc/codecs/wcd9335.c1
-rw-r--r--sound/soc/codecs/wcd934x.c1
-rw-r--r--sound/soc/codecs/wcd938x.c1
-rw-r--r--sound/soc/codecs/wm1250-ev1.c5
-rw-r--r--sound/soc/codecs/wm2000.c12
-rw-r--r--sound/soc/codecs/wm2200.c5
-rw-r--r--sound/soc/codecs/wm5100.c5
-rw-r--r--sound/soc/codecs/wm8510.c5
-rw-r--r--sound/soc/codecs/wm8523.c5
-rw-r--r--sound/soc/codecs/wm8580.c5
-rw-r--r--sound/soc/codecs/wm8711.c5
-rw-r--r--sound/soc/codecs/wm8728.c5
-rw-r--r--sound/soc/codecs/wm8731-i2c.c68
-rw-r--r--sound/soc/codecs/wm8731-spi.c59
-rw-r--r--sound/soc/codecs/wm8731.c267
-rw-r--r--sound/soc/codecs/wm8731.h27
-rw-r--r--sound/soc/codecs/wm8737.c5
-rw-r--r--sound/soc/codecs/wm8741.c5
-rw-r--r--sound/soc/codecs/wm8750.c5
-rw-r--r--sound/soc/codecs/wm8753.c5
-rw-r--r--sound/soc/codecs/wm8776.c5
-rw-r--r--sound/soc/codecs/wm8804-i2c.c5
-rw-r--r--sound/soc/codecs/wm8900.c5
-rw-r--r--sound/soc/codecs/wm8903.c7
-rw-r--r--sound/soc/codecs/wm8904.c9
-rw-r--r--sound/soc/codecs/wm8940.c12
-rw-r--r--sound/soc/codecs/wm8955.c5
-rw-r--r--sound/soc/codecs/wm8960.c20
-rw-r--r--sound/soc/codecs/wm8961.c5
-rw-r--r--sound/soc/codecs/wm8962.c11
-rw-r--r--sound/soc/codecs/wm8971.c5
-rw-r--r--sound/soc/codecs/wm8974.c5
-rw-r--r--sound/soc/codecs/wm8978.c5
-rw-r--r--sound/soc/codecs/wm8983.c5
-rw-r--r--sound/soc/codecs/wm8985.c8
-rw-r--r--sound/soc/codecs/wm8988.c5
-rw-r--r--sound/soc/codecs/wm8990.c5
-rw-r--r--sound/soc/codecs/wm8991.c5
-rw-r--r--sound/soc/codecs/wm8993.c5
-rw-r--r--sound/soc/codecs/wm8995.c5
-rw-r--r--sound/soc/codecs/wm8996.c5
-rw-r--r--sound/soc/codecs/wm9081.c5
-rw-r--r--sound/soc/codecs/wm9090.c6
-rw-r--r--sound/soc/codecs/wm_adsp.c10
-rw-r--r--sound/soc/codecs/wsa881x.c1
-rw-r--r--sound/soc/fsl/fsl_asrc.c8
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c2
-rw-r--r--sound/soc/fsl/fsl_easrc.h2
-rw-r--r--sound/soc/fsl/fsl_esai.c6
-rw-r--r--sound/soc/fsl/fsl_micfil.c382
-rw-r--r--sound/soc/fsl/fsl_micfil.h269
-rw-r--r--sound/soc/fsl/fsl_sai.c43
-rw-r--r--sound/soc/fsl/fsl_sai.h1
-rw-r--r--sound/soc/fsl/fsl_ssi.c34
-rw-r--r--sound/soc/fsl/imx-es8328.c2
-rw-r--r--sound/soc/fsl/imx-hdmi.c9
-rw-r--r--sound/soc/fsl/imx-pcm.h2
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c14
-rw-r--r--sound/soc/fsl/imx-ssi.h2
-rw-r--r--sound/soc/generic/audio-graph-card2.c7
-rw-r--r--sound/soc/generic/simple-card-utils.c5
-rw-r--r--sound/soc/img/img-i2s-in.c6
-rw-r--r--sound/soc/img/img-parallel-out.c6
-rw-r--r--sound/soc/img/img-spdif-in.c6
-rw-r--r--sound/soc/img/img-spdif-out.c6
-rw-r--r--sound/soc/intel/Kconfig9
-rw-r--r--sound/soc/intel/atom/sst/sst.c1
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c15
-rw-r--r--sound/soc/intel/avs/Makefile8
-rw-r--r--sound/soc/intel/avs/apl.c250
-rw-r--r--sound/soc/intel/avs/avs.h102
-rw-r--r--sound/soc/intel/avs/board_selection.c501
-rw-r--r--sound/soc/intel/avs/core.c631
-rw-r--r--sound/soc/intel/avs/dsp.c27
-rw-r--r--sound/soc/intel/avs/ipc.c253
-rw-r--r--sound/soc/intel/avs/loader.c84
-rw-r--r--sound/soc/intel/avs/messages.c35
-rw-r--r--sound/soc/intel/avs/messages.h51
-rw-r--r--sound/soc/intel/avs/path.c1005
-rw-r--r--sound/soc/intel/avs/path.h72
-rw-r--r--sound/soc/intel/avs/pcm.c1182
-rw-r--r--sound/soc/intel/avs/registers.h8
-rw-r--r--sound/soc/intel/avs/skl.c125
-rw-r--r--sound/soc/intel/avs/topology.c1598
-rw-r--r--sound/soc/intel/avs/topology.h194
-rw-r--r--sound/soc/intel/avs/trace.c33
-rw-r--r--sound/soc/intel/avs/trace.h154
-rw-r--r--sound/soc/intel/avs/utils.c23
-rw-r--r--sound/soc/intel/boards/bdw-rt5650.c6
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c4
-rw-r--r--sound/soc/intel/boards/broadwell.c2
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c5
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c5
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c10
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c8
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c36
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c7
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c6
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c28
-rw-r--r--sound/soc/intel/boards/cht_bsw_nau8824.c4
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c6
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c12
-rw-r--r--sound/soc/intel/boards/cml_rt1011_rt5682.c5
-rw-r--r--sound/soc/intel/boards/glk_rt5682_max98357a.c5
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98357a.c5
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98927.c5
-rw-r--r--sound/soc/intel/boards/kbl_rt5660.c15
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c7
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c7
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_common.c8
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c6
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c6
-rw-r--r--sound/soc/intel/boards/skl_rt286.c5
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c5
-rw-r--r--sound/soc/intel/boards/sof_da7219_max98373.c2
-rw-r--r--sound/soc/intel/boards/sof_es8336.c8
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c2
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c39
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.h5
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c18
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt5682.c14
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt700.c14
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt711.c14
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt711_sdca.c14
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c25
-rw-r--r--sound/soc/intel/catpt/messages.h4
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c7
-rw-r--r--sound/soc/mediatek/Kconfig27
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c9
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c13
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c4
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c5
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c5
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c203
-rw-r--r--sound/soc/mediatek/mt8195/Makefile3
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c8
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c1198
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c (renamed from sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c)843
-rw-r--r--sound/soc/mxs/mxs-saif.c1
-rw-r--r--sound/soc/pxa/Kconfig4
-rw-r--r--sound/soc/pxa/hx4700.c6
-rw-r--r--sound/soc/pxa/palm27x.c7
-rw-r--r--sound/soc/pxa/ttc-dkb.c14
-rw-r--r--sound/soc/pxa/z2.c7
-rw-r--r--sound/soc/qcom/Kconfig2
-rw-r--r--sound/soc/qcom/apq8016_sbc.c2
-rw-r--r--sound/soc/qcom/lpass-platform.c2
-rw-r--r--sound/soc/qcom/sc7180.c4
-rw-r--r--sound/soc/qcom/sc7280.c99
-rw-r--r--sound/soc/qcom/sdm845.c2
-rw-r--r--sound/soc/qcom/sm8250.c2
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c8
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c16
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c16
-rw-r--r--sound/soc/rockchip/rockchip_rt5645.c2
-rw-r--r--sound/soc/samsung/aries_wm8994.c6
-rw-r--r--sound/soc/samsung/bells.c4
-rw-r--r--sound/soc/samsung/h1940_uda1380.c3
-rw-r--r--sound/soc/samsung/littlemill.c6
-rw-r--r--sound/soc/samsung/lowland.c13
-rw-r--r--sound/soc/samsung/midas_wm1811.c2
-rw-r--r--sound/soc/samsung/rx1950_uda1380.c3
-rw-r--r--sound/soc/samsung/smartq_wm8987.c8
-rw-r--r--sound/soc/samsung/spdif.c3
-rw-r--r--sound/soc/samsung/speyside.c14
-rw-r--r--sound/soc/samsung/tobermory.c12
-rw-r--r--sound/soc/sh/Kconfig2
-rw-r--r--sound/soc/sh/rcar/core.c17
-rw-r--r--sound/soc/sh/rcar/dma.c9
-rw-r--r--sound/soc/sh/rcar/rsnd.h2
-rw-r--r--sound/soc/sh/rcar/src.c7
-rw-r--r--sound/soc/sh/rcar/ssi.c14
-rw-r--r--sound/soc/sh/rcar/ssiu.c11
-rw-r--r--sound/soc/sh/rz-ssi.c24
-rw-r--r--sound/soc/soc-card.c56
-rw-r--r--sound/soc/soc-component.c16
-rw-r--r--sound/soc/soc-core.c98
-rw-r--r--sound/soc/soc-dapm.c2
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c57
-rw-r--r--sound/soc/soc-jack.c2
-rw-r--r--sound/soc/soc-pcm.c38
-rw-r--r--sound/soc/soc-topology.c455
-rw-r--r--sound/soc/soc-utils-test.c186
-rw-r--r--sound/soc/soc-utils.c45
-rw-r--r--sound/soc/sof/Makefile4
-rw-r--r--sound/soc/sof/amd/acp-trace.c4
-rw-r--r--sound/soc/sof/amd/acp.c66
-rw-r--r--sound/soc/sof/amd/acp.h10
-rw-r--r--sound/soc/sof/amd/pci-rn.c15
-rw-r--r--sound/soc/sof/amd/renoir.c6
-rw-r--r--sound/soc/sof/compress.c8
-rw-r--r--sound/soc/sof/control.c36
-rw-r--r--sound/soc/sof/core.c28
-rw-r--r--sound/soc/sof/debug.c9
-rw-r--r--sound/soc/sof/imx/imx8.c40
-rw-r--r--sound/soc/sof/imx/imx8m.c20
-rw-r--r--sound/soc/sof/intel/Kconfig2
-rw-r--r--sound/soc/sof/intel/Makefile2
-rw-r--r--sound/soc/sof/intel/apl.c143
-rw-r--r--sound/soc/sof/intel/bdw.c21
-rw-r--r--sound/soc/sof/intel/byt.c56
-rw-r--r--sound/soc/sof/intel/cnl.c212
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c106
-rw-r--r--sound/soc/sof/intel/hda-dai.c434
-rw-r--r--sound/soc/sof/intel/hda-dsp.c52
-rw-r--r--sound/soc/sof/intel/hda-ipc.c82
-rw-r--r--sound/soc/sof/intel/hda-loader.c123
-rw-r--r--sound/soc/sof/intel/hda-trace.c4
-rw-r--r--sound/soc/sof/intel/hda.c40
-rw-r--r--sound/soc/sof/intel/hda.h50
-rw-r--r--sound/soc/sof/intel/icl.c121
-rw-r--r--sound/soc/sof/intel/pci-apl.c36
-rw-r--r--sound/soc/sof/intel/pci-cnl.c54
-rw-r--r--sound/soc/sof/intel/pci-icl.c36
-rw-r--r--sound/soc/sof/intel/pci-tgl.c97
-rw-r--r--sound/soc/sof/intel/pci-tng.c27
-rw-r--r--sound/soc/sof/intel/shim.h16
-rw-r--r--sound/soc/sof/intel/tgl.c160
-rw-r--r--sound/soc/sof/ipc.c969
-rw-r--r--sound/soc/sof/ipc3-control.c155
-rw-r--r--sound/soc/sof/ipc3-dtrace.c649
-rw-r--r--sound/soc/sof/ipc3-loader.c415
-rw-r--r--sound/soc/sof/ipc3-ops.h21
-rw-r--r--sound/soc/sof/ipc3-pcm.c10
-rw-r--r--sound/soc/sof/ipc3-priv.h65
-rw-r--r--sound/soc/sof/ipc3-topology.c79
-rw-r--r--sound/soc/sof/ipc3.c1040
-rw-r--r--sound/soc/sof/ipc4-loader.c210
-rw-r--r--sound/soc/sof/ipc4-priv.h44
-rw-r--r--sound/soc/sof/ipc4.c606
-rw-r--r--sound/soc/sof/loader.c698
-rw-r--r--sound/soc/sof/mediatek/Kconfig11
-rw-r--r--sound/soc/sof/mediatek/Makefile2
-rw-r--r--sound/soc/sof/mediatek/adsp_helper.h20
-rw-r--r--sound/soc/sof/mediatek/mt8186/Makefile4
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.c101
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.h24
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-loader.c58
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c554
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h80
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c192
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c84
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.h10
-rw-r--r--sound/soc/sof/ops.c2
-rw-r--r--sound/soc/sof/ops.h34
-rw-r--r--sound/soc/sof/pcm.c30
-rw-r--r--sound/soc/sof/pm.c22
-rw-r--r--sound/soc/sof/sof-acpi-dev.c6
-rw-r--r--sound/soc/sof/sof-audio.c398
-rw-r--r--sound/soc/sof/sof-audio.h80
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c3
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c191
-rw-r--r--sound/soc/sof/sof-client-probes.c14
-rw-r--r--sound/soc/sof/sof-client.c66
-rw-r--r--sound/soc/sof/sof-client.h2
-rw-r--r--sound/soc/sof/sof-of-dev.c12
-rw-r--r--sound/soc/sof/sof-of-dev.h1
-rw-r--r--sound/soc/sof/sof-pci-dev.c96
-rw-r--r--sound/soc/sof/sof-priv.h130
-rw-r--r--sound/soc/sof/topology.c79
-rw-r--r--sound/soc/sof/trace.c596
-rw-r--r--sound/soc/tegra/Kconfig12
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1046
-rw-r--r--sound/soc/tegra/tegra186_asrc.h112
-rw-r--r--sound/soc/tegra/tegra210_ahub.c82
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c30
-rw-r--r--sound/soc/tegra/tegra_wm8903.c10
-rw-r--r--sound/soc/ti/ams-delta.c4
-rw-r--r--sound/soc/ti/davinci-mcasp.c2
-rw-r--r--sound/soc/ti/j721e-evm.c44
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c8
-rw-r--r--sound/soc/ti/omap-twl4030.c8
-rw-r--r--sound/soc/ti/osk5912.c4
-rw-r--r--sound/soc/ti/rx51.c2
-rw-r--r--sound/soc/uniphier/aio-compress.c7
-rw-r--r--sound/soc/ux500/mop500_ab8500.c9
-rw-r--r--sound/usb/card.c1
-rw-r--r--sound/usb/card.h3
-rw-r--r--sound/usb/clock.c7
-rw-r--r--sound/usb/endpoint.c90
-rw-r--r--sound/usb/implicit.c10
-rw-r--r--sound/usb/pcm.c17
-rw-r--r--sound/usb/quirks-table.h3
-rw-r--r--sound/usb/quirks.c6
-rw-r--r--sound/usb/usbaudio.h7
508 files changed, 22927 insertions, 9086 deletions
diff --git a/sound/core/jack.c b/sound/core/jack.c
index d1e3055f2b6a..88493cc31914 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -42,8 +42,11 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data;
- if (!jack->input_dev)
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
return 0;
+ }
/* If the input device is registered with the input subsystem
* then we need to use a different deallocator. */
@@ -52,6 +55,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
else
input_free_device(jack->input_dev);
jack->input_dev = NULL;
+ mutex_unlock(&jack->input_dev_lock);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
return 0;
}
@@ -90,8 +94,11 @@ static int snd_jack_dev_register(struct snd_device *device)
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
- if (!jack->input_dev)
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
return 0;
+ }
jack->input_dev->name = jack->name;
@@ -116,6 +123,7 @@ static int snd_jack_dev_register(struct snd_device *device)
if (err == 0)
jack->registered = 1;
+ mutex_unlock(&jack->input_dev_lock);
return err;
}
#endif /* CONFIG_SND_JACK_INPUT_DEV */
@@ -517,9 +525,11 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
return -ENOMEM;
}
- /* don't creat input device for phantom jack */
- if (!phantom_jack) {
#ifdef CONFIG_SND_JACK_INPUT_DEV
+ mutex_init(&jack->input_dev_lock);
+
+ /* don't create input device for phantom jack */
+ if (!phantom_jack) {
int i;
jack->input_dev = input_allocate_device();
@@ -537,8 +547,8 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
-#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
@@ -578,10 +588,14 @@ EXPORT_SYMBOL(snd_jack_new);
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
{
WARN_ON(jack->registered);
- if (!jack->input_dev)
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
return;
+ }
jack->input_dev->dev.parent = parent;
+ mutex_unlock(&jack->input_dev_lock);
}
EXPORT_SYMBOL(snd_jack_set_parent);
@@ -629,6 +643,8 @@ EXPORT_SYMBOL(snd_jack_set_key);
/**
* snd_jack_report - Report the current status of a jack
+ * Note: This function uses mutexes and should be called from a
+ * context which can sleep (such as a workqueue).
*
* @jack: The jack to report status for
* @status: The current status of the jack
@@ -654,8 +670,11 @@ void snd_jack_report(struct snd_jack *jack, int status)
status & jack_kctl->mask_bits);
#ifdef CONFIG_SND_JACK_INPUT_DEV
- if (!jack->input_dev)
+ mutex_lock(&jack->input_dev_lock);
+ if (!jack->input_dev) {
+ mutex_unlock(&jack->input_dev_lock);
return;
+ }
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
@@ -675,6 +694,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
}
input_sync(jack->input_dev);
+ mutex_unlock(&jack->input_dev_lock);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
EXPORT_SYMBOL(snd_jack_report);
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 8848d2f3160d..b8296b6eb2c1 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -453,7 +453,6 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
*/
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
{
- struct snd_card *card = substream->pcm->card;
struct snd_pcm_runtime *runtime;
if (PCM_RUNTIME_CHECK(substream))
@@ -462,6 +461,8 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
if (runtime->dma_area == NULL)
return 0;
if (runtime->dma_buffer_p != &substream->dma_buffer) {
+ struct snd_card *card = substream->pcm->card;
+
/* it's a newly allocated buffer. release it now. */
do_free_pages(card, runtime->dma_buffer_p);
kfree(runtime->dma_buffer_p);
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 84d78630463e..25fcf5a2c71c 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -139,7 +139,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
port_subs_info_init(&new_port->c_dest);
snd_use_lock_use(&new_port->use_lock);
- num = port >= 0 ? port : 0;
+ num = max(port, 0);
mutex_lock(&client->ports_mutex);
write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index ca4cdf666f82..be3009746f3a 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -165,6 +165,24 @@ config SND_SERIAL_U16550
To compile this driver as a module, choose M here: the module
will be called snd-serial-u16550.
+config SND_SERIAL_GENERIC
+ tristate "Generic serial MIDI driver"
+ depends on SERIAL_DEV_BUS
+ depends on OF
+ select SND_RAWMIDI
+ help
+ To include support for mapping generic serial devices as raw
+ ALSA MIDI devices, say Y here. The driver only supports setting
+ the serial port to standard baudrates. To attain the standard MIDI
+ baudrate of 31.25 kBaud, configure the clock of the underlying serial
+ device so that a requested 38.4 kBaud will result in the standard speed.
+
+ Use this devicetree binding to configure serial port mapping
+ <file:Documentation/devicetree/bindings/sound/serial-midi.yaml>
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-serial-generic.
+
config SND_MPU401
tristate "Generic MPU-401 UART driver"
select SND_MPU401_UART
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index c0fe4eccdaef..b60303180a1b 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -10,6 +10,7 @@ snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
snd-portman2x4-objs := portman2x4.o
snd-serial-u16550-objs := serial-u16550.o
+snd-serial-generic-objs := serial-generic.o
snd-virmidi-objs := virmidi.o
# Toplevel Module Dependency
@@ -17,6 +18,7 @@ obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
+obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c
new file mode 100644
index 000000000000..e1f864dc7939
--- /dev/null
+++ b/sound/drivers/serial-generic.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serial-generic.c
+ * Copyright (c) by Daniel Kaehn <kaehndan@gmail.com
+ * Based on serial-u16550.c by Jaroslav Kysela <perex@perex.cz>,
+ * Isaku Yamahata <yamahata@private.email.ne.jp>,
+ * George Hansper <ghansper@apana.org.au>,
+ * Hannu Savolainen
+ *
+ * Generic serial MIDI driver using the serdev serial bus API for hardware interaction
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/serdev.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/dev_printk.h>
+
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/initval.h>
+
+MODULE_DESCRIPTION("Generic serial MIDI driver");
+MODULE_LICENSE("GPL");
+
+#define SERIAL_MODE_INPUT_OPEN 1
+#define SERIAL_MODE_OUTPUT_OPEN 2
+#define SERIAL_MODE_INPUT_TRIGGERED 3
+#define SERIAL_MODE_OUTPUT_TRIGGERED 4
+
+#define SERIAL_TX_STATE_ACTIVE 1
+#define SERIAL_TX_STATE_WAKEUP 2
+
+struct snd_serial_generic {
+ struct serdev_device *serdev;
+
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_output;
+ struct snd_rawmidi_substream *midi_input;
+
+ unsigned int baudrate;
+
+ unsigned long filemode; /* open status of file */
+ struct work_struct tx_work;
+ unsigned long tx_state;
+
+};
+
+static void snd_serial_generic_tx_wakeup(struct snd_serial_generic *drvdata)
+{
+ if (test_and_set_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state))
+ set_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);
+
+ schedule_work(&drvdata->tx_work);
+}
+
+#define INTERNAL_BUF_SIZE 256
+
+static void snd_serial_generic_tx_work(struct work_struct *work)
+{
+ static char buf[INTERNAL_BUF_SIZE];
+ int num_bytes;
+ struct snd_serial_generic *drvdata = container_of(work, struct snd_serial_generic,
+ tx_work);
+ struct snd_rawmidi_substream *substream = drvdata->midi_output;
+
+ clear_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);
+
+ while (!snd_rawmidi_transmit_empty(substream)) {
+
+ if (!test_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode))
+ break;
+
+ num_bytes = snd_rawmidi_transmit_peek(substream, buf, INTERNAL_BUF_SIZE);
+ num_bytes = serdev_device_write_buf(drvdata->serdev, buf, num_bytes);
+
+ if (!num_bytes)
+ break;
+
+ snd_rawmidi_transmit_ack(substream, num_bytes);
+
+ if (!test_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state))
+ break;
+ }
+
+ clear_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state);
+}
+
+static void snd_serial_generic_write_wakeup(struct serdev_device *serdev)
+{
+ struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
+
+ snd_serial_generic_tx_wakeup(drvdata);
+}
+
+static int snd_serial_generic_receive_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t count)
+{
+ int ret;
+ struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
+
+ if (!test_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode))
+ return 0;
+
+ ret = snd_rawmidi_receive(drvdata->midi_input, buf, count);
+ return ret < 0 ? 0 : ret;
+}
+
+static const struct serdev_device_ops snd_serial_generic_serdev_device_ops = {
+ .receive_buf = snd_serial_generic_receive_buf,
+ .write_wakeup = snd_serial_generic_write_wakeup
+};
+
+static int snd_serial_generic_ensure_serdev_open(struct snd_serial_generic *drvdata)
+{
+ int err;
+ unsigned int actual_baud;
+
+ if (drvdata->filemode)
+ return 0;
+
+ dev_dbg(drvdata->card->dev, "Opening serial port for card %s\n",
+ drvdata->card->shortname);
+ err = serdev_device_open(drvdata->serdev);
+ if (err < 0)
+ return err;
+
+ actual_baud = serdev_device_set_baudrate(drvdata->serdev,
+ drvdata->baudrate);
+ if (actual_baud != drvdata->baudrate) {
+ dev_warn(drvdata->card->dev, "requested %d baud for card %s but it was actually set to %d\n",
+ drvdata->baudrate, drvdata->card->shortname, actual_baud);
+ }
+
+ return 0;
+}
+
+static int snd_serial_generic_input_open(struct snd_rawmidi_substream *substream)
+{
+ int err;
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Opening input for card %s\n",
+ drvdata->card->shortname);
+
+ err = snd_serial_generic_ensure_serdev_open(drvdata);
+ if (err < 0)
+ return err;
+
+ set_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);
+ drvdata->midi_input = substream;
+ return 0;
+}
+
+static int snd_serial_generic_input_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Closing input for card %s\n",
+ drvdata->card->shortname);
+
+ clear_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);
+ clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+
+ drvdata->midi_input = NULL;
+
+ if (!drvdata->filemode)
+ serdev_device_close(drvdata->serdev);
+ return 0;
+}
+
+static void snd_serial_generic_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ if (up)
+ set_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+ else
+ clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+}
+
+static int snd_serial_generic_output_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+ int err;
+
+ dev_dbg(drvdata->card->dev, "Opening output for card %s\n",
+ drvdata->card->shortname);
+
+ err = snd_serial_generic_ensure_serdev_open(drvdata);
+ if (err < 0)
+ return err;
+
+ set_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);
+
+ drvdata->midi_output = substream;
+ return 0;
+};
+
+static int snd_serial_generic_output_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Closing output for card %s\n",
+ drvdata->card->shortname);
+
+ clear_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);
+ clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+
+ if (!drvdata->filemode)
+ serdev_device_close(drvdata->serdev);
+
+ drvdata->midi_output = NULL;
+
+ return 0;
+};
+
+static void snd_serial_generic_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ if (up)
+ set_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+ else
+ clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+
+ if (up)
+ snd_serial_generic_tx_wakeup(drvdata);
+}
+
+static void snd_serial_generic_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ /* Flush any pending characters */
+ serdev_device_write_flush(drvdata->serdev);
+ cancel_work_sync(&drvdata->tx_work);
+}
+
+static const struct snd_rawmidi_ops snd_serial_generic_output = {
+ .open = snd_serial_generic_output_open,
+ .close = snd_serial_generic_output_close,
+ .trigger = snd_serial_generic_output_trigger,
+ .drain = snd_serial_generic_output_drain,
+};
+
+static const struct snd_rawmidi_ops snd_serial_generic_input = {
+ .open = snd_serial_generic_input_open,
+ .close = snd_serial_generic_input_close,
+ .trigger = snd_serial_generic_input_trigger,
+};
+
+static void snd_serial_generic_parse_dt(struct serdev_device *serdev,
+ struct snd_serial_generic *drvdata)
+{
+ int err;
+
+ err = of_property_read_u32(serdev->dev.of_node, "current-speed",
+ &drvdata->baudrate);
+ if (err < 0) {
+ dev_dbg(drvdata->card->dev,
+ "MIDI device reading of current-speed DT param failed with error %d, using default of 38400\n",
+ err);
+ drvdata->baudrate = 38400;
+ }
+
+}
+
+static void snd_serial_generic_substreams(struct snd_rawmidi_str *stream, int dev_num)
+{
+ struct snd_rawmidi_substream *substream;
+
+ list_for_each_entry(substream, &stream->substreams, list) {
+ sprintf(substream->name, "Serial MIDI %d-%d", dev_num, substream->number);
+ }
+}
+
+static int snd_serial_generic_rmidi(struct snd_serial_generic *drvdata,
+ int outs, int ins, struct snd_rawmidi **rmidi)
+{
+ struct snd_rawmidi *rrawmidi;
+ int err;
+
+ err = snd_rawmidi_new(drvdata->card, drvdata->card->driver, 0,
+ outs, ins, &rrawmidi);
+
+ if (err < 0)
+ return err;
+
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_serial_generic_input);
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_serial_generic_output);
+ strcpy(rrawmidi->name, drvdata->card->shortname);
+
+ snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+ drvdata->serdev->ctrl->nr);
+ snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
+ drvdata->serdev->ctrl->nr);
+
+ rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ if (rmidi)
+ *rmidi = rrawmidi;
+ return 0;
+}
+
+static int snd_serial_generic_probe(struct serdev_device *serdev)
+{
+ struct snd_card *card;
+ struct snd_serial_generic *drvdata;
+ int err;
+
+ err = snd_devm_card_new(&serdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1, THIS_MODULE,
+ sizeof(struct snd_serial_generic), &card);
+
+ if (err < 0)
+ return err;
+
+ strcpy(card->driver, "SerialMIDI");
+ sprintf(card->shortname, "SerialMIDI-%d", serdev->ctrl->nr);
+ sprintf(card->longname, "Serial MIDI device at serial%d", serdev->ctrl->nr);
+
+ drvdata = card->private_data;
+
+ drvdata->serdev = serdev;
+ drvdata->card = card;
+
+ snd_serial_generic_parse_dt(serdev, drvdata);
+
+ INIT_WORK(&drvdata->tx_work, snd_serial_generic_tx_work);
+
+ err = snd_serial_generic_rmidi(drvdata, 1, 1, &drvdata->rmidi);
+ if (err < 0)
+ return err;
+
+ serdev_device_set_client_ops(serdev, &snd_serial_generic_serdev_device_ops);
+ serdev_device_set_drvdata(drvdata->serdev, drvdata);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static const struct of_device_id snd_serial_generic_dt_ids[] = {
+ { .compatible = "serial-midi" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, snd_serial_generic_dt_ids);
+
+static struct serdev_device_driver snd_serial_generic_driver = {
+ .driver = {
+ .name = "snd-serial-generic",
+ .of_match_table = of_match_ptr(snd_serial_generic_dt_ids),
+ },
+ .probe = snd_serial_generic_probe,
+};
+
+module_serdev_device_driver(snd_serial_generic_driver);
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 5ff10fec7b90..0db24cc4d916 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -129,7 +129,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
return 0;
/* the u32 cast is okay because in snd*create we successfully told
- pci alloc that we're only 32 bit capable so the uppper will be 0 */
+ pci alloc that we're only 32 bit capable so the upper will be 0 */
addr = (u32) substream->runtime->dma_addr;
desc_addr = (u32) dma->desc_buf.addr;
for (i = 0; i < periods; i++) {
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 78f35e88aed6..fbdb8a3d5b8e 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -36,6 +36,7 @@
| ((IEC958_AES3_CON_FS_48000) << 24))
static const struct snd_pci_quirk subsys_20k1_list[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0021, "SB046x", CTSB046X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X),
@@ -64,6 +65,7 @@ static const struct snd_pci_quirk subsys_20k2_list[] = {
static const char *ct_subsys_name[NUM_CTCARDS] = {
/* 20k1 models */
+ [CTSB046X] = "SB046x",
[CTSB055X] = "SB055x",
[CTSB073X] = "SB073x",
[CTUAA] = "UAA",
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index f406b626a28c..2875cec83b8f 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -26,8 +26,9 @@ enum CHIPTYP {
enum CTCARDS {
/* 20k1 models */
+ CTSB046X,
+ CT20K1_MODEL_FIRST = CTSB046X,
CTSB055X,
- CT20K1_MODEL_FIRST = CTSB055X,
CTSB073X,
CTUAA,
CT20K1_UNKNOWN,
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 0cea4982ed7d..9edbf5d8c3c7 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1916,7 +1916,7 @@ static int hw_card_start(struct hw *hw)
}
- /* Switch to X-Fi mode from UAA mode if neeeded */
+ /* Switch to X-Fi mode from UAA mode if needed */
if (hw->model == CTUAA) {
err = uaa_to_xfi(pci);
if (err)
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index 7be5c3327b16..47b2c023ee3d 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -124,7 +124,6 @@ static int midi_service_irq(struct echoaudio *chip)
return 0;
/* Get the MIDI data from the comm page */
- i = 1;
received = 0;
for (i = 1; i <= count; i++) {
/* Get the MIDI byte */
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 86cc1ca025e4..3880f359e688 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1751,11 +1751,8 @@ static void snd_emu10k1_detect_iommu(struct snd_emu10k1 *emu)
emu->iommu_workaround = false;
- if (!iommu_present(emu->card->dev->bus))
- return;
-
domain = iommu_get_domain_for_dev(emu->card->dev);
- if (domain && domain->type == IOMMU_DOMAIN_IDENTITY)
+ if (!domain || domain->type == IOMMU_DOMAIN_IDENTITY)
return;
dev_notice(emu->card->dev,
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 9f6c99c1d87b..79ade4787d95 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -102,6 +102,7 @@ config SND_HDA_SCODEC_CS35L41_I2C
select SND_HDA_GENERIC
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
+ select REGMAP_IRQ
help
Say Y or M here to include CS35L41 I2C HD-audio side codec support
in snd-hda-intel driver, such as ALC287.
@@ -117,6 +118,7 @@ config SND_HDA_SCODEC_CS35L41_SPI
select SND_HDA_GENERIC
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
+ select REGMAP_IRQ
help
Say Y or M here to include CS35L41 SPI HD-audio side codec support
in snd-hda-intel driver, such as ALC287.
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index 718595380868..cce27a86267f 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -17,163 +17,88 @@
#include "cs35l41_hda.h"
static const struct reg_sequence cs35l41_hda_config[] = {
- { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3200000Hz, BCLK Input, PLL_REFCLK_EN = 1
+ { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
{ CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz
{ CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1
{ CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
- { CS35L41_SP_FORMAT, 0x20200200 }, // 24 bits, I2S, BCLK Slave, FSYNC Slave
+ { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
{ CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1
{ CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB
{ CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
- { CS35L41_PWR_CTRL2, 0x00000001 }, // AMP_EN = 1
};
-static const struct reg_sequence cs35l41_hda_start_bst[] = {
- { CS35L41_PWR_CTRL2, 0x00000021 }, // BST_EN = 10, AMP_EN = 1
- { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1
-};
-
-static const struct reg_sequence cs35l41_hda_stop_bst[] = {
- { CS35L41_PWR_CTRL1, 0x00000000, 3000}, // set GLOBAL_EN = 0
-};
-
-// only on amps where GPIO1 is used to control ext. VSPK switch
-static const struct reg_sequence cs35l41_start_ext_vspk[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
- { 0x00007414, 0x08C82222 },
- { 0x0000742C, 0x00000009 },
- { 0x00011008, 0x00008001 },
- { 0x0000742C, 0x0000000F },
- { 0x0000742C, 0x00000079 },
- { 0x00007438, 0x00585941 },
- { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1
- { 0x0000742C, 0x000000F9 },
- { 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-//only on amps where GPIO1 is used to control ext. VSPK switch
-static const struct reg_sequence cs35l41_stop_ext_vspk[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
- { 0x00002014, 0x00000000, 3000}, // set GLOBAL_EN = 0
- { 0x0000742C, 0x00000009 },
- { 0x00007438, 0x00580941 },
- { 0x00011008, 0x00000001 },
- { 0x0000393C, 0x000000C0, 6000},
- { 0x0000393C, 0x00000000 },
- { 0x00007414, 0x00C82222 },
- { 0x0000742C, 0x00000000 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct reg_sequence cs35l41_safe_to_active[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x0000742C, 0x0000000F },
- { 0x0000742C, 0x00000079 },
- { 0x00007438, 0x00585941 },
- { CS35L41_PWR_CTRL1, 0x00000001, 2000 }, // GLOBAL_EN = 1
- { 0x0000742C, 0x000000F9 },
- { 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct reg_sequence cs35l41_active_to_safe[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
+static const struct reg_sequence cs35l41_hda_mute[] = {
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB
{ CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_VOL_PCM Mute
- { CS35L41_PWR_CTRL2, 0x00000000 }, // AMP_EN = 0
- { CS35L41_PWR_CTRL1, 0x00000000 },
- { 0x0000742C, 0x00000009, 2000 },
- { 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct reg_sequence cs35l41_reset_to_safe[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
- { 0x00007414, 0x08C82222 },
- { 0x0000742C, 0x00000009 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_no_bst = {
- .probe = cs35l41_reset_to_safe,
- .num_probe = ARRAY_SIZE(cs35l41_reset_to_safe),
- .open = cs35l41_hda_config,
- .num_open = ARRAY_SIZE(cs35l41_hda_config),
- .prepare = cs35l41_safe_to_active,
- .num_prepare = ARRAY_SIZE(cs35l41_safe_to_active),
- .cleanup = cs35l41_active_to_safe,
- .num_cleanup = ARRAY_SIZE(cs35l41_active_to_safe),
};
-static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_ext_bst = {
- .open = cs35l41_hda_config,
- .num_open = ARRAY_SIZE(cs35l41_hda_config),
- .prepare = cs35l41_start_ext_vspk,
- .num_prepare = ARRAY_SIZE(cs35l41_start_ext_vspk),
- .cleanup = cs35l41_stop_ext_vspk,
- .num_cleanup = ARRAY_SIZE(cs35l41_stop_ext_vspk),
-};
+/* Protection release cycle to get the speaker out of Safe-Mode */
+static void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask)
+{
+ regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+ regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+}
-static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_int_bst = {
- .open = cs35l41_hda_config,
- .num_open = ARRAY_SIZE(cs35l41_hda_config),
- .prepare = cs35l41_hda_start_bst,
- .num_prepare = ARRAY_SIZE(cs35l41_hda_start_bst),
- .cleanup = cs35l41_hda_stop_bst,
- .num_cleanup = ARRAY_SIZE(cs35l41_hda_stop_bst),
-};
+/* Clear all errors to release safe mode. Global Enable must be cleared first. */
+static void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
+{
+ cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors);
+ cs35l41->irq_errors = 0;
+}
static void cs35l41_hda_playback_hook(struct device *dev, int action)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
- const struct cs35l41_hda_reg_sequence *reg_seq = cs35l41->reg_seq;
struct regmap *reg = cs35l41->regmap;
int ret = 0;
switch (action) {
case HDA_GEN_PCM_ACT_OPEN:
- if (reg_seq->open)
- ret = regmap_multi_reg_write(reg, reg_seq->open, reg_seq->num_open);
+ regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config));
+ ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
break;
case HDA_GEN_PCM_ACT_PREPARE:
- if (reg_seq->prepare)
- ret = regmap_multi_reg_write(reg, reg_seq->prepare, reg_seq->num_prepare);
+ ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1);
break;
case HDA_GEN_PCM_ACT_CLEANUP:
- if (reg_seq->cleanup)
- ret = regmap_multi_reg_write(reg, reg_seq->cleanup, reg_seq->num_cleanup);
+ regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
+ ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0);
break;
case HDA_GEN_PCM_ACT_CLOSE:
- if (reg_seq->close)
- ret = regmap_multi_reg_write(reg, reg_seq->close, reg_seq->num_close);
+ ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
+ cs35l41_irq_release(cs35l41);
break;
default:
- ret = -EINVAL;
+ dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
break;
}
if (ret)
- dev_warn(cs35l41->dev, "Failed to apply multi reg write: %d\n", ret);
+ dev_err(cs35l41->dev, "Regmap access fail: %d\n", ret);
}
static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ static const char * const channel_name[] = { "L", "R" };
+
+ if (!cs35l41->amp_name) {
+ if (*rx_slot >= ARRAY_SIZE(channel_name))
+ return -EINVAL;
+
+ cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d",
+ channel_name[*rx_slot], cs35l41->channel_index);
+ if (!cs35l41->amp_name)
+ return -ENOMEM;
+ }
return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
rx_slot);
@@ -194,7 +119,6 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
comps->dev = dev;
strscpy(comps->name, dev_name(dev), sizeof(comps->name));
comps->playback_hook = cs35l41_hda_playback_hook;
- comps->set_channel_map = cs35l41_hda_channel_map;
return 0;
}
@@ -213,67 +137,169 @@ static const struct component_ops cs35l41_hda_comp_ops = {
.unbind = cs35l41_hda_unbind,
};
-static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41,
- const struct cs35l41_hda_hw_config *hw_cfg)
+static irqreturn_t cs35l41_bst_short_err(int irq, void *data)
{
- bool internal_boost = false;
- int ret;
+ struct cs35l41_hda *cs35l41 = data;
- if (!hw_cfg) {
- cs35l41->reg_seq = &cs35l41_hda_reg_seq_no_bst;
- return 0;
- }
+ dev_crit_ratelimited(cs35l41->dev, "LBST Error\n");
+ set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
- if (hw_cfg->bst_ind || hw_cfg->bst_cap || hw_cfg->bst_ipk)
- internal_boost = true;
+ return IRQ_HANDLED;
+}
- switch (hw_cfg->gpio1_func) {
- case CS35L41_NOT_USED:
- break;
- case CS35l41_VSPK_SWITCH:
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO1_CTRL_MASK, 1 << CS35L41_GPIO1_CTRL_SHIFT);
- break;
- case CS35l41_SYNC:
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO1_CTRL_MASK, 2 << CS35L41_GPIO1_CTRL_SHIFT);
- break;
- default:
- dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n", hw_cfg->gpio1_func);
- return -EINVAL;
- }
+static irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
- switch (hw_cfg->gpio2_func) {
- case CS35L41_NOT_USED:
- break;
- case CS35L41_INTERRUPT:
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO2_CTRL_MASK, 2 << CS35L41_GPIO2_CTRL_SHIFT);
- break;
- default:
- dev_err(cs35l41->dev, "Invalid function %d for GPIO2\n", hw_cfg->gpio2_func);
+ dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+ set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_bst_ovp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
+ set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
+ set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_warn(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
+ set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_amp_short(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
+ set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static const struct cs35l41_irq cs35l41_irqs[] = {
+ CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err),
+ CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err),
+ CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err),
+ CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn),
+ CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err),
+ CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short),
+};
+
+static const struct regmap_irq cs35l41_reg_irqs[] = {
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR),
+};
+
+static const struct regmap_irq_chip cs35l41_regmap_irq_chip = {
+ .name = "cs35l41 IRQ1 Controller",
+ .status_base = CS35L41_IRQ1_STATUS1,
+ .mask_base = CS35L41_IRQ1_MASK1,
+ .ack_base = CS35L41_IRQ1_STATUS1,
+ .num_regs = 4,
+ .irqs = cs35l41_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l41_reg_irqs),
+};
+
+static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ bool using_irq = false;
+ int irq, irq_pol;
+ int ret;
+ int i;
+
+ if (!cs35l41->hw_cfg.valid)
return -EINVAL;
+
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
+
+ if (hw_cfg->gpio1.valid) {
+ switch (hw_cfg->gpio1.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35l41_VSPK_SWITCH:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_GPIO;
+ hw_cfg->gpio1.out_en = true;
+ break;
+ case CS35l41_SYNC:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_MDSYNC;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n",
+ hw_cfg->gpio1.func);
+ return -EINVAL;
+ }
}
- if (internal_boost) {
- cs35l41->reg_seq = &cs35l41_hda_reg_seq_int_bst;
- if (!(hw_cfg->bst_ind && hw_cfg->bst_cap && hw_cfg->bst_ipk))
+ if (hw_cfg->gpio2.valid) {
+ switch (hw_cfg->gpio2.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35L41_INTERRUPT:
+ using_irq = true;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func);
return -EINVAL;
- ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap,
- hw_cfg->bst_ind, hw_cfg->bst_cap, hw_cfg->bst_ipk);
+ }
+ }
+
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
+
+ if (cs35l41->irq && using_irq) {
+ ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
if (ret)
return ret;
- } else {
- cs35l41->reg_seq = &cs35l41_hda_reg_seq_ext_bst;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
+ cs35l41_irqs[i].handler,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ cs35l41_irqs[i].name, cs35l41);
+ if (ret)
+ return ret;
+ }
}
- return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, (unsigned int *)&hw_cfg->spk_pos);
+ return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
}
-static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41,
- const char *hid, int id)
+static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
{
- struct cs35l41_hda_hw_config *hw_cfg;
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
u32 values[HDA_MAX_COMPONENTS];
struct acpi_device *adev;
struct device *physdev;
@@ -284,7 +310,7 @@ static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *c
adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
if (!adev) {
dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
}
physdev = get_device(acpi_get_first_physical_node(adev));
@@ -324,56 +350,67 @@ static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *c
cs35l41->reset_gpio = fwnode_gpiod_get_index(&adev->fwnode, "reset", cs35l41->index,
GPIOD_OUT_LOW, "cs35l41-reset");
- hw_cfg = kzalloc(sizeof(*hw_cfg), GFP_KERNEL);
- if (!hw_cfg) {
- ret = -ENOMEM;
- goto err;
- }
-
property = "cirrus,speaker-position";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret)
- goto err_free;
+ goto err;
hw_cfg->spk_pos = values[cs35l41->index];
+ cs35l41->channel_index = 0;
+ for (i = 0; i < cs35l41->index; i++)
+ if (values[i] == hw_cfg->spk_pos)
+ cs35l41->channel_index++;
+
property = "cirrus,gpio1-func";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret)
- goto err_free;
- hw_cfg->gpio1_func = values[cs35l41->index];
+ goto err;
+ hw_cfg->gpio1.func = values[cs35l41->index];
+ hw_cfg->gpio1.valid = true;
property = "cirrus,gpio2-func";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret)
- goto err_free;
- hw_cfg->gpio2_func = values[cs35l41->index];
+ goto err;
+ hw_cfg->gpio2.func = values[cs35l41->index];
+ hw_cfg->gpio2.valid = true;
property = "cirrus,boost-peak-milliamp";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret == 0)
hw_cfg->bst_ipk = values[cs35l41->index];
+ else
+ hw_cfg->bst_ipk = -1;
property = "cirrus,boost-ind-nanohenry";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret == 0)
hw_cfg->bst_ind = values[cs35l41->index];
+ else
+ hw_cfg->bst_ind = -1;
property = "cirrus,boost-cap-microfarad";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret == 0)
hw_cfg->bst_cap = values[cs35l41->index];
+ else
+ hw_cfg->bst_cap = -1;
+ if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0)
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ else
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+
+ hw_cfg->valid = true;
put_device(physdev);
- return hw_cfg;
+ return 0;
-err_free:
- kfree(hw_cfg);
err:
put_device(physdev);
dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
- return ERR_PTR(ret);
+ return ret;
no_acpi_dsd:
/*
@@ -384,25 +421,32 @@ no_acpi_dsd:
* fwnode.
*/
if (strncmp(hid, "CLSA0100", 8) != 0)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
/* check I2C address to assign the index */
cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->hw_cfg.spk_pos = cs35l41->index;
+ cs35l41->channel_index = 0;
cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
- cs35l41->vspk_always_on = true;
+ cs35l41->hw_cfg.bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
+ hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN;
+ hw_cfg->gpio2.valid = true;
+ cs35l41->hw_cfg.valid = true;
put_device(physdev);
- return NULL;
+ return 0;
}
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
struct regmap *regmap)
{
unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status;
- struct cs35l41_hda_hw_config *acpi_hw_cfg;
struct cs35l41_hda *cs35l41;
int ret;
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs));
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ);
+
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -415,9 +459,11 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
cs35l41->regmap = regmap;
dev_set_drvdata(dev, cs35l41);
- acpi_hw_cfg = cs35l41_hda_read_acpi(cs35l41, device_name, id);
- if (IS_ERR(acpi_hw_cfg))
- return PTR_ERR(acpi_hw_cfg);
+ ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Platform not supported %d\n", ret);
+ return ret;
+ }
if (IS_ERR(cs35l41->reset_gpio)) {
ret = PTR_ERR(cs35l41->reset_gpio);
@@ -490,20 +536,9 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
if (ret)
goto err;
- ret = cs35l41_hda_apply_properties(cs35l41, acpi_hw_cfg);
+ ret = cs35l41_hda_apply_properties(cs35l41);
if (ret)
goto err;
- kfree(acpi_hw_cfg);
- acpi_hw_cfg = NULL;
-
- if (cs35l41->reg_seq->probe) {
- ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41->reg_seq->probe,
- cs35l41->reg_seq->num_probe);
- if (ret) {
- dev_err(cs35l41->dev, "Fail to apply probe reg patch: %d\n", ret);
- goto err;
- }
- }
ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
if (ret) {
@@ -516,8 +551,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
return 0;
err:
- kfree(acpi_hw_cfg);
- if (!cs35l41->vspk_always_on)
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
gpiod_put(cs35l41->reset_gpio);
@@ -531,7 +565,7 @@ void cs35l41_hda_remove(struct device *dev)
component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
- if (!cs35l41->vspk_always_on)
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
gpiod_put(cs35l41->reset_gpio);
}
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
index 74951001501c..a52ffd1f7999 100644
--- a/sound/pci/hda/cs35l41_hda.h
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -27,39 +27,18 @@ enum cs35l41_hda_gpio_function {
CS35l41_SYNC,
};
-struct cs35l41_hda_reg_sequence {
- const struct reg_sequence *probe;
- unsigned int num_probe;
- const struct reg_sequence *open;
- unsigned int num_open;
- const struct reg_sequence *prepare;
- unsigned int num_prepare;
- const struct reg_sequence *cleanup;
- unsigned int num_cleanup;
- const struct reg_sequence *close;
- unsigned int num_close;
-};
-
-struct cs35l41_hda_hw_config {
- unsigned int spk_pos;
- unsigned int gpio1_func;
- unsigned int gpio2_func;
- int bst_ind;
- int bst_ipk;
- int bst_cap;
-};
-
struct cs35l41_hda {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
- const struct cs35l41_hda_reg_sequence *reg_seq;
+ struct cs35l41_hw_cfg hw_cfg;
int irq;
int index;
-
- /* Don't put the AMP in reset of VSPK can not be turned off */
- bool vspk_always_on;
+ int channel_index;
+ unsigned volatile long irq_errors;
+ const char *amp_name;
+ struct regmap_irq_chip_data *irq_data;
};
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c
index 50eb6c0e6658..22e088f28438 100644
--- a/sound/pci/hda/cs35l41_hda_spi.c
+++ b/sound/pci/hda/cs35l41_hda_spi.c
@@ -48,7 +48,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
static struct spi_driver cs35l41_spi_driver = {
.driver = {
- .name = "cs35l41_hda",
+ .name = "cs35l41-hda",
.acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match),
},
.id_table = cs35l41_hda_spi_id,
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 5cbac315dbe1..7579a6982f47 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -2935,7 +2935,9 @@ static int hda_codec_runtime_suspend(struct device *dev)
if (!codec->card)
return 0;
- cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (!codec->bus->jackpoll_in_suspend)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+
state = hda_call_codec_suspend(codec);
if (codec->link_down_at_suspend ||
(codec_has_clkstop(codec) && codec_has_epss(codec) &&
@@ -2984,6 +2986,9 @@ static void hda_codec_pm_complete(struct device *dev)
static int hda_codec_pm_suspend(struct device *dev)
{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
dev->power.power_state = PMSG_SUSPEND;
return pm_runtime_force_suspend(dev);
}
@@ -2996,6 +3001,9 @@ static int hda_codec_pm_resume(struct device *dev)
static int hda_codec_pm_freeze(struct device *dev)
{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
dev->power.power_state = PMSG_FREEZE;
return pm_runtime_force_suspend(dev);
}
@@ -3038,6 +3046,7 @@ void snd_hda_codec_shutdown(struct hda_codec *codec)
if (!codec->registered)
return;
+ cancel_delayed_work_sync(&codec->jackpoll_work);
list_for_each_entry(cpcm, &codec->pcm_list_head, list)
snd_pcm_suspend_all(cpcm->pcm);
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
index 2e52be6db9c2..e26c896a13f3 100644
--- a/sound/pci/hda/hda_component.h
+++ b/sound/pci/hda/hda_component.h
@@ -15,6 +15,4 @@ struct hda_component {
struct device *dev;
char name[HDA_MAX_NAME_SIZE];
void (*playback_hook)(struct device *dev, int action);
- int (*set_channel_map)(struct device *dev, unsigned int rx_num, unsigned int *rx_slot,
- unsigned int tx_num, unsigned int *tx_slot);
};
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 2347d0304f93..7debb2c76aa6 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -420,6 +420,7 @@ static int hda_tegra_create(struct snd_card *card,
chip->driver_caps = driver_caps;
chip->driver_type = driver_caps & 0xff;
chip->dev_index = 0;
+ chip->jackpoll_interval = msecs_to_jiffies(5000);
INIT_LIST_HEAD(&chip->pcm_list);
chip->codec_probe_mask = -1;
@@ -436,6 +437,7 @@ static int hda_tegra_create(struct snd_card *card,
chip->bus.core.sync_write = 0;
chip->bus.core.needs_damn_long_delay = 1;
chip->bus.core.aligned_mmio = 1;
+ chip->bus.jackpoll_in_suspend = 1;
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 0515137a75b0..aa360a0af284 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -1105,6 +1105,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
static const struct hda_device_id snd_hda_id_conexant[] = {
HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 74c50ec040d9..4f4cc8215917 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -76,67 +76,74 @@ const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
{} /* terminator */
};
+const struct hda_pintbl cs8409_cs42l42_pincfgs_no_dmic[] = {
+ { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */
+ { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ {} /* terminator */
+};
+
/* Vendor specific HW configuration for CS42L42 */
static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x9F },
- { 0x1107, 0x01 },
- { 0x1009, 0x02 },
- { 0x1007, 0x03 },
- { 0x1201, 0x00 },
- { 0x1208, 0x13 },
- { 0x1205, 0xFF },
- { 0x1206, 0x00 },
- { 0x1207, 0x20 },
- { 0x1202, 0x0D },
- { 0x2A02, 0x02 },
- { 0x2A03, 0x00 },
- { 0x2A04, 0x00 },
- { 0x2A05, 0x02 },
- { 0x2A06, 0x00 },
- { 0x2A07, 0x20 },
- { 0x2A08, 0x02 },
- { 0x2A09, 0x00 },
- { 0x2A0A, 0x80 },
- { 0x2A0B, 0x02 },
- { 0x2A0C, 0x00 },
- { 0x2A0D, 0xA0 },
- { 0x2A01, 0x0C },
- { 0x2902, 0x01 },
- { 0x2903, 0x02 },
- { 0x2904, 0x00 },
- { 0x2905, 0x00 },
- { 0x2901, 0x01 },
- { 0x1101, 0x0A },
- { 0x1102, 0x84 },
- { 0x2301, 0x3F },
- { 0x2303, 0x3F },
- { 0x2302, 0x3f },
- { 0x2001, 0x03 },
- { 0x1B75, 0xB6 },
- { 0x1B73, 0xC2 },
- { 0x1129, 0x01 },
- { 0x1121, 0xF3 },
- { 0x1103, 0x20 },
- { 0x1105, 0x00 },
- { 0x1112, 0x00 },
- { 0x1113, 0x80 },
- { 0x1C03, 0xC0 },
- { 0x1101, 0x02 },
- { 0x1316, 0xff },
- { 0x1317, 0xff },
- { 0x1318, 0xff },
- { 0x1319, 0xff },
- { 0x131a, 0xff },
- { 0x131b, 0xff },
- { 0x131c, 0xff },
- { 0x131e, 0xff },
- { 0x131f, 0xff },
- { 0x1320, 0xff },
- { 0x1b79, 0xff },
- { 0x1b7a, 0xff },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_HP_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff },
};
/* Vendor specific hw configuration for CS8409 */
@@ -252,7 +259,6 @@ struct sub_codec cs8409_cs42l42_codec = {
.init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
- .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
@@ -282,115 +288,115 @@ const struct hda_pintbl dolphin_pincfgs[] = {
/* Vendor specific HW configuration for CS42L42 */
static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x9F },
- { 0x1107, 0x01 },
- { 0x1009, 0x02 },
- { 0x1007, 0x03 },
- { 0x1201, 0x00 },
- { 0x1208, 0x13 },
- { 0x1205, 0xFF },
- { 0x1206, 0x00 },
- { 0x1207, 0x20 },
- { 0x1202, 0x0D },
- { 0x2A02, 0x02 },
- { 0x2A03, 0x00 },
- { 0x2A04, 0x00 },
- { 0x2A05, 0x02 },
- { 0x2A06, 0x00 },
- { 0x2A07, 0x20 },
- { 0x2A01, 0x0C },
- { 0x2902, 0x01 },
- { 0x2903, 0x02 },
- { 0x2904, 0x00 },
- { 0x2905, 0x00 },
- { 0x2901, 0x01 },
- { 0x1101, 0x0A },
- { 0x1102, 0x84 },
- { 0x2001, 0x03 },
- { 0x2301, 0x3F },
- { 0x2303, 0x3F },
- { 0x2302, 0x3f },
- { 0x1B75, 0xB6 },
- { 0x1B73, 0xC2 },
- { 0x1129, 0x01 },
- { 0x1121, 0xF3 },
- { 0x1103, 0x20 },
- { 0x1105, 0x00 },
- { 0x1112, 0x00 },
- { 0x1113, 0x80 },
- { 0x1C03, 0xC0 },
- { 0x1101, 0x02 },
- { 0x1316, 0xff },
- { 0x1317, 0xff },
- { 0x1318, 0xff },
- { 0x1319, 0xff },
- { 0x131a, 0xff },
- { 0x131b, 0xff },
- { 0x131c, 0xff },
- { 0x131e, 0xff },
- { 0x131f, 0xff },
- { 0x1320, 0xff },
- { 0x1b79, 0xff },
- { 0x1b7a, 0xff }
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x03 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
};
static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x9F },
- { 0x1107, 0x01 },
- { 0x1009, 0x02 },
- { 0x1007, 0x03 },
- { 0x1201, 0x00 },
- { 0x1208, 0x13 },
- { 0x1205, 0xFF },
- { 0x1206, 0x00 },
- { 0x1207, 0x20 },
- { 0x1202, 0x0D },
- { 0x2A02, 0x02 },
- { 0x2A03, 0x00 },
- { 0x2A04, 0x80 },
- { 0x2A05, 0x02 },
- { 0x2A06, 0x00 },
- { 0x2A07, 0xA0 },
- { 0x2A01, 0x0C },
- { 0x2902, 0x00 },
- { 0x2903, 0x02 },
- { 0x2904, 0x00 },
- { 0x2905, 0x00 },
- { 0x2901, 0x00 },
- { 0x1101, 0x0E },
- { 0x1102, 0x84 },
- { 0x2001, 0x01 },
- { 0x2301, 0x3F },
- { 0x2303, 0x3F },
- { 0x2302, 0x3f },
- { 0x1B75, 0xB6 },
- { 0x1B73, 0xC2 },
- { 0x1129, 0x01 },
- { 0x1121, 0xF3 },
- { 0x1103, 0x20 },
- { 0x1105, 0x00 },
- { 0x1112, 0x00 },
- { 0x1113, 0x80 },
- { 0x1C03, 0xC0 },
- { 0x1101, 0x06 },
- { 0x1316, 0xff },
- { 0x1317, 0xff },
- { 0x1318, 0xff },
- { 0x1319, 0xff },
- { 0x131a, 0xff },
- { 0x131b, 0xff },
- { 0x131c, 0xff },
- { 0x131e, 0xff },
- { 0x131f, 0xff },
- { 0x1320, 0xff },
- { 0x1b79, 0xff },
- { 0x1b7a, 0xff }
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_PWR_CTL1, 0x0E },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x01 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x06 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
};
/* Vendor specific hw configuration for CS8409 */
@@ -444,7 +450,6 @@ struct sub_codec dolphin_cs42l42_0 = {
.init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
- .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
@@ -458,7 +463,6 @@ struct sub_codec dolphin_cs42l42_1 = {
.init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
- .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 1,
@@ -521,6 +525,11 @@ const struct snd_pci_quirk cs8409_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0B95, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
SND_PCI_QUIRK(0x1028, 0x0B96, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0B97, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BA5, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA6, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN),
SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK),
@@ -548,6 +557,7 @@ const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" },
{ .id = CS8409_CYBORG, .name = "cyborg" },
{ .id = CS8409_DOLPHIN, .name = "dolphin" },
+ { .id = CS8409_ODIN, .name = "odin" },
{}
};
@@ -596,4 +606,10 @@ const struct hda_fixup cs8409_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = dolphin_fixups,
},
+ [CS8409_ODIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs_no_dmic,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 343fabc4387d..754aa8ddd2e4 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -419,6 +419,39 @@ static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP));
}
+static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio);
+ return 0;
+}
+
+static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) |
+ (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs8409_spk_sw_gpio_get,
+ .put = cs8409_spk_sw_gpio_put,
+};
+
/******************************************************************************
* CS42L42 Specific Functions
******************************************************************************/
@@ -481,26 +514,26 @@ static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type,
if (mute) {
if (vol_type == CS42L42_VOL_DAC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA, 0x3f);
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f);
if (chs & BIT(1))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB, 0x3f);
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f);
} else if (vol_type == CS42L42_VOL_ADC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL, 0x9f);
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f);
}
} else {
if (vol_type == CS42L42_VOL_DAC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL,
-(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET])
- & CS42L42_REG_HS_VOL_MASK);
+ & CS42L42_MIXER_CH_VOL_MASK);
if (chs & BIT(1))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL,
-(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET])
- & CS42L42_REG_HS_VOL_MASK);
+ & CS42L42_MIXER_CH_VOL_MASK);
} else if (vol_type == CS42L42_VOL_ADC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME,
cs42l42->vol[CS42L42_ADC_VOL_OFFSET]
& CS42L42_REG_AMIC_VOL_MASK);
}
@@ -601,76 +634,167 @@ static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo,
/* Configure CS42L42 slave codec for jack autodetect */
static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
{
- cs8409_i2c_write(cs42l42, 0x1b70, cs42l42->hsbias_hiz);
+ cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz);
/* Clear WAKE# */
- cs8409_i2c_write(cs42l42, 0x1b71, 0x00C1);
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1);
/* Wait ~2.5ms */
usleep_range(2500, 3000);
/* Set mode WAKE# output follows the combination logic directly */
- cs8409_i2c_write(cs42l42, 0x1b71, 0x00C0);
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0);
/* Clear interrupts status */
- cs8409_i2c_read(cs42l42, 0x130f);
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
/* Enable interrupt */
- cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
}
/* Enable and run CS42L42 slave codec jack auto detect */
static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
{
/* Clear interrupts */
- cs8409_i2c_read(cs42l42, 0x1308);
- cs8409_i2c_read(cs42l42, 0x1b77);
- cs8409_i2c_write(cs42l42, 0x1320, 0xFF);
- cs8409_i2c_read(cs42l42, 0x130f);
-
- cs8409_i2c_write(cs42l42, 0x1102, 0x87);
- cs8409_i2c_write(cs42l42, 0x1f06, 0x86);
- cs8409_i2c_write(cs42l42, 0x1b74, 0x07);
- cs8409_i2c_write(cs42l42, 0x131b, 0xFD);
- cs8409_i2c_write(cs42l42, 0x1120, 0x80);
+ cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF);
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87);
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86);
+ cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07);
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
/* Wait ~20ms*/
usleep_range(20000, 25000);
- cs8409_i2c_write(cs42l42, 0x111f, 0x77);
- cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0);
}
-static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
+static int cs42l42_manual_hs_det(struct sub_codec *cs42l42)
{
- int status_changed = cs42l42->force_status_change;
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+ unsigned int hs_type;
- cs42l42->force_status_change = 0;
+ /* Set hs detect to manual, active mode */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
- /* TIP_SENSE INSERT/REMOVE */
- switch (reg_ts_status) {
- case CS42L42_JACK_INSERTED:
- if (!cs42l42->hp_jack_in) {
- if (cs42l42->no_type_dect) {
- status_changed = 1;
- cs42l42->hp_jack_in = 1;
- cs42l42->mic_jack_in = 0;
- } else {
- cs42l42_run_jack_detect(cs42l42);
- }
- }
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ case CS42L42_HSDET_COMP_TYPE3:
+ hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ default:
+ hs_type = CS42L42_PLUG_INVALID;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE4;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
- case CS42L42_JACK_REMOVED:
- if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ return hs_type;
+}
+
+static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
+{
+ int status_changed = 0;
+
+ /* TIP_SENSE INSERT/REMOVE */
+ switch (reg_ts_status) {
+ case CS42L42_TS_PLUG:
+ if (cs42l42->no_type_dect) {
status_changed = 1;
- cs42l42->hp_jack_in = 0;
+ cs42l42->hp_jack_in = 1;
cs42l42->mic_jack_in = 0;
+ } else {
+ cs42l42_run_jack_detect(cs42l42);
}
break;
+
+ case CS42L42_TS_UNPLUG:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
default:
/* jack in transition */
break;
}
+ codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
+
return status_changed;
}
static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
{
+ int current_plug_status;
int status_changed = 0;
int reg_cdc_status;
int reg_hs_status;
@@ -678,46 +802,65 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
int type;
/* Read jack detect status registers */
- reg_cdc_status = cs8409_i2c_read(cs42l42, 0x1308);
- reg_hs_status = cs8409_i2c_read(cs42l42, 0x1124);
- reg_ts_status = cs8409_i2c_read(cs42l42, 0x130f);
+ reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+ reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
/* If status values are < 0, read error has occurred. */
if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
return -EIO;
+ current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK))
+ >> CS42L42_TS_PLUG_SHIFT;
+
/* HSDET_AUTO_DONE */
- if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
+ if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) {
/* Disable HSDET_AUTO_DONE */
- cs8409_i2c_write(cs42l42, 0x131b, 0xFF);
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF);
+
+ type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT;
- type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
+ /* Configure the HSDET mode. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
if (cs42l42->no_type_dect) {
- status_changed = cs42l42_handle_tip_sense(cs42l42, reg_ts_status);
- } else if (type == 4) {
- /* Type 4 not supported */
- status_changed = cs42l42_handle_tip_sense(cs42l42, CS42L42_JACK_REMOVED);
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
} else {
- if (!cs42l42->hp_jack_in) {
- status_changed = 1;
- cs42l42->hp_jack_in = 1;
+ if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) {
+ codec_dbg(cs42l42->codec,
+ "Auto detect value not valid (%d), running manual det\n",
+ type);
+ type = cs42l42_manual_hs_det(cs42l42);
}
- /* type = 3 has no mic */
- if ((!cs42l42->mic_jack_in) && (type != 3)) {
+
+ switch (type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
status_changed = 1;
+ cs42l42->hp_jack_in = 1;
cs42l42->mic_jack_in = 1;
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ break;
+ default:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
}
+ codec_dbg(cs42l42->codec, "Detection done (%d)\n", type);
}
- /* Configure the HSDET mode. */
- cs8409_i2c_write(cs42l42, 0x1120, 0x80);
+
/* Enable the HPOUT ground clamp and configure the HP pull-down */
- cs8409_i2c_write(cs42l42, 0x1F06, 0x02);
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02);
/* Re-Enable Tip Sense Interrupt */
- cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
} else {
- status_changed = cs42l42_handle_tip_sense(cs42l42, reg_ts_status);
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
}
return status_changed;
@@ -726,19 +869,19 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
static void cs42l42_resume(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
- unsigned int gpio_data;
+ struct cs8409_spec *spec = codec->spec;
struct cs8409_i2c_param irq_regs[] = {
- { 0x1308, 0x00 },
- { 0x1309, 0x00 },
- { 0x130A, 0x00 },
- { 0x130F, 0x00 },
+ { CS42L42_CODEC_STATUS, 0x00 },
+ { CS42L42_DET_INT_STATUS1, 0x00 },
+ { CS42L42_DET_INT_STATUS2, 0x00 },
+ { CS42L42_TSRS_PLUG_STATUS, 0x00 },
};
int fsv_old, fsv_new;
/* Bring CS42L42 out of Reset */
- gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
- gpio_data |= cs42l42->reset_gpio;
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data |= cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
usleep_range(10000, 15000);
cs42l42->suspended = 0;
@@ -750,13 +893,13 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
/* Clear interrupts, by reading interrupt status registers */
cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
- fsv_old = cs8409_i2c_read(cs42l42, 0x2001);
+ fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL);
if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB)
fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK;
else
fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK;
if (fsv_new != fsv_old)
- cs8409_i2c_write(cs42l42, 0x2001, fsv_new);
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new);
/* we have to explicitly allow unsol event handling even during the
* resume phase so that the jack event is processed properly
@@ -770,41 +913,40 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
static void cs42l42_suspend(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
- unsigned int gpio_data;
+ struct cs8409_spec *spec = codec->spec;
int reg_cdc_status = 0;
const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = {
- { 0x1F06, 0x02 },
- { 0x1129, 0x00 },
- { 0x2301, 0x3F },
- { 0x2302, 0x3F },
- { 0x2303, 0x3F },
- { 0x2001, 0x0F },
- { 0x2A01, 0x00 },
- { 0x1207, 0x00 },
- { 0x1101, 0xFE },
- { 0x1102, 0x8C },
- { 0x1101, 0xFF },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_HP_CTL, 0x0F },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_PWR_CTL1, 0xFE },
+ { CS42L42_PWR_CTL2, 0x8C },
+ { CS42L42_PWR_CTL1, 0xFF },
};
cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq));
if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status,
(reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US,
- true, cs42l42, 0x1308) < 0)
+ true, cs42l42, CS42L42_CODEC_STATUS) < 0)
codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n");
/* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(cs42l42, 0x1102, 0x9C);
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C);
cs42l42->suspended = 1;
cs42l42->last_page = 0;
cs42l42->hp_jack_in = 0;
cs42l42->mic_jack_in = 0;
- cs42l42->force_status_change = 1;
/* Put CS42L42 into Reset */
- gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
- gpio_data &= ~cs42l42->reset_gpio;
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data &= ~cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
#endif
@@ -918,6 +1060,10 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
/* DMIC1_MO=00b, DMIC1/2_SR=1 */
cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003);
break;
+ case CS8409_ODIN:
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */
+ cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00);
+ break;
default:
break;
}
@@ -994,6 +1140,8 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->gen.no_primary_hp = 1;
spec->gen.suppress_vmaster = 1;
+ spec->speaker_pdn_gpio = 0;
+
/* GPIO 5 out, 3,4 in */
spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
spec->gpio_data = 0;
@@ -1005,20 +1153,35 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
- /* Set HSBIAS_SENSE_EN and Full Scale volume for some variants. */
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+
switch (codec->fixup_id) {
+ case CS8409_CYBORG:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol =
+ CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
+ case CS8409_ODIN:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
case CS8409_WARLOCK_MLK:
case CS8409_WARLOCK_MLK_DUAL_MIC:
- spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
break;
default:
- spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
spec->scodecs[CS8409_CODEC0]->full_scale_vol =
CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
break;
}
+ if (spec->speaker_pdn_gpio > 0) {
+ spec->gpio_dir |= spec->speaker_pdn_gpio;
+ spec->gpio_data |= spec->speaker_pdn_gpio;
+ }
+
break;
case HDA_FIXUP_ACT_PROBE:
/* Fix Sample Rate to 48kHz */
@@ -1027,13 +1190,17 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
/* add hooks */
spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
- /* Set initial DMIC volume to -26 dB */
- snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
- HDA_INPUT, 0, 0xff, 0x19);
+ if (codec->fixup_id != CS8409_ODIN)
+ /* Set initial DMIC volume to -26 dB */
+ snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
+ HDA_INPUT, 0, 0xff, 0x19);
snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
&cs42l42_adc_volume_mixer);
+ if (spec->speaker_pdn_gpio > 0)
+ snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
+ &cs8409_spk_sw_ctrl);
/* Disable Unsolicited Response during boot */
cs8409_enable_ur(codec, 0);
snd_hda_codec_set_name(codec, "CS8409/CS42L42");
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 7df46bd8d2da..260388a6256c 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <sound/tlv.h>
#include <linux/workqueue.h>
+#include <sound/cs42l42.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
@@ -222,15 +223,8 @@ enum cs8409_coefficient_index_registers {
#define CS42L42_HP_VOL_REAL_MAX (0)
#define CS42L42_AMIC_VOL_REAL_MIN (-97)
#define CS42L42_AMIC_VOL_REAL_MAX (12)
-#define CS42L42_REG_HS_VOL_CHA (0x2301)
-#define CS42L42_REG_HS_VOL_CHB (0x2303)
-#define CS42L42_REG_HS_VOL_MASK (0x003F)
-#define CS42L42_REG_AMIC_VOL (0x1D03)
#define CS42L42_REG_AMIC_VOL_MASK (0x00FF)
-#define CS42L42_HSDET_AUTO_DONE (0x02)
#define CS42L42_HSTYPE_MASK (0x03)
-#define CS42L42_JACK_INSERTED (0x0C)
-#define CS42L42_JACK_REMOVED (0x00)
#define CS42L42_I2C_TIMEOUT_US (20000)
#define CS42L42_I2C_SLEEP_US (2000)
#define CS42L42_PDN_TIMEOUT_US (250000)
@@ -244,6 +238,8 @@ enum cs8409_coefficient_index_registers {
#define CS42L42_I2C_ADDR (0x48 << 1)
#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */
#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */
+#define CS8409_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */
+#define CS8409_WARLOCK_SPEAKER_PDN GENMASK(1, 1) /* CS8409_GPIO1 */
#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A
#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
@@ -273,6 +269,7 @@ enum {
CS8409_FIXUPS,
CS8409_DOLPHIN,
CS8409_DOLPHIN_FIXUPS,
+ CS8409_ODIN,
};
enum {
@@ -310,7 +307,6 @@ struct sub_codec {
unsigned int hp_jack_in:1;
unsigned int mic_jack_in:1;
- unsigned int force_status_change:1;
unsigned int suspended:1;
unsigned int paged:1;
unsigned int last_page;
@@ -332,6 +328,8 @@ struct cs8409_spec {
unsigned int gpio_dir;
unsigned int gpio_data;
+ int speaker_pdn_gpio;
+
struct mutex i2c_mux;
unsigned int i2c_clck_enabled;
unsigned int dev_addr;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ad292df7d805..80e4955e8c10 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3131,6 +3131,7 @@ enum {
ALC269_TYPE_ALC257,
ALC269_TYPE_ALC215,
ALC269_TYPE_ALC225,
+ ALC269_TYPE_ALC245,
ALC269_TYPE_ALC287,
ALC269_TYPE_ALC294,
ALC269_TYPE_ALC300,
@@ -3168,6 +3169,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC257:
case ALC269_TYPE_ALC215:
case ALC269_TYPE_ALC225:
+ case ALC269_TYPE_ALC245:
case ALC269_TYPE_ALC287:
case ALC269_TYPE_ALC294:
case ALC269_TYPE_ALC300:
@@ -3695,7 +3697,8 @@ static void alc225_init(struct hda_codec *codec)
hda_nid_t hp_pin = alc_get_hp_pin(spec);
bool hp1_pin_sense, hp2_pin_sense;
- if (spec->codec_variant != ALC269_TYPE_ALC287)
+ if (spec->codec_variant != ALC269_TYPE_ALC287 &&
+ spec->codec_variant != ALC269_TYPE_ALC245)
/* required only at boot or S3 and S4 resume time */
if (!spec->done_hp_init ||
is_s3_resume(codec) ||
@@ -6580,18 +6583,6 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
}
}
-static int find_comp_by_dev_name(struct alc_spec *spec, const char *name)
-{
- int i;
-
- for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
- if (strcmp(spec->comps[i].name, name) == 0)
- return i;
- }
-
- return -ENODEV;
-}
-
static int comp_bind(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
@@ -6666,50 +6657,10 @@ static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fix
cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 4);
}
-static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
- struct snd_pcm_substream *sub, int action)
-{
- struct alc_spec *spec = cdc->spec;
- unsigned int rx_slot;
- int i;
-
- switch (action) {
- case HDA_GEN_PCM_ACT_PREPARE:
- rx_slot = 0;
- i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0");
- if (i >= 0)
- spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot);
-
- rx_slot = 1;
- i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1");
- if (i >= 0)
- spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot);
- break;
- }
-
- comp_generic_playback_hook(hinfo, cdc, sub, action);
-}
-
static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
int action)
{
- struct device *dev = hda_codec_dev(cdc);
- struct alc_spec *spec = cdc->spec;
- int ret;
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- component_match_add(dev, &spec->match, component_compare_dev_name,
- "i2c-CLSA0100:00-cs35l41-hda.0");
- component_match_add(dev, &spec->match, component_compare_dev_name,
- "i2c-CLSA0100:00-cs35l41-hda.1");
- ret = component_master_add_with_match(dev, &comp_master_ops, spec->match);
- if (ret)
- codec_err(cdc, "Fail to register component aggregator %d\n", ret);
- else
- spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook;
- break;
- }
+ cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2);
}
/* for alc295_fixup_hp_top_speakers */
@@ -10148,7 +10099,10 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0245:
case 0x10ec0285:
case 0x10ec0289:
- spec->codec_variant = ALC269_TYPE_ALC215;
+ if (alc_get_coef0(codec) & 0x0010)
+ spec->codec_variant = ALC269_TYPE_ALC245;
+ else
+ spec->codec_variant = ALC269_TYPE_ALC215;
spec->shutup = alc225_shutup;
spec->init_hook = alc225_init;
spec->gen.mixer_nid = 0;
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
index 738ec987000a..32193fae978d 100644
--- a/sound/pci/lola/lola_pcm.c
+++ b/sound/pci/lola/lola_pcm.c
@@ -561,8 +561,9 @@ static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
{
int i;
+ u8 num_streams = min_t(u8, pcm->num_streams, ARRAY_SIZE(pcm->streams));
- for (i = 0; bits && i < pcm->num_streams; i++) {
+ for (i = 0; bits && i < num_streams; i++) {
if (bits & (1 << i)) {
struct lola_stream *str = &pcm->streams[i];
if (str->substream && str->running)
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 3db641318d3a..dcc43a81ae0e 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -3314,7 +3314,7 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
if (hdsp->io_type == RPM) {
/* RPM Bypass, Disconnect and Input switches */
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_rpm_controls); idx++) {
- err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
if (err < 0)
return err;
}
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 5dcf77af07af..7d4747b6bab2 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -14,7 +14,7 @@ menuconfig SND_SOC
If you want ASoC support, you should say Y here and also to the
specific driver for your SoC platform below.
-
+
ASoC provides power efficient ALSA support for embedded battery powered
SoC based systems like PDA's, Phones and Personal Media Players.
@@ -55,6 +55,13 @@ config SND_SOC_TOPOLOGY_KUNIT_TEST
userspace applications such as pulseaudio, to prevent unnecessary
problems.
+config SND_SOC_UTILS_KUNIT_TEST
+ tristate "KUnit tests for SoC utils"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ If you want to perform tests on ALSA SoC utils library say Y here.
+
config SND_SOC_ACPI
tristate
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index a7b37c06dc43..d4528962ac34 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -12,6 +12,11 @@ ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),)
obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) := soc-topology-test.o
endif
+ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),)
+# snd-soc-test-objs := soc-utils-test.o
+obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) := soc-utils-test.o
+endif
+
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
endif
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index 3bf86c2424ae..ef1b4cefc273 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -71,7 +71,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &cz_jack, NULL, 0);
+ &cz_jack);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
@@ -151,7 +151,7 @@ static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &cz_jack, NULL, 0);
+ &cz_jack);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
index a79a46646d50..532aa98a2241 100644
--- a/sound/soc/amd/acp-rt5645.c
+++ b/sound/soc/amd/acp-rt5645.c
@@ -80,7 +80,7 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &cz_jack, NULL, 0);
+ &cz_jack);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 626e4a5cb06a..9dae2719084c 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -42,7 +42,7 @@ config SND_AMD_ASOC_RENOIR
config SND_SOC_AMD_MACH_COMMON
tristate
- depends on X86 && PCI && I2C && GPIOLIB
+ depends on X86 && PCI && I2C
select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
@@ -54,14 +54,14 @@ config SND_SOC_AMD_MACH_COMMON
config SND_SOC_AMD_LEGACY_MACH
tristate "AMD Legacy Machine Driver Support"
- depends on X86 && PCI && I2C && GPIOLIB
+ depends on X86 && PCI && I2C
select SND_SOC_AMD_MACH_COMMON
help
This option enables legacy sound card support for ACP audio.
config SND_SOC_AMD_SOF_MACH
tristate "AMD SOF Machine Driver Support"
- depends on X86 && PCI && I2C && GPIOLIB
+ depends on X86 && PCI && I2C
select SND_SOC_AMD_MACH_COMMON
help
This option enables SOF sound card support for ACP audio.
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
index 5d276365d644..7f04a048ca3a 100644
--- a/sound/soc/amd/acp/acp-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -27,7 +27,6 @@ static struct acp_card_drvdata rt5682_rt1019_data = {
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
- .gpio_spkr_en = EN_SPKR_GPIO_GB,
};
static struct acp_card_drvdata rt5682s_max_data = {
@@ -37,7 +36,6 @@ static struct acp_card_drvdata rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
- .gpio_spkr_en = EN_SPKR_GPIO_NONE,
};
static struct acp_card_drvdata rt5682s_rt1019_data = {
@@ -47,7 +45,6 @@ static struct acp_card_drvdata rt5682s_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
- .gpio_spkr_en = EN_SPKR_GPIO_NONE,
};
static const struct snd_kcontrol_new acp_controls[] = {
@@ -62,16 +59,15 @@ static const struct snd_kcontrol_new acp_controls[] = {
static const struct snd_soc_dapm_widget acp_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_SPK("Spk", event_spkr_handler),
- SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler),
- SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static int acp_asoc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
- unsigned int spkr_gpio;
int ret;
if (!pdev->id_entry)
@@ -89,20 +85,9 @@ static int acp_asoc_probe(struct platform_device *pdev)
card->controls = acp_controls;
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
- spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en;
acp_legacy_dai_links_create(card);
- if (gpio_is_valid(spkr_gpio)) {
- ret = devm_gpio_request(dev, spkr_gpio, "spkren");
- if (ret) {
- dev_err(dev, "(%s) gpio request failed: %d\n",
- __func__, ret);
- return ret;
- }
- gpio_direction_output(spkr_gpio, 0);
- }
-
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
@@ -131,6 +116,7 @@ static const struct platform_device_id board_ids[] = {
};
static struct platform_driver acp_asoc_audio = {
.driver = {
+ .pm = &snd_soc_pm_ops,
.name = "acp_mach",
},
.probe = acp_asoc_probe,
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index caa202f7864e..6ae454bf60af 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -71,31 +71,6 @@ static const struct snd_soc_dapm_route rt5682_map[] = {
{ "IN1P", NULL, "Headset Mic" },
};
-int event_spkr_handler(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct acp_card_drvdata *drvdata = snd_soc_card_get_drvdata(card);
-
- if (!gpio_is_valid(drvdata->gpio_spkr_en))
- return 0;
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- gpio_set_value(drvdata->gpio_spkr_en, 1);
- break;
- case SND_SOC_DAPM_PRE_PMD:
- gpio_set_value(drvdata->gpio_spkr_en, 0);
- break;
- default:
- dev_warn(card->dev, "%s invalid setting\n", __func__);
- break;
- }
- return 0;
-}
-EXPORT_SYMBOL_NS_GPL(event_spkr_handler, SND_SOC_AMD_MACH);
-
/* Define card ops for RT5682 CODEC */
static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -145,7 +120,7 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pco_jack, NULL, 0);
+ &pco_jack);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
@@ -266,7 +241,7 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pco_jack, NULL, 0);
+ &pco_jack);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index c855f50d6b34..5dc47cfbff10 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -17,11 +17,6 @@
#include <linux/input.h>
#include <linux/module.h>
#include <sound/soc.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-
-#define EN_SPKR_GPIO_GB 0x11F
-#define EN_SPKR_GPIO_NONE -EINVAL
enum be_id {
HEADSET_BE_ID = 0,
@@ -54,11 +49,9 @@ struct acp_card_drvdata {
unsigned int dai_fmt;
struct clk *wclk;
struct clk *bclk;
- unsigned int gpio_spkr_en;
};
int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
int acp_legacy_dai_links_create(struct snd_soc_card *card);
-int event_spkr_handler(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event);
+
#endif
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index 3346677949e3..d1531cdab110 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -27,7 +27,6 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = {
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
- .gpio_spkr_en = EN_SPKR_GPIO_GB,
};
static struct acp_card_drvdata sof_rt5682_max_data = {
@@ -37,7 +36,6 @@ static struct acp_card_drvdata sof_rt5682_max_data = {
.hs_codec_id = RT5682,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
- .gpio_spkr_en = EN_SPKR_GPIO_NONE,
};
static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
@@ -56,7 +54,6 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
- .gpio_spkr_en = EN_SPKR_GPIO_NONE,
};
static const struct snd_kcontrol_new acp_controls[] = {
@@ -70,16 +67,15 @@ static const struct snd_kcontrol_new acp_controls[] = {
static const struct snd_soc_dapm_widget acp_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_SPK("Spk", event_spkr_handler),
- SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler),
- SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static int acp_sof_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
- unsigned int spkr_gpio;
int ret;
if (!pdev->id_entry)
@@ -97,20 +93,9 @@ static int acp_sof_probe(struct platform_device *pdev)
card->controls = acp_controls;
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
- spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en;
acp_sofdsp_dai_links_create(card);
- if (gpio_is_valid(spkr_gpio)) {
- ret = devm_gpio_request(dev, spkr_gpio, "spkren");
- if (ret) {
- dev_err(dev, "(%s) gpio request failed: %d\n",
- __func__, ret);
- return ret;
- }
- gpio_direction_output(spkr_gpio, 0);
- }
-
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
@@ -144,6 +129,7 @@ static const struct platform_device_id board_ids[] = {
static struct platform_driver acp_asoc_audio = {
.driver = {
.name = "sof_mach",
+ .pm = &snd_soc_pm_ops,
},
.probe = acp_sof_probe,
.id_table = board_ids,
diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c
index dad70436d063..0543dda75b99 100644
--- a/sound/soc/amd/acp3x-rt5682-max9836.c
+++ b/sound/soc/amd/acp3x-rt5682-max9836.c
@@ -90,7 +90,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pco_jack, NULL, 0);
+ &pco_jack);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
index 1551546c3050..727de46860b1 100644
--- a/sound/soc/amd/vangogh/acp5x-mach.c
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -17,10 +17,8 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
-#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
-#include <linux/io.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -61,10 +59,10 @@ static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
* Headset buttons map to the google Reference headset.
* These can be configured by userspace.
*/
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &vg_headset, acp5x_nau8821_jack_pins,
- ARRAY_SIZE(acp5x_nau8821_jack_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &vg_headset, acp5x_nau8821_jack_pins,
+ ARRAY_SIZE(acp5x_nau8821_jack_pins));
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index 9a767f47b89f..f06e6c1a7799 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -12,6 +12,7 @@
#include <sound/pcm_params.h>
#include <linux/io.h>
#include <linux/dmi.h>
+#include <linux/acpi.h>
#include "acp6x.h"
@@ -45,108 +46,126 @@ static struct snd_soc_card acp6x_card = {
static const struct dmi_system_id yc_acp_quirk_table[] = {
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21AW"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21AX"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21BN"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21BQ"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D8"),
}
},
{
+ .driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D9"),
@@ -157,18 +176,33 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
static int acp6x_probe(struct platform_device *pdev)
{
+ const struct dmi_system_id *dmi_id;
struct acp6x_pdm *machine = NULL;
struct snd_soc_card *card;
+ struct acpi_device *adev;
int ret;
- const struct dmi_system_id *dmi_id;
+ /* check the parent device's firmware node has _DSD or not */
+ adev = ACPI_COMPANION(pdev->dev.parent);
+ if (adev) {
+ const union acpi_object *obj;
+
+ if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == 1)
+ platform_set_drvdata(pdev, &acp6x_card);
+ }
+
+ /* check for any DMI overrides */
dmi_id = dmi_first_match(yc_acp_quirk_table);
- if (!dmi_id)
+ if (dmi_id)
+ platform_set_drvdata(pdev, dmi_id->driver_data);
+
+ card = platform_get_drvdata(pdev);
+ if (!card)
return -ENODEV;
- card = &acp6x_card;
+ dev_info(&pdev->dev, "Enabling ACP DMIC support via %s", dmi_id ? "DMI" : "ACPI");
acp6x_card.dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c
index 7e9a9a9d8ddd..20f7a99783f2 100644
--- a/sound/soc/amd/yc/pci-acp6x.c
+++ b/sound/soc/amd/yc/pci-acp6x.c
@@ -154,9 +154,14 @@ static int snd_acp6x_probe(struct pci_dev *pci,
irqflags = IRQF_SHARED;
/* Yellow Carp device check */
- if (pci->revision != 0x60)
+ switch (pci->revision) {
+ case 0x60:
+ case 0x6f:
+ break;
+ default:
+ dev_err(&pci->dev, "acp6x pci device not found\n");
return -ENODEV;
-
+ }
if (pci_enable_device(pci)) {
dev_err(&pci->dev, "pci_enable_device failed\n");
return -ENODEV;
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 795c0b0b527a..5d59e00be823 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -42,9 +42,9 @@ config SND_ATMEL_SOC_SSC_DMA
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
depends on ARCH_AT91 || COMPILE_TEST
- depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
+ depends on ATMEL_SSC && I2C
select SND_ATMEL_SOC_SSC_PDC
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Say Y if you want to add support for SoC audio on WM8731-based
AT91sam9g20 evaluation board.
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index a9f9f449c48c..74b7b2611aa7 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -458,7 +458,6 @@ static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
.num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
.idle_bias_on = 1,
.use_pmdown_time = 1,
- .endianness = 1,
};
/* ASoC sound card */
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index 42117de299e7..ea34efac2fff 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -481,7 +481,6 @@ static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
.num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
.idle_bias_on = 1,
.use_pmdown_time = 1,
- .endianness = 1,
};
/* ASoC sound card */
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 0d639a33ad96..4d25fb61c652 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -127,8 +127,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
ret = atmel_ssc_set_audio(0);
if (ret) {
- dev_err(&pdev->dev, "ssc channel is not valid\n");
- return -EINVAL;
+ dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
+ return ret;
}
card->dev = &pdev->dev;
@@ -148,7 +148,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
if (!codec_np) {
dev_err(&pdev->dev, "codec info missing\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
at91sam9g20ek_dai.codecs->of_node = codec_np;
@@ -159,7 +160,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
if (!cpu_np) {
dev_err(&pdev->dev, "dai and pcm info missing\n");
of_node_put(codec_np);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
at91sam9g20ek_dai.cpus->of_node = cpu_np;
at91sam9g20ek_dai.platforms->of_node = cpu_np;
@@ -169,10 +171,12 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
ret = snd_soc_register_card(card);
if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed\n");
+ dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card() failed: %d\n", ret);
+ goto err;
}
- return ret;
+ return 0;
err:
atmel_ssc_put_audio(0);
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
index 38de7c0efbc7..8a78809e8754 100644
--- a/sound/soc/au1x/Kconfig
+++ b/sound/soc/au1x/Kconfig
@@ -58,7 +58,7 @@ config SND_SOC_DB1200
select SND_SOC_AC97_CODEC
select SND_SOC_WM9712
select SND_SOC_AU1XPSC_I2S
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Select this option to enable audio (AC97 and I2S) on the
Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards.
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f46a22660103..6165db92a629 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -65,6 +65,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS35L36
imply SND_SOC_CS35L41_SPI
imply SND_SOC_CS35L41_I2C
+ imply SND_SOC_CS35L45_I2C
+ imply SND_SOC_CS35L45_SPI
imply SND_SOC_CS42L42
imply SND_SOC_CS42L51_I2C
imply SND_SOC_CS42L52
@@ -127,6 +129,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_MAX98373_I2C
imply SND_SOC_MAX98373_SDW
imply SND_SOC_MAX98390
+ imply SND_SOC_MAX98396
imply SND_SOC_MAX9850
imply SND_SOC_MAX9860
imply SND_SOC_MAX9759
@@ -168,6 +171,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT1011
imply SND_SOC_RT1015
imply SND_SOC_RT1015P
+ imply SND_SOC_RT1016
imply SND_SOC_RT1019
imply SND_SOC_RT1305
imply SND_SOC_RT1308
@@ -265,7 +269,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_WM8711
imply SND_SOC_WM8727
imply SND_SOC_WM8728
- imply SND_SOC_WM8731
+ imply SND_SOC_WM8731_I2C
+ imply SND_SOC_WM8731_SPI
imply SND_SOC_WM8737
imply SND_SOC_WM8741
imply SND_SOC_WM8750
@@ -655,6 +660,34 @@ config SND_SOC_CS35L41_I2C
select SND_SOC_CS35L41
select REGMAP_I2C
+config SND_SOC_CS35L45_TABLES
+ tristate
+
+config SND_SOC_CS35L45
+ tristate
+
+config SND_SOC_CS35L45_SPI
+ tristate "Cirrus Logic CS35L45 CODEC (SPI)"
+ depends on SPI_MASTER
+ select REGMAP
+ select REGMAP_SPI
+ select SND_SOC_CS35L45_TABLES
+ select SND_SOC_CS35L45
+ help
+ Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+ with SPI control.
+
+config SND_SOC_CS35L45_I2C
+ tristate "Cirrus Logic CS35L45 CODEC (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS35L45_TABLES
+ select SND_SOC_CS35L45
+ help
+ Enable support for Cirrus Logic CS35L45 smart speaker amplifier
+ with I2C control.
+
config SND_SOC_CS42L42
tristate "Cirrus Logic CS42L42 CODEC"
depends on I2C
@@ -953,7 +986,6 @@ config SND_SOC_MAX98095
config SND_SOC_MAX98357A
tristate "Maxim MAX98357A CODEC"
- depends on GPIOLIB
config SND_SOC_MAX98371
tristate
@@ -1015,6 +1047,15 @@ config SND_SOC_MAX98390
tristate "Maxim Integrated MAX98390 Speaker Amplifier"
depends on I2C
+config SND_SOC_MAX98396
+ tristate "Analog Devices MAX98396 Speaker Amplifier"
+ depends on I2C
+ help
+ Enable support for Analog Devices MAX98396 audio
+ amplifier. The device provides a PCM interface for
+ audio data and a standard I2C interface for control
+ data communication.
+
config SND_SOC_MAX9850
tristate
depends on I2C
@@ -1213,7 +1254,10 @@ config SND_SOC_RT1015
config SND_SOC_RT1015P
tristate
- depends on GPIOLIB
+
+config SND_SOC_RT1016
+ tristate
+ depends on I2C
config SND_SOC_RT1019
tristate
@@ -1753,8 +1797,19 @@ config SND_SOC_WM8728
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
- tristate "Wolfson Microelectronics WM8731 CODEC"
- depends on SND_SOC_I2C_AND_SPI
+ tristate
+
+config SND_SOC_WM8731_I2C
+ tristate "Wolfson Microelectronics WM8731 CODEC with I2C"
+ depends on I2C
+ select REGMAP
+ select SND_SOC_WM8731
+
+config SND_SOC_WM8731_SPI
+ tristate "Wolfson Microelectronics WM8731 CODEC with SPI"
+ depends on SPI
+ select REGMAP
+ select SND_SOC_WM8731
config SND_SOC_WM8737
tristate "Wolfson Microelectronics WM8737 ADC"
@@ -1811,7 +1866,7 @@ config SND_SOC_WM8904
depends on I2C
config SND_SOC_WM8940
- tristate
+ tristate "Wolfson Microelectronics WM8940 codec"
depends on I2C
config SND_SOC_WM8955
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 8637e9e869e3..28dc4edfd01f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -60,6 +60,10 @@ snd-soc-cs35l41-lib-objs := cs35l41-lib.o
snd-soc-cs35l41-objs := cs35l41.o
snd-soc-cs35l41-spi-objs := cs35l41-spi.o
snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o
+snd-soc-cs35l45-tables-objs := cs35l45-tables.o
+snd-soc-cs35l45-objs := cs35l45.o
+snd-soc-cs35l45-spi-objs := cs35l45-spi.o
+snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -135,6 +139,7 @@ snd-soc-max98373-objs := max98373.o
snd-soc-max98373-i2c-objs := max98373-i2c.o
snd-soc-max98373-sdw-objs := max98373-sdw.o
snd-soc-max98390-objs := max98390.o
+snd-soc-max98396-objs := max98396.o
snd-soc-max9850-objs := max9850.o
snd-soc-max9860-objs := max9860.o
snd-soc-mc13783-objs := mc13783.o
@@ -181,6 +186,7 @@ snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt1011-objs := rt1011.o
snd-soc-rt1015-objs := rt1015.o
snd-soc-rt1015p-objs := rt1015p.o
+snd-soc-rt1016-objs := rt1016.o
snd-soc-rt1019-objs := rt1019.o
snd-soc-rt1305-objs := rt1305.o
snd-soc-rt1308-objs := rt1308.o
@@ -290,6 +296,8 @@ snd-soc-wm8711-objs := wm8711.o
snd-soc-wm8727-objs := wm8727.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
+snd-soc-wm8731-i2c-objs := wm8731-i2c.o
+snd-soc-wm8731-spi-objs := wm8731-spi.o
snd-soc-wm8737-objs := wm8737.o
snd-soc-wm8741-objs := wm8741.o
snd-soc-wm8750-objs := wm8750.o
@@ -404,6 +412,10 @@ obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o
obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o
obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o
obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L45_TABLES) += snd-soc-cs35l45-tables.o
+obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o
+obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
+obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
@@ -474,6 +486,7 @@ obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o
obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o
obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o
obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o
+obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
@@ -520,6 +533,7 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o
obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o
+obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o
obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
@@ -632,6 +646,8 @@ obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8731_I2C) += snd-soc-wm8731-i2c.o
+obj-$(CONFIG_SND_SOC_WM8731_SPI) += snd-soc-wm8731-spi.o
obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o
obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index 3d509a65e4ca..4cb8d87f9011 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -20,10 +20,10 @@ static const struct i2c_device_id ad193x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
-static int ad193x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ad193x_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(ad193x_id, client);
config = ad193x_regmap_config;
config.val_bits = 8;
@@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
- .probe = ad193x_i2c_probe,
+ .probe_new = 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 fc87a76ff1ee..8ed0ffdedbc9 100644
--- a/sound/soc/codecs/adau1372-i2c.c
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -14,7 +14,7 @@
#include "adau1372.h"
-static int adau1372_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int adau1372_i2c_probe(struct i2c_client *client)
{
return adau1372_probe(&client->dev,
devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL);
@@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = {
.driver = {
.name = "adau1372",
},
- .probe = adau1372_i2c_probe,
+ .probe_new = adau1372_i2c_probe,
.id_table = adau1372_i2c_ids,
};
module_i2c_driver(adau1372_i2c_driver);
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
index 1faa4c426365..a9f89e8565ec 100644
--- a/sound/soc/codecs/adau1372.c
+++ b/sound/soc/codecs/adau1372.c
@@ -859,6 +859,7 @@ static const struct snd_soc_component_driver adau1372_driver = {
.num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets),
.dapm_routes = adau1372_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes),
+ .endianness = 1,
};
static const struct snd_soc_dai_ops adau1372_dai_ops = {
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 46128aaceae9..a9032b5c8d78 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1473,8 +1473,7 @@ static const struct snd_soc_component_driver adau1373_component_driver = {
.non_legacy_dai_naming = 1,
};
-static int adau1373_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1373_i2c_probe(struct i2c_client *client)
{
struct adau1373 *adau1373;
int ret;
@@ -1508,7 +1507,7 @@ static struct i2c_driver adau1373_i2c_driver = {
.driver = {
.name = "adau1373",
},
- .probe = adau1373_i2c_probe,
+ .probe_new = adau1373_i2c_probe,
.id_table = adau1373_i2c_id,
};
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index dba9af753188..98768e5300f0 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -785,8 +785,7 @@ static const struct regmap_config adau1701_regmap = {
.reg_read = adau1701_reg_read,
};
-static int adau1701_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1701_i2c_probe(struct i2c_client *client)
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
@@ -878,7 +877,7 @@ static struct i2c_driver adau1701_i2c_driver = {
.name = "adau1701",
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
- .probe = adau1701_i2c_probe,
+ .probe_new = 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 c8fce37e5cfa..0683caf86aea 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -14,10 +14,12 @@
#include "adau1761.h"
-static int adau1761_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct i2c_device_id adau1761_i2c_ids[];
+
+static int adau1761_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client);
config = adau1761_regmap_config;
config.val_bits = 8;
@@ -59,7 +61,7 @@ static struct i2c_driver adau1761_i2c_driver = {
.name = "adau1761",
.of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
},
- .probe = adau1761_i2c_probe,
+ .probe_new = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
.id_table = adau1761_i2c_ids,
};
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index fb006fc81653..8f887227981f 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -556,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" },
- { "DSP", NULL, "Digital Clock 0" },
-
{ "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
@@ -569,6 +567,56 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Digital Clock 1", NULL, "SYSCLK" },
};
+static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = {
+ { "DSP", NULL, "Digital Clock 0" },
+};
+
+static int adau1761_compatibility_probe(struct device *dev)
+{
+ struct adau *adau = dev_get_drvdata(dev);
+ struct regmap *regmap = adau->regmap;
+ int val, ret = 0;
+
+ /* Only consider compatibility mode when ADAU1361 was specified. */
+ if (adau->type != ADAU1361)
+ return 0;
+
+ regcache_cache_bypass(regmap, true);
+
+ /*
+ * This will enable the core clock and bypass the PLL,
+ * so that we can access the registers for probing purposes
+ * (without having to set up the PLL).
+ */
+ regmap_write(regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+ /*
+ * ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips;
+ * reading it results in zero at all times, and write is a no-op.
+ * Use this register to probe for ADAU1761.
+ */
+ regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1);
+ ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+ if (ret)
+ goto exit;
+ if (val != 1)
+ goto exit;
+ regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0);
+ ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
+ if (ret)
+ goto exit;
+ if (val != 0)
+ goto exit;
+
+ adau->type = ADAU1761_AS_1361;
+exit:
+ /* Disable core clock after probing. */
+ regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0);
+ regcache_cache_bypass(regmap, false);
+ return ret;
+}
+
static int adau1761_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
@@ -823,7 +871,11 @@ static int adau1761_component_probe(struct snd_soc_component *component)
if (ret)
return ret;
- if (adau->type == ADAU1761) {
+ /*
+ * If we've got an ADAU1761, or an ADAU1761 operating as an
+ * ADAU1361, we need these non-DSP related DAPM widgets and routes.
+ */
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) {
ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
ARRAY_SIZE(adau1761_dapm_widgets));
if (ret)
@@ -834,7 +886,29 @@ static int adau1761_component_probe(struct snd_soc_component *component)
if (ret)
return ret;
}
-
+ /*
+ * These routes are DSP related and only used when we have a
+ * bona fide ADAU1761.
+ */
+ if (adau->type == ADAU1761) {
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes,
+ ARRAY_SIZE(adau1761_dapm_dsp_routes));
+ if (ret)
+ return ret;
+ }
+ /*
+ * In the ADAU1761, by default, the AIF is routed to the DSP, whereas
+ * for the ADAU1361, the AIF is permanently routed to the ADC and DAC.
+ * Thus, if we have an ADAU1761 masquerading as an ADAU1361,
+ * we need to explicitly route the AIF to the ADC and DAC.
+ * For the ADAU1761, this is normally done by set_tdm_slot, but this
+ * function is not necessarily called during stream setup, so set up
+ * the compatible AIF routings here from the start.
+ */
+ if (adau->type == ADAU1761_AS_1361) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01);
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01);
+ }
ret = adau17x1_add_routes(component);
if (ret < 0)
return ret;
@@ -919,6 +993,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
+ ret = adau1761_compatibility_probe(dev);
+ if (ret)
+ return ret;
+
/* Enable cache only mode as we could miss writes before bias level
* reaches standby and the core clock is enabled */
regcache_cache_only(regmap, true);
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 1c476429ad99..e046de0ebcc7 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -14,10 +14,12 @@
#include "adau1781.h"
-static int adau1781_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct i2c_device_id adau1781_i2c_ids[];
+
+static int adau1781_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client);
config = adau1781_regmap_config;
config.val_bits = 8;
@@ -55,7 +57,7 @@ static struct i2c_driver adau1781_i2c_driver = {
.name = "adau1781",
.of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
},
- .probe = adau1781_i2c_probe,
+ .probe_new = 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 af05463af4ac..c0f44ecef606 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -334,6 +334,17 @@ static bool adau17x1_has_dsp(struct adau *adau)
}
}
+/* Chip has a DSP but we're pretending it doesn't. */
+static bool adau17x1_has_disused_dsp(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761_AS_1361:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool adau17x1_has_safeload(struct adau *adau)
{
switch (adau->type) {
@@ -516,10 +527,11 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div);
- if (adau17x1_has_dsp(adau)) {
+
+ if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau))
regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+ if (adau17x1_has_dsp(adau))
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
- }
if (adau->sigmadsp) {
ret = adau17x1_setup_firmware(component, params_rate(params));
@@ -663,7 +675,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
switch (slot_width * slots) {
case 32:
- if (adau->type == ADAU1761)
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
@@ -738,7 +750,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
- if (!adau17x1_has_dsp(adau))
+ if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index 98a3b6f5bc96..5e58abfffc3d 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -10,6 +10,7 @@
enum adau17x1_type {
ADAU1361,
ADAU1761,
+ ADAU1761_AS_1361,
ADAU1381,
ADAU1781,
};
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
index 82a49c85d536..9f137a0634d5 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -14,10 +14,12 @@
#include "adau1977.h"
-static int adau1977_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct i2c_device_id adau1977_i2c_ids[];
+
+static int adau1977_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client);
config = adau1977_regmap_config;
config.val_bits = 8;
@@ -40,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = {
.driver = {
.name = "adau1977",
},
- .probe = adau1977_i2c_probe,
+ .probe_new = 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 aa7afb3b826d..afed48401b25 100644
--- a/sound/soc/codecs/adau7118-i2c.c
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -48,8 +48,7 @@ static const struct regmap_config adau7118_regmap_config = {
.volatile_reg = adau7118_volatile,
};
-static int adau7118_probe_i2c(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int adau7118_probe_i2c(struct i2c_client *i2c)
{
struct regmap *map;
@@ -79,7 +78,7 @@ static struct i2c_driver adau7118_driver = {
.name = "adau7118",
.of_match_table = adau7118_of_match,
},
- .probe = adau7118_probe_i2c,
+ .probe_new = 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 0f565b851ea5..bf181bbaabed 100644
--- a/sound/soc/codecs/adav803.c
+++ b/sound/soc/codecs/adav803.c
@@ -19,8 +19,7 @@ static const struct i2c_device_id adav803_id[] = {
};
MODULE_DEVICE_TABLE(i2c, adav803_id);
-static int adav803_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adav803_probe(struct i2c_client *client)
{
return adav80x_bus_probe(&client->dev,
devm_regmap_init_i2c(client, &adav80x_regmap_config));
@@ -30,7 +29,7 @@ static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
},
- .probe = adav803_probe,
+ .probe_new = 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 2e6bafd2a821..5c4a78c16733 100644
--- a/sound/soc/codecs/ak4118.c
+++ b/sound/soc/codecs/ak4118.c
@@ -356,8 +356,7 @@ static const struct regmap_config ak4118_regmap = {
.max_register = AK4118_REG_MAX - 1,
};
-static int ak4118_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4118_i2c_probe(struct i2c_client *i2c)
{
struct ak4118_priv *ak4118;
int ret;
@@ -416,7 +415,7 @@ static struct i2c_driver ak4118_i2c_driver = {
.of_match_table = of_match_ptr(ak4118_of_match),
},
.id_table = ak4118_id_table,
- .probe = ak4118_i2c_probe,
+ .probe_new = ak4118_i2c_probe,
};
module_i2c_driver(ak4118_i2c_driver);
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 91e7a57c43da..cc803e730c6e 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -405,8 +405,7 @@ static const struct snd_soc_component_driver soc_component_dev_ak4535 = {
.non_legacy_dai_naming = 1,
};
-static int ak4535_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4535_i2c_probe(struct i2c_client *i2c)
{
struct ak4535_priv *ak4535;
int ret;
@@ -441,7 +440,7 @@ static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
},
- .probe = ak4535_i2c_probe,
+ .probe_new = ak4535_i2c_probe,
.id_table = ak4535_i2c_id,
};
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 034195c83bd7..55e773f92122 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -10,11 +10,97 @@
// Based on ak4535.c by Richard Purdie
// Based on wm8753.c by Liam Girdwood
+/*
+ * +-------+
+ * |AK4613 |
+ * SDTO1 <-| |
+ * | |
+ * SDTI1 ->| |
+ * SDTI2 ->| |
+ * SDTI3 ->| |
+ * +-------+
+ *
+ * +---+
+ * clk | |___________________________________________...
+ *
+ * [TDM512]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4][L5][R5][L6][R6]
+ *
+ * [TDM256]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4]
+ * SDTI2 [L5][R5][L6][R6]
+ *
+ * [TDM128]
+ * SDTO1 [L1][R1][L2][R2]
+ * SDTI1 [L1][R1][L2][R2]
+ * SDTI2 [L3][R3][L4][R4]
+ * SDTI3 [L5][R5][L6][R6]
+ *
+ * [STEREO]
+ * Playback 2ch : SDTI1
+ * Capture 2ch : SDTO1
+ *
+ * [TDM512]
+ * Playback 12ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ * [TDM256]
+ * Playback 12ch : SDTI1 + SDTI2
+ * Playback 8ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ * [TDM128]
+ * Playback 12ch : SDTI1 + SDTI2 + SDTI3
+ * Playback 8ch : SDTI1 + SDTI2
+ * Playback 4ch : SDTI1
+ * Capture 4ch : SDTO1
+ *
+ *
+ * !!! NOTE !!!
+ *
+ * Renesas is the only user of ak4613 on upstream so far,
+ * but the chip connection is like below.
+ * Thus, Renesas can't test all connection case.
+ * Tested TDM is very limited.
+ *
+ * +-----+ +-----------+
+ * | SoC | | AK4613 |
+ * | |<-----|SDTO1 IN1|<-- Mic
+ * | | | IN2|
+ * | | | |
+ * | |----->|SDTI1 OUT1|--> Headphone
+ * +-----+ |SDTI2 OUT2|
+ * |SDTI3 OUT3|
+ * | OUT4|
+ * | OUT5|
+ * | OUT6|
+ * +-----------+
+ *
+ * Renesas SoC can handle [2, 6,8] channels.
+ * Ak4613 can handle [2,4, 8,12] channels.
+ *
+ * Because of above HW connection and available channels number,
+ * Renesas could test are ...
+ *
+ * [STEREO] Playback 2ch : SDTI1
+ * Capture 2ch : SDTO1
+ * [TDM256] Playback 8ch : SDTI1 (*)
+ *
+ * (*) it used 8ch data between SoC <-> AK4613 on TDM256 mode,
+ * but could confirm is only first 2ch because only 1
+ * Headphone is connected.
+ *
+ * see
+ * AK4613_ENABLE_TDM_TEST
+ */
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/of_device.h>
+#include <linux/of_graph.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -78,29 +164,74 @@
/* OCTRL */
#define OCTRL_MASK (0x3F)
-struct ak4613_formats {
- unsigned int width;
- unsigned int fmt;
-};
+/*
+ * configs
+ *
+ * 0x000000BA
+ *
+ * B : AK4613_CONFIG_SDTI_x
+ * A : AK4613_CONFIG_MODE_x
+ */
+#define AK4613_CONFIG_SET(priv, x) priv->configs |= AK4613_CONFIG_##x
+#define AK4613_CONFIG_GET(priv, x) (priv->configs & AK4613_CONFIG_##x##_MASK)
+
+/*
+ * AK4613_CONFIG_SDTI_x
+ *
+ * It indicates how many SDTIx is connected.
+ */
+#define AK4613_CONFIG_SDTI_MASK (0xF << 4)
+#define AK4613_CONFIG_SDTI(x) (((x) & 0xF) << 4)
+#define AK4613_CONFIG_SDTI_set(priv, x) AK4613_CONFIG_SET(priv, SDTI(x))
+#define AK4613_CONFIG_SDTI_get(priv) ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF)
+
+/*
+ * AK4613_CONFIG_MODE_x
+ *
+ * Same as Ctrl1 :: TDM1/TDM0
+ * No shift is requested
+ * see
+ * AK4613_CTRL1_TO_MODE()
+ * Table 11/12/13/14
+ */
+#define AK4613_CONFIG_MODE_MASK (0xF)
+#define AK4613_CONFIG_MODE_STEREO (0x0)
+#define AK4613_CONFIG_MODE_TDM512 (0x1)
+#define AK4613_CONFIG_MODE_TDM256 (0x2)
+#define AK4613_CONFIG_MODE_TDM128 (0x3)
+
+/*
+ * !!!! FIXME !!!!
+ *
+ * Because of testable HW limitation, TDM256 8ch TDM was only tested.
+ * This driver uses AK4613_ENABLE_TDM_TEST instead of new DT property so far.
+ * Don't hesitate to update driver, you don't need to care compatible
+ * with Renesas.
+ *
+ * #define AK4613_ENABLE_TDM_TEST
+ */
struct ak4613_interface {
- struct ak4613_formats capture;
- struct ak4613_formats playback;
+ unsigned int width;
+ unsigned int fmt;
+ u8 dif;
};
struct ak4613_priv {
struct mutex lock;
- const struct ak4613_interface *iface;
- struct snd_pcm_hw_constraint_list constraint;
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ struct snd_pcm_hw_constraint_list constraint_channels;
struct work_struct dummy_write_work;
struct snd_soc_component *component;
unsigned int rate;
unsigned int sysclk;
unsigned int fmt;
+ unsigned int configs;
+ int cnt;
+ u8 ctrl1;
u8 oc;
u8 ic;
- int cnt;
};
/*
@@ -137,14 +268,24 @@ static const struct reg_default ak4613_reg[] = {
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
};
-#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
-#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+/*
+ * CTRL1 register
+ * see
+ * Table 11/12/13/14
+ */
+#define AUDIO_IFACE(_dif, _width, _fmt) \
+ { \
+ .dif = _dif, \
+ .width = _width, \
+ .fmt = SND_SOC_DAIFMT_##_fmt,\
+ }
static const struct ak4613_interface ak4613_iface[] = {
- /* capture */ /* playback */
- /* [0] - [2] are not supported */
- [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) },
- [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) },
+ /* It doesn't support asymmetric format */
+
+ AUDIO_IFACE(0x03, 24, LEFT_J),
+ AUDIO_IFACE(0x04, 24, I2S),
};
+#define AK4613_CTRL1_TO_MODE(priv) ((priv)->ctrl1 >> 6) /* AK4613_CONFIG_MODE_x */
static const struct regmap_config ak4613_regmap_cfg = {
.reg_bits = 8,
@@ -250,13 +391,14 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
priv->cnt = 0;
}
if (!priv->cnt)
- priv->iface = NULL;
+ priv->ctrl1 = 0;
mutex_unlock(&priv->lock);
}
static void ak4613_hw_constraints(struct ak4613_priv *priv,
- struct snd_pcm_runtime *runtime)
+ struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
static const unsigned int ak4613_rates[] = {
32000,
44100,
@@ -267,10 +409,36 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
176400,
192000,
};
- struct snd_pcm_hw_constraint_list *constraint = &priv->constraint;
+#define AK4613_CHANNEL_2 0
+#define AK4613_CHANNEL_4 1
+#define AK4613_CHANNEL_8 2
+#define AK4613_CHANNEL_12 3
+#define AK4613_CHANNEL_NONE -1
+ static const unsigned int ak4613_channels[] = {
+ [AK4613_CHANNEL_2] = 2,
+ [AK4613_CHANNEL_4] = 4,
+ [AK4613_CHANNEL_8] = 8,
+ [AK4613_CHANNEL_12] = 12,
+ };
+#define MODE_MAX 4
+#define SDTx_MAX 4
+#define MASK(x) (1 << AK4613_CHANNEL_##x)
+ static const int mask_list[MODE_MAX][SDTx_MAX] = {
+ /* SDTO SDTIx1 SDTIx2 SDTIx3 */
+ [AK4613_CONFIG_MODE_STEREO] = { MASK(2), MASK(2), MASK(2), MASK(2)},
+ [AK4613_CONFIG_MODE_TDM512] = { MASK(4), MASK(12), MASK(12), MASK(12)},
+ [AK4613_CONFIG_MODE_TDM256] = { MASK(4), MASK(8), MASK(8)|MASK(12), MASK(8)|MASK(12)},
+ [AK4613_CONFIG_MODE_TDM128] = { MASK(4), MASK(4), MASK(4)|MASK(8), MASK(4)|MASK(8)|MASK(12)},
+ };
+ struct snd_pcm_hw_constraint_list *constraint;
+ unsigned int mask;
+ unsigned int mode;
unsigned int fs;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int sdti_num;
int i;
+ constraint = &priv->constraint_rates;
constraint->list = ak4613_rates;
constraint->mask = 0;
constraint->count = 0;
@@ -296,6 +464,41 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, constraint);
+
+
+ sdti_num = AK4613_CONFIG_SDTI_get(priv);
+ if (WARN_ON(sdti_num >= SDTx_MAX))
+ return;
+
+ if (priv->cnt) {
+ /*
+ * If it was already working,
+ * the constraint is same as working mode.
+ */
+ mode = AK4613_CTRL1_TO_MODE(priv);
+ mask = 0; /* no default */
+ } else {
+ /*
+ * It is not yet working,
+ * the constraint is based on board configs.
+ * STEREO mask is default
+ */
+ mode = AK4613_CONFIG_GET(priv, MODE);
+ mask = mask_list[AK4613_CONFIG_MODE_STEREO][is_play * sdti_num];
+ }
+
+ if (WARN_ON(mode >= MODE_MAX))
+ return;
+
+ /* add each mode mask */
+ mask |= mask_list[mode][is_play * sdti_num];
+
+ constraint = &priv->constraint_channels;
+ constraint->list = ak4613_channels;
+ constraint->mask = mask;
+ constraint->count = sizeof(ak4613_channels);
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
}
static int ak4613_dai_startup(struct snd_pcm_substream *substream,
@@ -304,9 +507,10 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ mutex_lock(&priv->lock);
+ ak4613_hw_constraints(priv, substream);
priv->cnt++;
-
- ak4613_hw_constraints(priv, substream->runtime);
+ mutex_unlock(&priv->lock);
return 0;
}
@@ -322,13 +526,13 @@ static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format)
{
struct snd_soc_component *component = dai->component;
struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int fmt;
- fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
-
+ fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
switch (fmt) {
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_I2S:
@@ -338,24 +542,20 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- return 0;
-}
-
-static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
- int is_play,
- unsigned int fmt, unsigned int width)
-{
- const struct ak4613_formats *fmts;
-
- fmts = (is_play) ? &iface->playback : &iface->capture;
-
- if (fmts->fmt != fmt)
- return false;
-
- if (fmts->width != width)
- return false;
+ fmt = format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ switch (fmt) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ /*
+ * SUPPORTME
+ *
+ * "clock provider" is not yet supperted
+ */
+ return -EINVAL;
+ }
- return true;
+ return 0;
}
static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
@@ -364,14 +564,12 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
- const struct ak4613_interface *iface;
struct device *dev = component->dev;
unsigned int width = params_width(params);
unsigned int fmt = priv->fmt;
unsigned int rate;
- int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int i, ret;
- u8 fmt_ctrl, ctrl2;
+ u8 ctrl2;
rate = params_rate(params);
switch (rate) {
@@ -397,40 +595,51 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
/*
* FIXME
*
- * It doesn't support TDM at this point
+ * It doesn't have full TDM suppert yet
*/
- fmt_ctrl = NO_FMT;
ret = -EINVAL;
- iface = NULL;
mutex_lock(&priv->lock);
- if (priv->iface) {
- if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
- iface = priv->iface;
+ if (priv->cnt > 1) {
+ /*
+ * If it was already working, use current priv->ctrl1
+ */
+ ret = 0;
} else {
+ /*
+ * It is not yet working,
+ */
+ unsigned int channel = params_channels(params);
+ u8 tdm;
+
+ /* STEREO or TDM */
+ if (channel == 2)
+ tdm = AK4613_CONFIG_MODE_STEREO;
+ else
+ tdm = AK4613_CONFIG_GET(priv, MODE);
+
for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) {
- if (!ak4613_dai_fmt_matching(ak4613_iface + i,
- is_play,
- fmt, width))
- continue;
- iface = ak4613_iface + i;
- break;
+ const struct ak4613_interface *iface = ak4613_iface + i;
+
+ if ((iface->fmt == fmt) && (iface->width == width)) {
+ /*
+ * Ctrl1
+ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
+ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE|
+ * < tdm > < iface->dif >
+ */
+ priv->ctrl1 = (tdm << 6) | (iface->dif << 3);
+ ret = 0;
+ break;
+ }
}
}
-
- if ((priv->iface == NULL) ||
- (priv->iface == iface)) {
- priv->iface = iface;
- ret = 0;
- }
mutex_unlock(&priv->lock);
if (ret < 0)
goto hw_params_end;
- fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
-
- snd_soc_component_update_bits(component, CTRL1, FMT_MASK, fmt_ctrl);
+ snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1);
snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2);
snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic);
@@ -574,14 +783,14 @@ static struct snd_soc_dai_driver ak4613_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 12,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 4,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
@@ -626,6 +835,7 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
{
struct device_node *np = dev->of_node;
char prop[32];
+ int sdti_num;
int i;
/* Input 1 - 2 */
@@ -641,10 +851,32 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
if (!of_get_property(np, prop, NULL))
priv->oc |= 1 << i;
}
+
+ /*
+ * enable TDM256 test
+ *
+ * !!! FIXME !!!
+ *
+ * It should be configured by DT or other way
+ * if it was full supported.
+ * But it is using ifdef style for now for test
+ * purpose.
+ */
+#if defined(AK4613_ENABLE_TDM_TEST)
+ AK4613_CONFIG_SET(priv, MODE_TDM256);
+#endif
+
+ /*
+ * connected STDI
+ */
+ sdti_num = of_graph_get_endpoint_count(np);
+ if (WARN_ON((sdti_num > 3) || (sdti_num < 1)))
+ return;
+
+ AK4613_CONFIG_SDTI_set(priv, sdti_num);
}
-static int ak4613_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4613_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct device_node *np = dev->of_node;
@@ -655,8 +887,11 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
regmap_cfg = NULL;
if (np)
regmap_cfg = of_device_get_match_data(dev);
- else
+ else {
+ const struct i2c_device_id *id =
+ i2c_match_id(ak4613_i2c_id, i2c);
regmap_cfg = (const struct regmap_config *)id->driver_data;
+ }
if (!regmap_cfg)
return -EINVAL;
@@ -667,7 +902,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
ak4613_parse_of(priv, dev);
- priv->iface = NULL;
+ priv->ctrl1 = 0;
priv->cnt = 0;
priv->sysclk = 0;
INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
@@ -694,7 +929,7 @@ static struct i2c_driver ak4613_i2c_driver = {
.name = "ak4613-codec",
.of_match_table = ak4613_of_match,
},
- .probe = ak4613_i2c_probe,
+ .probe_new = ak4613_i2c_probe,
.remove = ak4613_i2c_remove,
.id_table = ak4613_i2c_id,
};
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 04aef0e72aa5..d8d9cc712d67 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -548,8 +548,7 @@ static const struct regmap_config ak4641_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4641_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4641_i2c_probe(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
struct ak4641_priv *ak4641;
@@ -632,7 +631,7 @@ static struct i2c_driver ak4641_i2c_driver = {
.driver = {
.name = "ak4641",
},
- .probe = ak4641_i2c_probe,
+ .probe_new = 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 c284dcc5af76..3c20ff5595eb 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -630,8 +630,8 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
#endif
static const struct of_device_id ak4642_of_match[];
-static int ak4642_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id ak4642_i2c_id[];
+static int ak4642_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct device_node *np = dev->of_node;
@@ -651,6 +651,8 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (of_id)
drvdata = of_id->data;
} else {
+ const struct i2c_device_id *id =
+ i2c_match_id(ak4642_i2c_id, i2c);
drvdata = (const struct ak4642_drvdata *)id->driver_data;
}
@@ -697,7 +699,7 @@ static struct i2c_driver ak4642_i2c_driver = {
.name = "ak4642-codec",
.of_match_table = ak4642_of_match,
},
- .probe = ak4642_i2c_probe,
+ .probe_new = ak4642_i2c_probe,
.id_table = ak4642_i2c_id,
};
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index e9d1251c4265..60edcbe56014 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -629,8 +629,7 @@ static const struct regmap_config ak4671_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4671_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ak4671_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -657,7 +656,7 @@ static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671-codec",
},
- .probe = ak4671_i2c_probe,
+ .probe_new = ak4671_i2c_probe,
.id_table = ak4671_i2c_id,
};
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index b10357a6d655..8e6235d2c544 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -968,14 +968,21 @@ static const struct regmap_config alc5623_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct i2c_device_id alc5623_i2c_table[] = {
+ {"alc5621", 0x21},
+ {"alc5622", 0x22},
+ {"alc5623", 0x23},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
+
/*
* ALC5623 2 wire address is determined by A1 pin
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
-static int alc5623_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int alc5623_i2c_probe(struct i2c_client *client)
{
struct alc5623_platform_data *pdata;
struct alc5623_priv *alc5623;
@@ -983,6 +990,7 @@ static int alc5623_i2c_probe(struct i2c_client *client,
unsigned int vid1, vid2;
int ret;
u32 val32;
+ const struct i2c_device_id *id;
alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
GFP_KERNEL);
@@ -1009,6 +1017,8 @@ static int alc5623_i2c_probe(struct i2c_client *client,
}
vid2 >>= 8;
+ id = i2c_match_id(alc5623_i2c_table, client);
+
if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
dev_err(&client->dev, "unknown or wrong codec\n");
dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
@@ -1060,14 +1070,6 @@ static int alc5623_i2c_probe(struct i2c_client *client,
return ret;
}
-static const struct i2c_device_id alc5623_i2c_table[] = {
- {"alc5621", 0x21},
- {"alc5622", 0x22},
- {"alc5623", 0x23},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
-
#ifdef CONFIG_OF
static const struct of_device_id alc5623_of_match[] = {
{ .compatible = "realtek,alc5623", },
@@ -1082,7 +1084,7 @@ static struct i2c_driver alc5623_i2c_driver = {
.name = "alc562x-codec",
.of_match_table = of_match_ptr(alc5623_of_match),
},
- .probe = alc5623_i2c_probe,
+ .probe_new = alc5623_i2c_probe,
.id_table = alc5623_i2c_table,
};
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 6d7af3736a91..641bdfddae16 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1092,18 +1092,24 @@ static const struct regmap_config alc5632_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct i2c_device_id alc5632_i2c_table[] = {
+ {"alc5632", 0x5c},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
+
/*
* alc5632 2 wire address is determined by A1 pin
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
-static int alc5632_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int alc5632_i2c_probe(struct i2c_client *client)
{
struct alc5632_priv *alc5632;
int ret, ret1, ret2;
unsigned int vid1, vid2;
+ const struct i2c_device_id *id;
alc5632 = devm_kzalloc(&client->dev,
sizeof(struct alc5632_priv), GFP_KERNEL);
@@ -1129,6 +1135,8 @@ static int alc5632_i2c_probe(struct i2c_client *client,
vid2 >>= 8;
+ id = i2c_match_id(alc5632_i2c_table, client);
+
if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
dev_err(&client->dev,
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
@@ -1161,12 +1169,6 @@ static int alc5632_i2c_probe(struct i2c_client *client,
return ret;
}
-static const struct i2c_device_id alc5632_i2c_table[] = {
- {"alc5632", 0x5c},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
-
#ifdef CONFIG_OF
static const struct of_device_id alc5632_of_match[] = {
{ .compatible = "realtek,alc5632", },
@@ -1181,7 +1183,7 @@ static struct i2c_driver alc5632_i2c_driver = {
.name = "alc5632",
.of_match_table = of_match_ptr(alc5632_of_match),
},
- .probe = alc5632_i2c_probe,
+ .probe_new = alc5632_i2c_probe,
.id_table = alc5632_i2c_table,
};
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
index 9b92e1a0d1a3..8b0a9c788a26 100644
--- a/sound/soc/codecs/cros_ec_codec.c
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -232,11 +232,11 @@ static int i2s_rx_hw_params(struct snd_pcm_substream *substream,
if (params_rate(params) != 48000)
return -EINVAL;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_width(params)) {
+ case 16:
depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
+ case 24:
depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24;
break;
default:
@@ -387,6 +387,7 @@ static const struct snd_soc_component_driver i2s_rx_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets),
.dapm_routes = i2s_rx_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes),
+ .endianness = 1,
};
static void *wov_map_shm(struct cros_ec_codec_priv *priv,
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 933e3d627e5f..badfc55bc5fa 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -346,8 +346,7 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l32_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l32_private *cs35l32;
struct cs35l32_platform_data *pdata =
@@ -576,7 +575,7 @@ static struct i2c_driver cs35l32_i2c_driver = {
.of_match_table = cs35l32_of_match,
},
.id_table = cs35l32_id,
- .probe = cs35l32_i2c_probe,
+ .probe_new = cs35l32_i2c_probe,
.remove = cs35l32_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index 2a6f5e46d031..47dc0f6d90a2 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -1116,8 +1116,7 @@ static int cs35l33_of_get_pdata(struct device *dev,
return 0;
}
-static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l33_private *cs35l33;
struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1286,7 +1285,7 @@ static struct i2c_driver cs35l33_i2c_driver = {
},
.id_table = cs35l33_id,
- .probe = cs35l33_i2c_probe,
+ .probe_new = cs35l33_i2c_probe,
.remove = cs35l33_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index ed678241c22b..50d509a06071 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -994,8 +994,7 @@ static const char * const cs35l34_core_supplies[] = {
"VP",
};
-static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l34_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l34_private *cs35l34;
struct cs35l34_platform_data *pdata =
@@ -1217,7 +1216,7 @@ static struct i2c_driver cs35l34_i2c_driver = {
},
.id_table = cs35l34_id,
- .probe = cs35l34_i2c_probe,
+ .probe_new = cs35l34_i2c_probe,
.remove = cs35l34_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 961a3e07e70f..6b70afb70a67 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -1466,8 +1466,7 @@ static const struct reg_sequence cs35l35_errata_patch[] = {
{ 0x7F, 0x00 },
};
-static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l35_private *cs35l35;
struct device *dev = &i2c_client->dev;
@@ -1658,7 +1657,7 @@ static struct i2c_driver cs35l35_i2c_driver = {
.of_match_table = cs35l35_of_match,
},
.id_table = cs35l35_id,
- .probe = cs35l35_i2c_probe,
+ .probe_new = cs35l35_i2c_probe,
.remove = cs35l35_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index d83c1b318c1c..920190daa4d1 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -1700,8 +1700,7 @@ static const struct reg_sequence cs35l36_revb0_errata_patch[] = {
{ CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 },
};
-static int cs35l36_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs35l36_i2c_probe(struct i2c_client *i2c_client)
{
struct cs35l36_private *cs35l36;
struct device *dev = &i2c_client->dev;
@@ -1804,7 +1803,7 @@ static int cs35l36_i2c_probe(struct i2c_client *i2c_client,
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n",
ret);
- return ret;
+ goto err;
}
if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37)
@@ -1947,7 +1946,7 @@ static struct i2c_driver cs35l36_i2c_driver = {
.of_match_table = cs35l36_of_match,
},
.id_table = cs35l36_id,
- .probe = cs35l36_i2c_probe,
+ .probe_new = 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 faad5c638cb8..37c703c08fd5 100644
--- a/sound/soc/codecs/cs35l41-i2c.c
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -29,12 +29,11 @@ static const struct i2c_device_id cs35l41_id_i2c[] = {
MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
-static int cs35l41_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs35l41_i2c_probe(struct i2c_client *client)
{
struct cs35l41_private *cs35l41;
struct device *dev = &client->dev;
- struct cs35l41_platform_data *pdata = dev_get_platdata(dev);
+ struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(dev);
const struct regmap_config *regmap_config = &cs35l41_regmap_i2c;
int ret;
@@ -54,7 +53,7 @@ static int cs35l41_i2c_probe(struct i2c_client *client,
return ret;
}
- return cs35l41_probe(cs35l41, pdata);
+ return cs35l41_probe(cs35l41, hw_cfg);
}
static int cs35l41_i2c_remove(struct i2c_client *client)
@@ -91,7 +90,7 @@ static struct i2c_driver cs35l41_i2c_driver = {
.acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
},
.id_table = cs35l41_id_i2c,
- .probe = cs35l41_i2c_probe,
+ .probe_new = cs35l41_i2c_probe,
.remove = cs35l41_i2c_remove,
};
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index aa6823fbd1a4..6d3070ea9e06 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -12,6 +12,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/firmware/cirrus/wmfw.h>
#include <sound/cs35l41.h>
@@ -422,7 +423,7 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
}
}
-static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = {
+static const struct cs35l41_otp_packed_element_t otp_map_1[] = {
/* addr shift size */
{ 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
{ 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
@@ -525,7 +526,7 @@ static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM]
{ 0x00017044, 0, 24 }, /*LOT_NUMBER*/
};
-static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = {
+static const struct cs35l41_otp_packed_element_t otp_map_2[] = {
/* addr shift size */
{ 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/
{ 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/
@@ -667,39 +668,58 @@ static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
{ CS35L41_AMP_GAIN_CTRL, 0x00000000 },
};
+static const struct reg_sequence cs35l41_fs_errata_patch[] = {
+ { CS35L41_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L41_DSP1_TX8_RATE, 0x00000001 },
+};
+
static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = {
{
.id = 0x01,
.map = otp_map_1,
- .num_elements = CS35L41_NUM_OTP_ELEM,
+ .num_elements = ARRAY_SIZE(otp_map_1),
.bit_offset = 16,
.word_offset = 2,
},
{
.id = 0x02,
.map = otp_map_2,
- .num_elements = CS35L41_NUM_OTP_ELEM,
+ .num_elements = ARRAY_SIZE(otp_map_2),
.bit_offset = 16,
.word_offset = 2,
},
{
.id = 0x03,
.map = otp_map_2,
- .num_elements = CS35L41_NUM_OTP_ELEM,
+ .num_elements = ARRAY_SIZE(otp_map_2),
.bit_offset = 16,
.word_offset = 2,
},
{
.id = 0x06,
.map = otp_map_2,
- .num_elements = CS35L41_NUM_OTP_ELEM,
+ .num_elements = ARRAY_SIZE(otp_map_2),
.bit_offset = 16,
.word_offset = 2,
},
{
.id = 0x08,
.map = otp_map_1,
- .num_elements = CS35L41_NUM_OTP_ELEM,
+ .num_elements = ARRAY_SIZE(otp_map_1),
.bit_offset = 16,
.word_offset = 2,
},
@@ -822,7 +842,7 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
word_offset = otp_map_match->word_offset;
for (i = 0; i < otp_map_match->num_elements; i++) {
- dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d otp_map[i].size = %d\n",
+ dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d, otp_map[i].size = %u\n",
bit_offset, word_offset, bit_sum % 32, otp_map[i].size);
if (bit_offset + otp_map[i].size - 1 >= 32) {
otp_val = (otp_mem[word_offset] &
@@ -956,9 +976,8 @@ static const unsigned char cs35l41_bst_slope_table[4] = {
0x75, 0x6B, 0x3B, 0x28
};
-
-int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, int boost_cap,
- int boost_ipk)
+static int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind,
+ int boost_cap, int boost_ipk)
{
unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled;
int ret;
@@ -994,10 +1013,20 @@ int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_in
case 101 ... 200:
bst_cbst_range = 3;
break;
- default: /* 201 uF and greater */
+ default:
+ if (boost_cap < 0) {
+ dev_err(dev, "Invalid boost capacitor value: %d nH\n", boost_cap);
+ return -EINVAL;
+ }
+ /* 201 uF and greater */
bst_cbst_range = 4;
}
+ if (boost_ipk < 1600 || boost_ipk > 4500) {
+ dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk);
+ return -EINVAL;
+ }
+
ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF,
CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK,
cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range]
@@ -1019,10 +1048,6 @@ int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_in
return ret;
}
- if (boost_ipk < 1600 || boost_ipk > 4500) {
- dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk);
- return -EINVAL;
- }
bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10;
ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, CS35L41_BST_IPK_MASK,
@@ -1032,9 +1057,269 @@ int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_in
return ret;
}
+ regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+
return 0;
}
-EXPORT_SYMBOL_GPL(cs35l41_boost_config);
+
+static const struct reg_sequence cs35l41_safe_to_reset[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000393C, 0x000000C0, 6000},
+ { 0x0000393C, 0x00000000 },
+ { 0x00007414, 0x00C82222 },
+ { 0x0000742C, 0x00000000 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000000 },
+ { 0x0000742C, 0x00000009, 3000 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_safe_to_active[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x0000742C, 0x0000000F },
+ { 0x0000742C, 0x00000079 },
+ { 0x00007438, 0x00585941 },
+ { CS35L41_PWR_CTRL1, 0x00000001, 3000 }, // GLOBAL_EN = 1
+ { 0x0000742C, 0x000000F9 },
+ { 0x00007438, 0x00580941 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_reset_to_safe[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00007438, 0x00585941 },
+ { 0x00007414, 0x08C82222 },
+ { 0x0000742C, 0x00000009 },
+ { 0x00000040, 0x000000CC },
+ { 0x00000040, 0x00000033 },
+};
+
+int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
+ struct cs35l41_hw_cfg *hw_cfg)
+{
+ int ret;
+
+ switch (hw_cfg->bst_type) {
+ case CS35L41_INT_BOOST:
+ ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind,
+ hw_cfg->bst_cap, hw_cfg->bst_ipk);
+ if (ret)
+ dev_err(dev, "Error in Boost DT config: %d\n", ret);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ /* Only CLSA0100 doesn't use GPIO as VSPK switch, but even on that laptop we can
+ * toggle GPIO1 as is not connected to anything.
+ * There will be no other device without VSPK switch.
+ */
+ regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_multi_reg_write(regmap, cs35l41_reset_to_safe,
+ ARRAY_SIZE(cs35l41_reset_to_safe));
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT);
+ break;
+ default:
+ dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_init_boost);
+
+bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
+{
+ switch (b_type) {
+ /* There is only one laptop that doesn't have VSPK switch. */
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ return false;
+ case CS35L41_EXT_BOOST:
+ regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
+ regmap_multi_reg_write(regmap, cs35l41_safe_to_reset,
+ ARRAY_SIZE(cs35l41_safe_to_reset));
+ return true;
+ default:
+ return true;
+ }
+}
+EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
+
+int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable)
+{
+ int ret;
+
+ switch (b_type) {
+ case CS35L41_INT_BOOST:
+ ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
+ enable << CS35L41_GLOBAL_EN_SHIFT);
+ usleep_range(3000, 3100);
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
+ if (enable)
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active,
+ ARRAY_SIZE(cs35l41_safe_to_active));
+ else
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe,
+ ARRAY_SIZE(cs35l41_active_to_safe));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_global_enable);
+
+int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg)
+{
+ struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1;
+ struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2;
+ int irq_pol = IRQF_TRIGGER_NONE;
+
+ regmap_update_bits(regmap, CS35L41_GPIO1_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ gpio1->pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !gpio1->out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ regmap_update_bits(regmap, CS35L41_GPIO2_CTRL1,
+ CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
+ gpio2->pol_inv << CS35L41_GPIO_POL_SHIFT |
+ !gpio2->out_en << CS35L41_GPIO_DIR_SHIFT);
+
+ if (gpio1->valid)
+ regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO1_CTRL_MASK,
+ gpio1->func << CS35L41_GPIO1_CTRL_SHIFT);
+
+ if (gpio2->valid) {
+ regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO2_CTRL_MASK,
+ gpio2->func << CS35L41_GPIO2_CTRL_SHIFT);
+
+ switch (gpio2->func) {
+ case CS35L41_GPIO2_INT_PUSH_PULL_LOW:
+ case CS35L41_GPIO2_INT_OPEN_DRAIN:
+ irq_pol = IRQF_TRIGGER_LOW;
+ break;
+ case CS35L41_GPIO2_INT_PUSH_PULL_HIGH:
+ irq_pol = IRQF_TRIGGER_HIGH;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return irq_pol;
+}
+EXPORT_SYMBOL_GPL(cs35l41_gpio_config);
+
+static const struct cs_dsp_region cs35l41_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0},
+};
+
+void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_dsp *dsp)
+{
+ dsp->num = 1;
+ dsp->type = WMFW_HALO;
+ dsp->rev = 0;
+ dsp->dev = dev;
+ dsp->regmap = reg;
+ dsp->base = CS35L41_DSP1_CTRL_BASE;
+ dsp->base_sysinfo = CS35L41_DSP1_SYS_ID;
+ dsp->mem = cs35l41_dsp1_regions;
+ dsp->num_mems = ARRAY_SIZE(cs35l41_dsp1_regions);
+ dsp->lock_regions = 0xFFFFFFFF;
+}
+EXPORT_SYMBOL_GPL(cs35l41_configure_cs_dsp);
+
+static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
+ enum cs35l41_cspl_mbox_status sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ default:
+ return false;
+ }
+}
+
+int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap,
+ enum cs35l41_cspl_mbox_cmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ // Set mailbox cmd
+ ret = regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(regmap, CS35L41_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (!cs35l41_check_cspl_mbox_sts(cmd, sts))
+ dev_dbg(dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+ else
+ return 0;
+ }
+
+ dev_err(dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+ return -ENOMSG;
+}
+EXPORT_SYMBOL_GPL(cs35l41_set_cspl_mbox_cmd);
+
+int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(regmap, cs35l41_fs_errata_patch,
+ ARRAY_SIZE(cs35l41_fs_errata_patch));
+ if (ret < 0)
+ dev_err(dev, "Failed to write fs errata: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_write_fs_errata);
MODULE_DESCRIPTION("CS35L41 library");
MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
index 169221a5b09f..9e19c946a66b 100644
--- a/sound/soc/codecs/cs35l41-spi.c
+++ b/sound/soc/codecs/cs35l41-spi.c
@@ -30,7 +30,7 @@ MODULE_DEVICE_TABLE(spi, cs35l41_id_spi);
static int cs35l41_spi_probe(struct spi_device *spi)
{
const struct regmap_config *regmap_config = &cs35l41_regmap_spi;
- struct cs35l41_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(&spi->dev);
struct cs35l41_private *cs35l41;
int ret;
@@ -52,7 +52,7 @@ static int cs35l41_spi_probe(struct spi_device *spi)
cs35l41->dev = &spi->dev;
cs35l41->irq = spi->irq;
- return cs35l41_probe(cs35l41, pdata);
+ return cs35l41_probe(cs35l41, hw_cfg);
}
static void cs35l41_spi_remove(struct spi_device *spi)
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index 6b784a62df0c..3e68a07a3c8e 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -208,67 +208,6 @@ static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w,
}
}
-static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
- enum cs35l41_cspl_mbox_status sts)
-{
- switch (cmd) {
- case CSPL_MBOX_CMD_NONE:
- case CSPL_MBOX_CMD_UNKNOWN_CMD:
- return true;
- case CSPL_MBOX_CMD_PAUSE:
- case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
- return (sts == CSPL_MBOX_STS_PAUSED);
- case CSPL_MBOX_CMD_RESUME:
- return (sts == CSPL_MBOX_STS_RUNNING);
- case CSPL_MBOX_CMD_REINIT:
- return (sts == CSPL_MBOX_STS_RUNNING);
- case CSPL_MBOX_CMD_STOP_PRE_REINIT:
- return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
- default:
- return false;
- }
-}
-
-static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41,
- enum cs35l41_cspl_mbox_cmd cmd)
-{
- unsigned int sts = 0, i;
- int ret;
-
- // Set mailbox cmd
- ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd);
- if (ret < 0) {
- if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
- dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret);
- return ret;
- }
-
- // Read mailbox status and verify it is appropriate for the given cmd
- for (i = 0; i < 5; i++) {
- usleep_range(1000, 1100);
-
- ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &sts);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Failed to read MBOX STS: %d\n", ret);
- continue;
- }
-
- if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) {
- dev_dbg(cs35l41->dev,
- "[%u] cmd %u returned invalid sts %u",
- i, cmd, sts);
- } else {
- return 0;
- }
- }
-
- dev_err(cs35l41->dev,
- "Failed to set mailbox cmd %u (status %u)\n",
- cmd, sts);
-
- return -ENOMSG;
-}
-
static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -299,9 +238,11 @@ static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w,
return -EINVAL;
}
- return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_RESUME);
+ return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_RESUME);
case SND_SOC_DAPM_PRE_PMD:
- return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_PAUSE);
+ return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
+ CSPL_MBOX_CMD_PAUSE);
default:
return 0;
}
@@ -578,15 +519,10 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
cs35l41_pup_patch,
ARRAY_SIZE(cs35l41_pup_patch));
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1,
- CS35L41_GLOBAL_EN_MASK,
- 1 << CS35L41_GLOBAL_EN_SHIFT);
-
- usleep_range(1000, 1100);
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1,
- CS35L41_GLOBAL_EN_MASK, 0);
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
val, val & CS35L41_PDN_DONE_MASK,
@@ -744,14 +680,6 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = {
{"CLASS H", NULL, "PCM Source"},
};
-static const struct cs_dsp_region cs35l41_dsp1_regions[] = {
- { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 },
- { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 },
- { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 },
- {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0},
- {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0},
-};
-
static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n,
unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot)
{
@@ -995,69 +923,53 @@ static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai,
static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
int ret;
- /* Set Platform Data */
- /* Required */
- if (cs35l41->pdata.bst_ipk &&
- cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) {
- ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap, cs35l41->pdata.bst_ind,
- cs35l41->pdata.bst_cap, cs35l41->pdata.bst_ipk);
- if (ret) {
- dev_err(cs35l41->dev, "Error in Boost DT config: %d\n", ret);
- return ret;
- }
- } else {
- dev_err(cs35l41->dev, "Incomplete Boost component DT config\n");
+ if (!hw_cfg->valid)
+ return -EINVAL;
+
+ if (hw_cfg->bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
return -EINVAL;
- }
+
+ /* Required */
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
/* Optional */
- if (cs35l41->pdata.dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK &&
- cs35l41->pdata.dout_hiz >= 0)
- regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL,
- CS35L41_ASP_DOUT_HIZ_MASK,
- cs35l41->pdata.dout_hiz);
+ if (hw_cfg->dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK && hw_cfg->dout_hiz >= 0)
+ regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL, CS35L41_ASP_DOUT_HIZ_MASK,
+ hw_cfg->dout_hiz);
return 0;
}
-static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41)
-{
- struct cs35l41_irq_cfg *irq_gpio_cfg1 = &cs35l41->pdata.irq_config1;
- struct cs35l41_irq_cfg *irq_gpio_cfg2 = &cs35l41->pdata.irq_config2;
- int irq_pol = IRQF_TRIGGER_NONE;
-
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO1_CTRL1,
- CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
- irq_gpio_cfg1->irq_pol_inv << CS35L41_GPIO_POL_SHIFT |
- !irq_gpio_cfg1->irq_out_en << CS35L41_GPIO_DIR_SHIFT);
-
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO2_CTRL1,
- CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK,
- irq_gpio_cfg2->irq_pol_inv << CS35L41_GPIO_POL_SHIFT |
- !irq_gpio_cfg2->irq_out_en << CS35L41_GPIO_DIR_SHIFT);
-
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO1_CTRL_MASK | CS35L41_GPIO2_CTRL_MASK,
- irq_gpio_cfg1->irq_src_sel << CS35L41_GPIO1_CTRL_SHIFT |
- irq_gpio_cfg2->irq_src_sel << CS35L41_GPIO2_CTRL_SHIFT);
-
- if ((irq_gpio_cfg2->irq_src_sel ==
- (CS35L41_GPIO_CTRL_ACTV_LO | CS35L41_VALID_PDATA)) ||
- (irq_gpio_cfg2->irq_src_sel ==
- (CS35L41_GPIO_CTRL_OPEN_INT | CS35L41_VALID_PDATA)))
- irq_pol = IRQF_TRIGGER_LOW;
- else if (irq_gpio_cfg2->irq_src_sel ==
- (CS35L41_GPIO_CTRL_ACTV_HI | CS35L41_VALID_PDATA))
- irq_pol = IRQF_TRIGGER_HIGH;
-
- return irq_pol;
-}
+static const struct snd_soc_dapm_route cs35l41_ext_bst_routes[] = {
+ {"Main AMP", NULL, "VSPK"},
+};
+
+static const struct snd_soc_dapm_widget cs35l41_ext_bst_widget[] = {
+ SND_SOC_DAPM_SUPPLY("VSPK", CS35L41_GPIO1_CTRL1, CS35L41_GPIO_LVL_SHIFT, 0, NULL, 0),
+};
static int cs35l41_component_probe(struct snd_soc_component *component)
{
struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) {
+ ret = snd_soc_dapm_new_controls(dapm, cs35l41_ext_bst_widget,
+ ARRAY_SIZE(cs35l41_ext_bst_widget));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, cs35l41_ext_bst_routes,
+ ARRAY_SIZE(cs35l41_ext_bst_routes));
+ if (ret)
+ return ret;
+ }
return wm_adsp2_component_probe(&cs35l41->dsp, component);
}
@@ -1113,75 +1025,68 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l41 = {
.controls = cs35l41_aud_controls,
.num_controls = ARRAY_SIZE(cs35l41_aud_controls),
.set_sysclk = cs35l41_component_set_sysclk,
+
+ .endianness = 1,
};
-static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_platform_data *pdata)
+static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg)
{
- struct cs35l41_irq_cfg *irq_gpio1_config = &pdata->irq_config1;
- struct cs35l41_irq_cfg *irq_gpio2_config = &pdata->irq_config2;
+ struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1;
+ struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2;
unsigned int val;
int ret;
+ ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
+ if (ret >= 0)
+ hw_cfg->bst_type = val;
+
ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
if (ret >= 0)
- pdata->bst_ipk = val;
+ hw_cfg->bst_ipk = val;
+ else
+ hw_cfg->bst_ipk = -1;
ret = device_property_read_u32(dev, "cirrus,boost-ind-nanohenry", &val);
if (ret >= 0)
- pdata->bst_ind = val;
+ hw_cfg->bst_ind = val;
+ else
+ hw_cfg->bst_ind = -1;
ret = device_property_read_u32(dev, "cirrus,boost-cap-microfarad", &val);
if (ret >= 0)
- pdata->bst_cap = val;
+ hw_cfg->bst_cap = val;
+ else
+ hw_cfg->bst_cap = -1;
ret = device_property_read_u32(dev, "cirrus,asp-sdout-hiz", &val);
if (ret >= 0)
- pdata->dout_hiz = val;
+ hw_cfg->dout_hiz = val;
else
- pdata->dout_hiz = -1;
+ hw_cfg->dout_hiz = -1;
/* GPIO1 Pin Config */
- irq_gpio1_config->irq_pol_inv = device_property_read_bool(dev,
- "cirrus,gpio1-polarity-invert");
- irq_gpio1_config->irq_out_en = device_property_read_bool(dev,
- "cirrus,gpio1-output-enable");
- ret = device_property_read_u32(dev, "cirrus,gpio1-src-select",
- &val);
- if (ret >= 0)
- irq_gpio1_config->irq_src_sel = val | CS35L41_VALID_PDATA;
+ gpio1->pol_inv = device_property_read_bool(dev, "cirrus,gpio1-polarity-invert");
+ gpio1->out_en = device_property_read_bool(dev, "cirrus,gpio1-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio1-src-select", &val);
+ if (ret >= 0) {
+ gpio1->func = val;
+ gpio1->valid = true;
+ }
/* GPIO2 Pin Config */
- irq_gpio2_config->irq_pol_inv = device_property_read_bool(dev,
- "cirrus,gpio2-polarity-invert");
- irq_gpio2_config->irq_out_en = device_property_read_bool(dev,
- "cirrus,gpio2-output-enable");
- ret = device_property_read_u32(dev, "cirrus,gpio2-src-select",
- &val);
- if (ret >= 0)
- irq_gpio2_config->irq_src_sel = val | CS35L41_VALID_PDATA;
+ gpio2->pol_inv = device_property_read_bool(dev, "cirrus,gpio2-polarity-invert");
+ gpio2->out_en = device_property_read_bool(dev, "cirrus,gpio2-output-enable");
+ ret = device_property_read_u32(dev, "cirrus,gpio2-src-select", &val);
+ if (ret >= 0) {
+ gpio2->func = val;
+ gpio2->valid = true;
+ }
+
+ hw_cfg->valid = true;
return 0;
}
-static const struct reg_sequence cs35l41_fs_errata_patch[] = {
- { CS35L41_DSP1_RX1_RATE, 0x00000001 },
- { CS35L41_DSP1_RX2_RATE, 0x00000001 },
- { CS35L41_DSP1_RX3_RATE, 0x00000001 },
- { CS35L41_DSP1_RX4_RATE, 0x00000001 },
- { CS35L41_DSP1_RX5_RATE, 0x00000001 },
- { CS35L41_DSP1_RX6_RATE, 0x00000001 },
- { CS35L41_DSP1_RX7_RATE, 0x00000001 },
- { CS35L41_DSP1_RX8_RATE, 0x00000001 },
- { CS35L41_DSP1_TX1_RATE, 0x00000001 },
- { CS35L41_DSP1_TX2_RATE, 0x00000001 },
- { CS35L41_DSP1_TX3_RATE, 0x00000001 },
- { CS35L41_DSP1_TX4_RATE, 0x00000001 },
- { CS35L41_DSP1_TX5_RATE, 0x00000001 },
- { CS35L41_DSP1_TX6_RATE, 0x00000001 },
- { CS35L41_DSP1_TX7_RATE, 0x00000001 },
- { CS35L41_DSP1_TX8_RATE, 0x00000001 },
-};
-
static int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
{
struct wm_adsp *dsp;
@@ -1189,25 +1094,14 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
dsp = &cs35l41->dsp;
dsp->part = "cs35l41";
- dsp->cs_dsp.num = 1;
- dsp->cs_dsp.type = WMFW_HALO;
- dsp->cs_dsp.rev = 0;
dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
dsp->toggle_preload = true;
- dsp->cs_dsp.dev = cs35l41->dev;
- dsp->cs_dsp.regmap = cs35l41->regmap;
- dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE;
- dsp->cs_dsp.base_sysinfo = CS35L41_DSP1_SYS_ID;
- dsp->cs_dsp.mem = cs35l41_dsp1_regions;
- dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l41_dsp1_regions);
- dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
-
- ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_fs_errata_patch,
- ARRAY_SIZE(cs35l41_fs_errata_patch));
- if (ret < 0) {
- dev_err(cs35l41->dev, "Failed to write fs errata: %d\n", ret);
+
+ cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, &dsp->cs_dsp);
+
+ ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap);
+ if (ret < 0)
return ret;
- }
ret = wm_halo_init(dsp);
if (ret) {
@@ -1248,17 +1142,16 @@ err_dsp:
return ret;
}
-int cs35l41_probe(struct cs35l41_private *cs35l41,
- struct cs35l41_platform_data *pdata)
+int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg)
{
u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match;
int irq_pol = 0;
int ret;
- if (pdata) {
- cs35l41->pdata = *pdata;
+ if (hw_cfg) {
+ cs35l41->hw_cfg = *hw_cfg;
} else {
- ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->pdata);
+ ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->hw_cfg);
if (ret != 0)
return ret;
}
@@ -1357,7 +1250,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
- irq_pol = cs35l41_irq_gpio_config(cs35l41);
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, &cs35l41->hw_cfg);
/* Set interrupt masks for critical errors */
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
@@ -1409,6 +1302,7 @@ err_pm:
wm_adsp2_remove(&cs35l41->dsp);
err:
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
@@ -1423,6 +1317,7 @@ void cs35l41_remove(struct cs35l41_private *cs35l41)
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
wm_adsp2_remove(&cs35l41->dsp);
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
pm_runtime_put_noidle(cs35l41->dev);
@@ -1442,6 +1337,7 @@ static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
dev_dbg(cs35l41->dev, "Enter hibernate\n");
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
@@ -1484,7 +1380,7 @@ static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41)
dev_dbg(cs35l41->dev, "Exit hibernate\n");
for (j = 0; j < wake_retries; j++) {
- ret = cs35l41_set_cspl_mbox_cmd(cs35l41,
+ ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
if (!ret)
break;
@@ -1538,6 +1434,7 @@ static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
return ret;
}
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
return 0;
}
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
index 88a3d6e3434f..c85cbc1dd333 100644
--- a/sound/soc/codecs/cs35l41.h
+++ b/sound/soc/codecs/cs35l41.h
@@ -23,28 +23,10 @@
extern const struct dev_pm_ops cs35l41_pm_ops;
-enum cs35l41_cspl_mbox_status {
- CSPL_MBOX_STS_RUNNING = 0,
- CSPL_MBOX_STS_PAUSED = 1,
- CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
-};
-
-enum cs35l41_cspl_mbox_cmd {
- CSPL_MBOX_CMD_NONE = 0,
- CSPL_MBOX_CMD_PAUSE = 1,
- CSPL_MBOX_CMD_RESUME = 2,
- CSPL_MBOX_CMD_REINIT = 3,
- CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
- CSPL_MBOX_CMD_HIBERNATE = 5,
- CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
- CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
- CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
-};
-
struct cs35l41_private {
struct wm_adsp dsp; /* needs to be first member */
struct snd_soc_codec *codec;
- struct cs35l41_platform_data pdata;
+ struct cs35l41_hw_cfg hw_cfg;
struct device *dev;
struct regmap *regmap;
struct regulator_bulk_data supplies[CS35L41_NUM_SUPPLIES];
@@ -53,8 +35,7 @@ struct cs35l41_private {
struct gpio_desc *reset_gpio;
};
-int cs35l41_probe(struct cs35l41_private *cs35l41,
- struct cs35l41_platform_data *pdata);
+int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);
void cs35l41_remove(struct cs35l41_private *cs35l41);
#endif /*__CS35L41_H__*/
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
new file mode 100644
index 000000000000..38a4dbc9e9fe
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-i2c.c -- CS35L45 I2C driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l45_private *cs35l45;
+ struct device *dev = &client->dev;
+ int ret;
+
+ cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+ if (!cs35l45)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs35l45);
+ cs35l45->regmap = devm_regmap_init_i2c(client, &cs35l45_i2c_regmap);
+ if (IS_ERR(cs35l45->regmap)) {
+ ret = PTR_ERR(cs35l45->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l45->dev = dev;
+
+ return cs35l45_probe(cs35l45);
+}
+
+static int cs35l45_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l45_private *cs35l45 = i2c_get_clientdata(client);
+
+ return cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+ { .compatible = "cirrus,cs35l45" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct i2c_device_id cs35l45_id_i2c[] = {
+ { "cs35l45", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c);
+
+static struct i2c_driver cs35l45_i2c_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = &cs35l45_pm_ops,
+ },
+ .id_table = cs35l45_id_i2c,
+ .probe_new = cs35l45_i2c_probe,
+ .remove = cs35l45_i2c_remove,
+};
+module_i2c_driver(cs35l45_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45);
+MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
new file mode 100644
index 000000000000..baaf6e0f4fb9
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-spi.c -- CS35L45 SPI driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_spi_probe(struct spi_device *spi)
+{
+ struct cs35l45_private *cs35l45;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL);
+ if (cs35l45 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs35l45);
+ cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
+ if (IS_ERR(cs35l45->regmap)) {
+ ret = PTR_ERR(cs35l45->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ cs35l45->dev = dev;
+
+ return cs35l45_probe(cs35l45);
+}
+
+static void cs35l45_spi_remove(struct spi_device *spi)
+{
+ struct cs35l45_private *cs35l45 = spi_get_drvdata(spi);
+
+ cs35l45_remove(cs35l45);
+}
+
+static const struct of_device_id cs35l45_of_match[] = {
+ { .compatible = "cirrus,cs35l45" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l45_of_match);
+
+static const struct spi_device_id cs35l45_id_spi[] = {
+ { "cs35l45", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l45_id_spi);
+
+static struct spi_driver cs35l45_spi_driver = {
+ .driver = {
+ .name = "cs35l45",
+ .of_match_table = cs35l45_of_match,
+ .pm = &cs35l45_pm_ops,
+ },
+ .id_table = cs35l45_id_spi,
+ .probe = cs35l45_spi_probe,
+ .remove = cs35l45_spi_remove,
+};
+module_spi_driver(cs35l45_spi_driver);
+
+MODULE_DESCRIPTION("SPI CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45);
+MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
new file mode 100644
index 000000000000..5a2c2e684ef9
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l45.h"
+
+static const struct reg_sequence cs35l45_patch[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00000044, 0x00000055 },
+ { 0x00000044, 0x000000AA },
+ { 0x00006480, 0x0830500A },
+ { 0x00007C60, 0x1000850B },
+ { CS35L45_BOOST_OV_CFG, 0x007000D0 },
+ { CS35L45_LDPM_CONFIG, 0x0001B636 },
+ { 0x00002C08, 0x00000009 },
+ { 0x00006850, 0x0A30FFC4 },
+ { 0x00003820, 0x00040100 },
+ { 0x00003824, 0x00000000 },
+ { 0x00007CFC, 0x62870004 },
+ { 0x00007C60, 0x1001850B },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000000 },
+ { CS35L45_BOOST_CCM_CFG, 0xF0000003 },
+ { CS35L45_BOOST_DCM_CFG, 0x08710220 },
+ { CS35L45_ERROR_RELEASE, 0x00200000 },
+};
+
+int cs35l45_apply_patch(struct cs35l45_private *cs35l45)
+{
+ return regmap_register_patch(cs35l45->regmap, cs35l45_patch,
+ ARRAY_SIZE(cs35l45_patch));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES);
+
+static const struct reg_default cs35l45_defaults[] = {
+ { CS35L45_BLOCK_ENABLES, 0x00003323 },
+ { CS35L45_BLOCK_ENABLES2, 0x00000010 },
+ { CS35L45_REFCLK_INPUT, 0x00000510 },
+ { CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
+ { CS35L45_ASP_ENABLES1, 0x00000000 },
+ { CS35L45_ASP_CONTROL1, 0x00000028 },
+ { CS35L45_ASP_CONTROL2, 0x18180200 },
+ { CS35L45_ASP_CONTROL3, 0x00000002 },
+ { CS35L45_ASP_FRAME_CONTROL1, 0x03020100 },
+ { CS35L45_ASP_FRAME_CONTROL2, 0x00000004 },
+ { CS35L45_ASP_FRAME_CONTROL5, 0x00000100 },
+ { CS35L45_ASP_DATA_CONTROL1, 0x00000018 },
+ { CS35L45_ASP_DATA_CONTROL5, 0x00000018 },
+ { CS35L45_DACPCM1_INPUT, 0x00000008 },
+ { CS35L45_ASPTX1_INPUT, 0x00000018 },
+ { CS35L45_ASPTX2_INPUT, 0x00000019 },
+ { CS35L45_ASPTX3_INPUT, 0x00000020 },
+ { CS35L45_ASPTX4_INPUT, 0x00000028 },
+ { CS35L45_ASPTX5_INPUT, 0x00000048 },
+ { CS35L45_AMP_PCM_CONTROL, 0x00100000 },
+};
+
+static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L45_DEVID ... CS35L45_OTPID:
+ case CS35L45_SFT_RESET:
+ case CS35L45_GLOBAL_ENABLES:
+ case CS35L45_BLOCK_ENABLES:
+ case CS35L45_BLOCK_ENABLES2:
+ case CS35L45_ERROR_RELEASE:
+ case CS35L45_REFCLK_INPUT:
+ case CS35L45_GLOBAL_SAMPLE_RATE:
+ case CS35L45_ASP_ENABLES1:
+ case CS35L45_ASP_CONTROL1:
+ case CS35L45_ASP_CONTROL2:
+ case CS35L45_ASP_CONTROL3:
+ case CS35L45_ASP_FRAME_CONTROL1:
+ case CS35L45_ASP_FRAME_CONTROL2:
+ case CS35L45_ASP_FRAME_CONTROL5:
+ case CS35L45_ASP_DATA_CONTROL1:
+ case CS35L45_ASP_DATA_CONTROL5:
+ case CS35L45_DACPCM1_INPUT:
+ case CS35L45_ASPTX1_INPUT:
+ case CS35L45_ASPTX2_INPUT:
+ case CS35L45_ASPTX3_INPUT:
+ case CS35L45_ASPTX4_INPUT:
+ case CS35L45_ASPTX5_INPUT:
+ case CS35L45_AMP_PCM_CONTROL:
+ case CS35L45_AMP_PCM_HPF_TST:
+ case CS35L45_IRQ1_EINT_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L45_DEVID ... CS35L45_OTPID:
+ case CS35L45_SFT_RESET:
+ case CS35L45_GLOBAL_ENABLES:
+ case CS35L45_ERROR_RELEASE:
+ case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
+ case CS35L45_IRQ1_EINT_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config cs35l45_i2c_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L45_LASTREG,
+ .reg_defaults = cs35l45_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+ .volatile_reg = cs35l45_volatile_reg,
+ .readable_reg = cs35l45_readable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45_TABLES);
+
+const struct regmap_config cs35l45_spi_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L45_LASTREG,
+ .reg_defaults = cs35l45_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
+ .volatile_reg = cs35l45_volatile_reg,
+ .readable_reg = cs35l45_readable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45_TABLES);
+
+static const struct {
+ u8 cfg_id;
+ u32 freq;
+} cs35l45_pll_refclk_freq[] = {
+ { 0x0C, 128000 },
+ { 0x0F, 256000 },
+ { 0x11, 384000 },
+ { 0x12, 512000 },
+ { 0x15, 768000 },
+ { 0x17, 1024000 },
+ { 0x19, 1411200 },
+ { 0x1B, 1536000 },
+ { 0x1C, 2116800 },
+ { 0x1D, 2048000 },
+ { 0x1E, 2304000 },
+ { 0x1F, 2822400 },
+ { 0x21, 3072000 },
+ { 0x23, 4233600 },
+ { 0x24, 4096000 },
+ { 0x25, 4608000 },
+ { 0x26, 5644800 },
+ { 0x27, 6000000 },
+ { 0x28, 6144000 },
+ { 0x29, 6350400 },
+ { 0x2A, 6912000 },
+ { 0x2D, 7526400 },
+ { 0x2E, 8467200 },
+ { 0x2F, 8192000 },
+ { 0x30, 9216000 },
+ { 0x31, 11289600 },
+ { 0x33, 12288000 },
+ { 0x37, 16934400 },
+ { 0x38, 18432000 },
+ { 0x39, 22579200 },
+ { 0x3B, 24576000 },
+};
+
+unsigned int cs35l45_get_clk_freq_id(unsigned int freq)
+{
+ int i;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_pll_refclk_freq); ++i) {
+ if (cs35l45_pll_refclk_freq[i].freq == freq)
+ return cs35l45_pll_refclk_freq[i].cfg_id;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45_TABLES);
+
+MODULE_DESCRIPTION("ASoC CS35L45 driver tables");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
new file mode 100644
index 000000000000..2367c1a4c10e
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.c
@@ -0,0 +1,690 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// cs35l45.c - CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs35l45.h"
+
+static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES,
+ CS35L45_GLOBAL_EN_MASK);
+
+ usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100);
+
+ regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const cs35l45_asp_tx_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2",
+ "VMON", "IMON", "ERR_VOL",
+ "VDD_BATTMON", "VDD_BSTMON",
+ "Interpolator", "IL_TARGET",
+};
+
+static const unsigned int cs35l45_asp_tx_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+ CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+ CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
+};
+
+static const struct soc_enum cs35l45_asp_tx_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt,
+ cs35l45_asp_tx_val),
+};
+
+static const char * const cs35l45_dac_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2"
+};
+
+static const unsigned int cs35l45_dac_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2
+};
+
+static const struct soc_enum cs35l45_dacpcm_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt,
+ cs35l45_dac_val),
+};
+
+static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
+ SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]),
+ SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]),
+ SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]),
+ SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]),
+ SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
+};
+
+static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
+ SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]),
+};
+
+static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
+ cs35l45_global_en_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SIGGEN("VMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("IMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"),
+ SND_SOC_DAPM_SIGGEN("ERR_VOL"),
+ SND_SOC_DAPM_SIGGEN("AMP_INTP"),
+ SND_SOC_DAPM_SIGGEN("IL_TARGET"),
+ SND_SOC_DAPM_ADC("VMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0),
+ SND_SOC_DAPM_ADC("IMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0),
+ SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, CS35L45_BLOCK_ENABLES,
+ CS35L45_VDD_BATTMON_EN_SHIFT, 0),
+ SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, CS35L45_BLOCK_ENABLES,
+ CS35L45_VDD_BSTMON_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]),
+ SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]),
+ SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]),
+ SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
+ SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
+
+ SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
+
+ SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+};
+
+#define CS35L45_ASP_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "VMON", "VMON" }, \
+ { name" Source", "IMON", "IMON" }, \
+ { name" Source", "ERR_VOL", "ERR_VOL" }, \
+ { name" Source", "VDD_BATTMON", "VDD_BATTMON" }, \
+ { name" Source", "VDD_BSTMON", "VDD_BSTMON" }, \
+ { name" Source", "Interpolator", "AMP_INTP" }, \
+ { name" Source", "IL_TARGET", "IL_TARGET" }
+
+#define CS35L45_DAC_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }
+
+static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
+ /* Feedback */
+ { "VMON", NULL, "VMON_SRC" },
+ { "IMON", NULL, "IMON_SRC" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" },
+
+ { "Capture", NULL, "ASP_TX1"},
+ { "Capture", NULL, "ASP_TX2"},
+ { "Capture", NULL, "ASP_TX3"},
+ { "Capture", NULL, "ASP_TX4"},
+ { "Capture", NULL, "ASP_TX5"},
+ { "ASP_TX1", NULL, "ASP_TX1 Source"},
+ { "ASP_TX2", NULL, "ASP_TX2 Source"},
+ { "ASP_TX3", NULL, "ASP_TX3 Source"},
+ { "ASP_TX4", NULL, "ASP_TX4 Source"},
+ { "ASP_TX5", NULL, "ASP_TX5 Source"},
+
+ { "ASP_TX1", NULL, "ASP_EN" },
+ { "ASP_TX2", NULL, "ASP_EN" },
+ { "ASP_TX3", NULL, "ASP_EN" },
+ { "ASP_TX4", NULL, "ASP_EN" },
+ { "ASP_TX1", NULL, "GLOBAL_EN" },
+ { "ASP_TX2", NULL, "GLOBAL_EN" },
+ { "ASP_TX3", NULL, "GLOBAL_EN" },
+ { "ASP_TX4", NULL, "GLOBAL_EN" },
+ { "ASP_TX5", NULL, "GLOBAL_EN" },
+
+ CS35L45_ASP_MUX_ROUTE("ASP_TX1"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX2"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX3"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX4"),
+ CS35L45_ASP_MUX_ROUTE("ASP_TX5"),
+
+ /* Playback */
+ { "ASP_RX1", NULL, "Playback" },
+ { "ASP_RX2", NULL, "Playback" },
+ { "ASP_RX1", NULL, "ASP_EN" },
+ { "ASP_RX2", NULL, "ASP_EN" },
+
+ { "AMP", NULL, "DACPCM1 Source"},
+ { "AMP", NULL, "GLOBAL_EN"},
+
+ CS35L45_DAC_MUX_ROUTE("DACPCM1"),
+
+ { "SPK", NULL, "AMP"},
+};
+
+static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+
+static const struct snd_kcontrol_new cs35l45_controls[] = {
+ /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */
+ SOC_SINGLE_S_TLV("Digital PCM Volume",
+ CS35L45_AMP_PCM_CONTROL,
+ CS35L45_AMP_VOL_PCM_SHIFT + 1,
+ -409, 48,
+ (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
+ 0, cs35l45_dig_pcm_vol_tlv),
+};
+
+static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
+{
+ unsigned int val;
+ int freq_id;
+
+ freq_id = cs35l45_get_clk_freq_id(freq);
+ if (freq_id < 0) {
+ dev_err(cs35l45->dev, "Invalid freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val);
+ val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT;
+ if (val == freq_id)
+ return 0;
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+ regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT,
+ CS35L45_PLL_REFCLK_FREQ_MASK,
+ freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK);
+ regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK);
+
+ return 0;
+}
+
+static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int asp_fmt, fsync_inv, bclk_inv;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(cs35l45->dev, "Invalid DAI clocking\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = CS35l45_ASP_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = CS35L45_ASP_FMT_I2S;
+ break;
+ default:
+ dev_err(cs35l45->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ fsync_inv = 1;
+ bclk_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ fsync_inv = 0;
+ bclk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsync_inv = 1;
+ bclk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ fsync_inv = 0;
+ bclk_inv = 0;
+ break;
+ default:
+ dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_FMT_MASK |
+ CS35L45_ASP_FSYNC_INV_MASK |
+ CS35L45_ASP_BCLK_INV_MASK,
+ (asp_fmt << CS35L45_ASP_FMT_SHIFT) |
+ (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) |
+ (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT));
+
+ return 0;
+}
+
+static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt;
+ int bclk;
+
+ switch (params_rate(params)) {
+ case 44100:
+ global_fs = CS35L45_44P100_KHZ;
+ break;
+ case 48000:
+ global_fs = CS35L45_48P0_KHZ;
+ break;
+ case 88200:
+ global_fs = CS35L45_88P200_KHZ;
+ break;
+ case 96000:
+ global_fs = CS35L45_96P0_KHZ;
+ break;
+ default:
+ dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE,
+ CS35L45_GLOBAL_FS_MASK,
+ global_fs << CS35L45_GLOBAL_FS_SHIFT);
+
+ asp_wl = params_width(params);
+
+ if (cs35l45->slot_width)
+ asp_width = cs35l45->slot_width;
+ else
+ asp_width = params_width(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_WIDTH_RX_MASK,
+ asp_width << CS35L45_ASP_WIDTH_RX_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5,
+ CS35L45_ASP_WL_MASK,
+ asp_wl << CS35L45_ASP_WL_SHIFT);
+ } else {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2,
+ CS35L45_ASP_WIDTH_TX_MASK,
+ asp_width << CS35L45_ASP_WIDTH_TX_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1,
+ CS35L45_ASP_WL_MASK,
+ asp_wl << CS35L45_ASP_WL_SHIFT);
+ }
+
+ if (cs35l45->sysclk_set)
+ return 0;
+
+ /* I2S always has an even number of channels */
+ regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt);
+ asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT;
+ if (asp_fmt == CS35L45_ASP_FMT_I2S)
+ slot_multiple = 2;
+ else
+ slot_multiple = 1;
+
+ bclk = snd_soc_tdm_params_to_bclk(params, asp_width,
+ cs35l45->slot_count, slot_multiple);
+
+ return cs35l45_set_pll(cs35l45, bclk);
+}
+
+static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+
+ if (slot_width && ((slot_width < 16) || (slot_width > 128)))
+ return -EINVAL;
+
+ cs35l45->slot_width = slot_width;
+ cs35l45->slot_count = slots;
+
+ return 0;
+}
+
+static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ if (clk_id != 0) {
+ dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ cs35l45->sysclk_set = false;
+ if (freq == 0)
+ return 0;
+
+ ret = cs35l45_set_pll(cs35l45, freq);
+ if (ret < 0)
+ return -EINVAL;
+
+ cs35l45->sysclk_set = true;
+
+ return 0;
+}
+
+static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int global_fs, val, hpf_tune;
+
+ if (mute)
+ return 0;
+
+ regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs);
+ global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT;
+ switch (global_fs) {
+ case CS35L45_44P100_KHZ:
+ hpf_tune = CS35L45_HPF_44P1;
+ break;
+ case CS35L45_88P200_KHZ:
+ hpf_tune = CS35L45_HPF_88P2;
+ break;
+ default:
+ hpf_tune = CS35l45_HPF_DEFAULT;
+ break;
+ }
+
+ regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val);
+ if (val != hpf_tune) {
+ struct reg_sequence hpf_override_seq[] = {
+ { 0x00000040, 0x00000055 },
+ { 0x00000040, 0x000000AA },
+ { 0x00000044, 0x00000055 },
+ { 0x00000044, 0x000000AA },
+ { CS35L45_AMP_PCM_HPF_TST, hpf_tune },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000000 },
+ };
+ regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq,
+ ARRAY_SIZE(hpf_override_seq));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = {
+ .set_fmt = cs35l45_asp_set_fmt,
+ .hw_params = cs35l45_asp_hw_params,
+ .set_tdm_slot = cs35l45_asp_set_tdm_slot,
+ .set_sysclk = cs35l45_asp_set_sysclk,
+ .mute_stream = cs35l45_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs35l45_dai[] = {
+ {
+ .name = "cs35l45",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L45_RATES,
+ .formats = CS35L45_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = CS35L45_RATES,
+ .formats = CS35L45_FORMATS,
+ },
+ .symmetric_rate = true,
+ .symmetric_sample_bits = true,
+ .ops = &cs35l45_asp_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver cs35l45_component = {
+ .dapm_widgets = cs35l45_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
+
+ .dapm_routes = cs35l45_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes),
+
+ .controls = cs35l45_controls,
+ .num_controls = ARRAY_SIZE(cs35l45_controls),
+
+ .name = "cs35l45",
+};
+
+static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs35l45->regmap, true);
+
+ dev_dbg(cs35l45->dev, "Runtime suspended\n");
+
+ return 0;
+}
+
+static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
+{
+ struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l45->dev, "Runtime resume\n");
+
+ regcache_cache_only(cs35l45->regmap, false);
+ ret = regcache_sync(cs35l45->regmap);
+ if (ret != 0)
+ dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
+
+ /* Clear global error status */
+ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK);
+ return ret;
+}
+
+static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
+{
+ unsigned int val;
+
+ if (device_property_read_u32(cs35l45->dev,
+ "cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
+ regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3,
+ CS35L45_ASP_DOUT_HIZ_CTRL_MASK,
+ val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT);
+ }
+
+ return 0;
+}
+
+static int cs35l45_initialize(struct cs35l45_private *cs35l45)
+{
+ struct device *dev = cs35l45->dev;
+ unsigned int dev_id[5];
+ unsigned int sts;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts,
+ (sts & CS35L45_OTP_BOOT_DONE_STS_MASK),
+ 1000, 5000);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n");
+ return ret;
+ }
+
+ ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id));
+ if (ret) {
+ dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret);
+ return ret;
+ }
+
+ switch (dev_id[0]) {
+ case 0x35A450:
+ break;
+ default:
+ dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]);
+ return -ENODEV;
+ }
+
+ dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n",
+ dev_id[1], dev_id[4]);
+
+ regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4,
+ CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK);
+
+ ret = cs35l45_apply_patch(cs35l45);
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply init patch %d\n", ret);
+ return ret;
+ }
+
+ ret = cs35l45_apply_property_config(cs35l45);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l45->dev);
+ pm_runtime_set_active(cs35l45->dev);
+ pm_runtime_enable(cs35l45->dev);
+
+ return 0;
+}
+
+int cs35l45_probe(struct cs35l45_private *cs35l45)
+{
+ struct device *dev = cs35l45->dev;
+ int ret;
+
+ cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
+ if (IS_ERR(cs35l45->vdd_batt))
+ return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt),
+ "Failed to request vdd-batt\n");
+
+ cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a");
+ if (IS_ERR(cs35l45->vdd_a))
+ return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a),
+ "Failed to request vdd-a\n");
+
+ /* VDD_BATT must always be enabled before other supplies */
+ ret = regulator_enable(cs35l45->vdd_batt);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n");
+
+ ret = regulator_enable(cs35l45->vdd_a);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable vdd-a\n");
+
+ /* If reset is shared only one instance can claim it */
+ cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l45->reset_gpio)) {
+ ret = PTR_ERR(cs35l45->reset_gpio);
+ cs35l45->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_dbg(dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err_probe(dev, ret, "Failed to get reset GPIO\n");
+ goto err;
+ }
+ }
+
+ if (cs35l45->reset_gpio) {
+ usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100);
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 1);
+ }
+
+ usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100);
+
+ ret = cs35l45_initialize(cs35l45);
+ if (ret < 0)
+ goto err_reset;
+
+ ret = devm_snd_soc_register_component(dev, &cs35l45_component,
+ cs35l45_dai,
+ ARRAY_SIZE(cs35l45_dai));
+ if (ret < 0)
+ goto err_reset;
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+err:
+ regulator_disable(cs35l45->vdd_a);
+ regulator_disable(cs35l45->vdd_batt);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45);
+
+int cs35l45_remove(struct cs35l45_private *cs35l45)
+{
+ pm_runtime_disable(cs35l45->dev);
+
+ gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+ regulator_disable(cs35l45->vdd_a);
+ /* VDD_BATT must be the last to power-off */
+ regulator_disable(cs35l45->vdd_batt);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_remove, SND_SOC_CS35L45);
+
+const struct dev_pm_ops cs35l45_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45);
+
+MODULE_DESCRIPTION("ASoC CS35L45 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
new file mode 100644
index 000000000000..4e266d19cd1c
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * cs35l45.h - CS35L45 ALSA SoC audio driver
+ *
+ * Copyright 2019-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef CS35L45_H
+#define CS35L45_H
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define CS35L45_DEVID 0x00000000
+#define CS35L45_REVID 0x00000004
+#define CS35L45_RELID 0x0000000C
+#define CS35L45_OTPID 0x00000010
+#define CS35L45_SFT_RESET 0x00000020
+#define CS35L45_GLOBAL_ENABLES 0x00002014
+#define CS35L45_BLOCK_ENABLES 0x00002018
+#define CS35L45_BLOCK_ENABLES2 0x0000201C
+#define CS35L45_ERROR_RELEASE 0x00002034
+#define CS35L45_REFCLK_INPUT 0x00002C04
+#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
+#define CS35L45_BOOST_CCM_CFG 0x00003808
+#define CS35L45_BOOST_DCM_CFG 0x0000380C
+#define CS35L45_BOOST_OV_CFG 0x0000382C
+#define CS35L45_ASP_ENABLES1 0x00004800
+#define CS35L45_ASP_CONTROL1 0x00004804
+#define CS35L45_ASP_CONTROL2 0x00004808
+#define CS35L45_ASP_CONTROL3 0x0000480C
+#define CS35L45_ASP_FRAME_CONTROL1 0x00004810
+#define CS35L45_ASP_FRAME_CONTROL2 0x00004814
+#define CS35L45_ASP_FRAME_CONTROL5 0x00004820
+#define CS35L45_ASP_DATA_CONTROL1 0x00004830
+#define CS35L45_ASP_DATA_CONTROL5 0x00004840
+#define CS35L45_DACPCM1_INPUT 0x00004C00
+#define CS35L45_ASPTX1_INPUT 0x00004C20
+#define CS35L45_ASPTX2_INPUT 0x00004C24
+#define CS35L45_ASPTX3_INPUT 0x00004C28
+#define CS35L45_ASPTX4_INPUT 0x00004C2C
+#define CS35L45_ASPTX5_INPUT 0x00004C30
+#define CS35L45_LDPM_CONFIG 0x00006404
+#define CS35L45_AMP_PCM_CONTROL 0x00007000
+#define CS35L45_AMP_PCM_HPF_TST 0x00007004
+#define CS35L45_IRQ1_EINT_4 0x0000E01C
+#define CS35L45_LASTREG 0x0000E01C
+
+/* SFT_RESET */
+#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000
+
+/* GLOBAL_ENABLES */
+#define CS35L45_GLOBAL_EN_SHIFT 0
+#define CS35L45_GLOBAL_EN_MASK BIT(0)
+
+/* BLOCK_ENABLES */
+#define CS35L45_IMON_EN_SHIFT 13
+#define CS35L45_VMON_EN_SHIFT 12
+#define CS35L45_VDD_BSTMON_EN_SHIFT 9
+#define CS35L45_VDD_BATTMON_EN_SHIFT 8
+#define CS35L45_BST_EN_SHIFT 4
+#define CS35L45_BST_EN_MASK GENMASK(5, 4)
+
+#define CS35L45_BST_DISABLE_FET_ON 0x01
+
+/* BLOCK_ENABLES2 */
+#define CS35L45_ASP_EN_SHIFT 27
+
+/* ERROR_RELEASE */
+#define CS35L45_GLOBAL_ERR_RLS_MASK BIT(11)
+
+/* REFCLK_INPUT */
+#define CS35L45_PLL_FORCE_EN_SHIFT 16
+#define CS35L45_PLL_FORCE_EN_MASK BIT(16)
+#define CS35L45_PLL_OPEN_LOOP_SHIFT 11
+#define CS35L45_PLL_OPEN_LOOP_MASK BIT(11)
+#define CS35L45_PLL_REFCLK_FREQ_SHIFT 5
+#define CS35L45_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
+#define CS35L45_PLL_REFCLK_EN_SHIFT 4
+#define CS35L45_PLL_REFCLK_EN_MASK BIT(4)
+#define CS35L45_PLL_REFCLK_SEL_SHIFT 0
+#define CS35L45_PLL_REFCLK_SEL_MASK GENMASK(2, 0)
+
+#define CS35L45_PLL_REFCLK_SEL_BCLK 0x0
+
+/* GLOBAL_SAMPLE_RATE */
+#define CS35L45_GLOBAL_FS_SHIFT 0
+#define CS35L45_GLOBAL_FS_MASK GENMASK(4, 0)
+
+#define CS35L45_48P0_KHZ 0x03
+#define CS35L45_96P0_KHZ 0x04
+#define CS35L45_44P100_KHZ 0x0B
+#define CS35L45_88P200_KHZ 0x0C
+
+/* ASP_ENABLES_1 */
+#define CS35L45_ASP_RX2_EN_SHIFT 17
+#define CS35L45_ASP_RX1_EN_SHIFT 16
+#define CS35L45_ASP_TX5_EN_SHIFT 4
+#define CS35L45_ASP_TX4_EN_SHIFT 3
+#define CS35L45_ASP_TX3_EN_SHIFT 2
+#define CS35L45_ASP_TX2_EN_SHIFT 1
+#define CS35L45_ASP_TX1_EN_SHIFT 0
+
+/* ASP_CONTROL2 */
+#define CS35L45_ASP_WIDTH_RX_SHIFT 24
+#define CS35L45_ASP_WIDTH_RX_MASK GENMASK(31, 24)
+#define CS35L45_ASP_WIDTH_TX_SHIFT 16
+#define CS35L45_ASP_WIDTH_TX_MASK GENMASK(23, 16)
+#define CS35L45_ASP_FMT_SHIFT 8
+#define CS35L45_ASP_FMT_MASK GENMASK(10, 8)
+#define CS35L45_ASP_BCLK_INV_SHIFT 6
+#define CS35L45_ASP_BCLK_INV_MASK BIT(6)
+#define CS35L45_ASP_FSYNC_INV_SHIFT 2
+#define CS35L45_ASP_FSYNC_INV_MASK BIT(2)
+
+#define CS35l45_ASP_FMT_DSP_A 0
+#define CS35L45_ASP_FMT_I2S 2
+
+/* ASP_CONTROL3 */
+#define CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT 0
+#define CS35L45_ASP_DOUT_HIZ_CTRL_MASK GENMASK(1, 0)
+
+/* ASP_FRAME_CONTROL1 */
+#define CS35L45_ASP_TX4_SLOT_SHIFT 24
+#define CS35L45_ASP_TX4_SLOT_MASK GENMASK(29, 24)
+#define CS35L45_ASP_TX3_SLOT_SHIFT 16
+#define CS35L45_ASP_TX3_SLOT_MASK GENMASK(21, 16)
+#define CS35L45_ASP_TX2_SLOT_SHIFT 8
+#define CS35L45_ASP_TX2_SLOT_MASK GENMASK(13, 8)
+#define CS35L45_ASP_TX1_SLOT_SHIFT 0
+#define CS35L45_ASP_TX1_SLOT_MASK GENMASK(5, 0)
+
+#define CS35L45_ASP_TX_ALL_SLOTS (CS35L45_ASP_TX4_SLOT_MASK | \
+ CS35L45_ASP_TX3_SLOT_MASK | \
+ CS35L45_ASP_TX2_SLOT_MASK | \
+ CS35L45_ASP_TX1_SLOT_MASK)
+/* ASP_FRAME_CONTROL5 */
+#define CS35L45_ASP_RX2_SLOT_SHIFT 8
+#define CS35L45_ASP_RX2_SLOT_MASK GENMASK(13, 8)
+#define CS35L45_ASP_RX1_SLOT_SHIFT 0
+#define CS35L45_ASP_RX1_SLOT_MASK GENMASK(5, 0)
+
+#define CS35L45_ASP_RX_ALL_SLOTS (CS35L45_ASP_RX2_SLOT_MASK | \
+ CS35L45_ASP_RX1_SLOT_MASK)
+
+/* ASP_DATA_CONTROL1 */
+/* ASP_DATA_CONTROL5 */
+#define CS35L45_ASP_WL_SHIFT 0
+#define CS35L45_ASP_WL_MASK GENMASK(5, 0)
+
+/* AMP_PCM_CONTROL */
+#define CS35L45_AMP_VOL_PCM_SHIFT 0
+#define CS35L45_AMP_VOL_PCM_WIDTH 11
+
+/* AMP_PCM_HPF_TST */
+#define CS35l45_HPF_DEFAULT 0x00000000
+#define CS35L45_HPF_44P1 0x000108BD
+#define CS35L45_HPF_88P2 0x0001045F
+
+/* IRQ1_EINT_4 */
+#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1)
+#define CS35L45_OTP_BUSY_MASK BIT(0)
+
+/* Mixer sources */
+#define CS35L45_PCM_SRC_MASK 0x7F
+#define CS35L45_PCM_SRC_ZERO 0x00
+#define CS35L45_PCM_SRC_ASP_RX1 0x08
+#define CS35L45_PCM_SRC_ASP_RX2 0x09
+#define CS35L45_PCM_SRC_VMON 0x18
+#define CS35L45_PCM_SRC_IMON 0x19
+#define CS35L45_PCM_SRC_ERR_VOL 0x20
+#define CS35L45_PCM_SRC_CLASSH_TGT 0x21
+#define CS35L45_PCM_SRC_VDD_BATTMON 0x28
+#define CS35L45_PCM_SRC_VDD_BSTMON 0x29
+#define CS35L45_PCM_SRC_TEMPMON 0x3A
+#define CS35L45_PCM_SRC_INTERPOLATOR 0x40
+#define CS35L45_PCM_SRC_IL_TARGET 0x48
+
+#define CS35L45_RESET_HOLD_US 2000
+#define CS35L45_RESET_US 2000
+#define CS35L45_POST_GLOBAL_EN_US 5000
+#define CS35L45_PRE_GLOBAL_DIS_US 3000
+
+#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE| \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define CS35L45_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+struct cs35l45_private {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vdd_batt;
+ struct regulator *vdd_a;
+ bool initialized;
+ bool sysclk_set;
+ u8 slot_width;
+ u8 slot_count;
+};
+
+extern const struct dev_pm_ops cs35l45_pm_ops;
+extern const struct regmap_config cs35l45_i2c_regmap;
+extern const struct regmap_config cs35l45_spi_regmap;
+int cs35l45_apply_patch(struct cs35l45_private *cs43l45);
+unsigned int cs35l45_get_clk_freq_id(unsigned int freq);
+int cs35l45_probe(struct cs35l45_private *cs35l45);
+int cs35l45_remove(struct cs35l45_private *cs35l45);
+
+#endif /* CS35L45_H */
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
index 20126cc675b1..881c5ba70c0e 100644
--- a/sound/soc/codecs/cs4234.c
+++ b/sound/soc/codecs/cs4234.c
@@ -663,6 +663,7 @@ static const struct snd_soc_component_driver soc_component_cs4234 = {
.non_legacy_dai_naming = 1,
.idle_bias_on = 1,
.suspend_bias_off = 1,
+ .endianness = 1,
};
static const struct regmap_config cs4234_regmap = {
@@ -731,7 +732,7 @@ static int cs4234_powerup(struct cs4234 *cs4234)
return 0;
}
-static int cs4234_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id)
+static int cs4234_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4234 *cs4234;
struct device *dev = &i2c_client->dev;
@@ -908,7 +909,7 @@ static struct i2c_driver cs4234_i2c_driver = {
.pm = &cs4234_pm,
.of_match_table = cs4234_of_match,
},
- .probe = cs4234_i2c_probe,
+ .probe_new = 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 4415fb364d4d..86bfa8d5ec78 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -568,8 +568,7 @@ static const struct regmap_config cs4265_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int cs4265_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4265_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4265_private *cs4265;
int ret;
@@ -653,7 +652,7 @@ static struct i2c_driver cs4265_i2c_driver = {
.of_match_table = cs4265_of_match,
},
.id_table = cs4265_id,
- .probe = cs4265_i2c_probe,
+ .probe_new = cs4265_i2c_probe,
.remove = cs4265_i2c_remove,
};
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 2d239e983a83..531f63b01554 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -32,18 +32,9 @@
#include <linux/gpio/consumer.h>
#include <linux/of_device.h>
-/*
- * The codec isn't really big-endian or little-endian, since the I2S
- * interface requires data to be sent serially with the MSbit first.
- * However, to support BE and LE I2S devices, we specify both here. That
- * way, ALSA will always match the bit patterns.
- */
-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
/* CS4270 registers addresses */
#define CS4270_CHIPID 0x01 /* Chip ID */
@@ -677,8 +668,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client)
* This function is called whenever the I2C subsystem finds a device that
* matches the device ID given via a prior call to i2c_add_driver().
*/
-static int cs4270_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4270_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4270_private *cs4270;
unsigned int val;
@@ -765,7 +755,7 @@ static struct i2c_driver cs4270_i2c_driver = {
.of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
- .probe = cs4270_i2c_probe,
+ .probe_new = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
};
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index 0a174236f573..0e8a7cf0da50 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -11,8 +11,7 @@
#include <sound/soc.h>
#include "cs4271.h"
-static int cs4271_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4271_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -35,7 +34,7 @@ static struct i2c_driver cs4271_i2c_driver = {
.name = "cs4271",
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
- .probe = cs4271_i2c_probe,
+ .probe_new = cs4271_i2c_probe,
.id_table = cs4271_i2c_id,
};
module_i2c_driver(cs4271_i2c_driver);
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index c8409d50e934..4fade2388797 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -2194,8 +2194,7 @@ static int __maybe_unused cs42l42_resume(struct device *dev)
return 0;
}
-static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l42_private *cs42l42;
int ret, i, devid;
@@ -2399,7 +2398,7 @@ static struct i2c_driver cs42l42_i2c_driver = {
.acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
},
.id_table = cs42l42_id,
- .probe = cs42l42_i2c_probe,
+ .probe_new = cs42l42_i2c_probe,
.remove = cs42l42_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index 60d3bdf5d7c9..5f50970375d4 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -2,7 +2,7 @@
/*
* cs42l42.h -- CS42L42 ALSA SoC audio driver header
*
- * Copyright 2016 Cirrus Logic, Inc.
+ * Copyright 2016-2022 Cirrus Logic, Inc.
*
* Author: James Schulman <james.schulman@cirrus.com>
* Author: Brian Austin <brian.austin@cirrus.com>
@@ -14,829 +14,7 @@
#include <linux/mutex.h>
#include <sound/jack.h>
-
-#define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */
-#define CS42L42_WIN_START 0x00
-#define CS42L42_WIN_LEN 0x100
-#define CS42L42_RANGE_MIN 0x00
-#define CS42L42_RANGE_MAX 0x7F
-
-#define CS42L42_PAGE_10 0x1000
-#define CS42L42_PAGE_11 0x1100
-#define CS42L42_PAGE_12 0x1200
-#define CS42L42_PAGE_13 0x1300
-#define CS42L42_PAGE_15 0x1500
-#define CS42L42_PAGE_19 0x1900
-#define CS42L42_PAGE_1B 0x1B00
-#define CS42L42_PAGE_1C 0x1C00
-#define CS42L42_PAGE_1D 0x1D00
-#define CS42L42_PAGE_1F 0x1F00
-#define CS42L42_PAGE_20 0x2000
-#define CS42L42_PAGE_21 0x2100
-#define CS42L42_PAGE_23 0x2300
-#define CS42L42_PAGE_24 0x2400
-#define CS42L42_PAGE_25 0x2500
-#define CS42L42_PAGE_26 0x2600
-#define CS42L42_PAGE_28 0x2800
-#define CS42L42_PAGE_29 0x2900
-#define CS42L42_PAGE_2A 0x2A00
-#define CS42L42_PAGE_30 0x3000
-
-#define CS42L42_CHIP_ID 0x42A42
-
-/* Page 0x10 Global Registers */
-#define CS42L42_DEVID_AB (CS42L42_PAGE_10 + 0x01)
-#define CS42L42_DEVID_CD (CS42L42_PAGE_10 + 0x02)
-#define CS42L42_DEVID_E (CS42L42_PAGE_10 + 0x03)
-#define CS42L42_FABID (CS42L42_PAGE_10 + 0x04)
-#define CS42L42_REVID (CS42L42_PAGE_10 + 0x05)
-#define CS42L42_FRZ_CTL (CS42L42_PAGE_10 + 0x06)
-
-#define CS42L42_SRC_CTL (CS42L42_PAGE_10 + 0x07)
-#define CS42L42_SRC_BYPASS_DAC_SHIFT 1
-#define CS42L42_SRC_BYPASS_DAC_MASK (1 << CS42L42_SRC_BYPASS_DAC_SHIFT)
-
-#define CS42L42_MCLK_STATUS (CS42L42_PAGE_10 + 0x08)
-
-#define CS42L42_MCLK_CTL (CS42L42_PAGE_10 + 0x09)
-#define CS42L42_INTERNAL_FS_SHIFT 1
-#define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT)
-
-#define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A)
-#define CS42L42_SLOW_START_ENABLE (CS42L42_PAGE_10 + 0x0B)
-#define CS42L42_SLOW_START_EN_MASK GENMASK(6, 4)
-#define CS42L42_SLOW_START_EN_SHIFT 4
-#define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E)
-#define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F)
-#define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10)
-
-/* Page 0x11 Power and Headset Detect Registers */
-#define CS42L42_PWR_CTL1 (CS42L42_PAGE_11 + 0x01)
-#define CS42L42_ASP_DAO_PDN_SHIFT 7
-#define CS42L42_ASP_DAO_PDN_MASK (1 << CS42L42_ASP_DAO_PDN_SHIFT)
-#define CS42L42_ASP_DAI_PDN_SHIFT 6
-#define CS42L42_ASP_DAI_PDN_MASK (1 << CS42L42_ASP_DAI_PDN_SHIFT)
-#define CS42L42_MIXER_PDN_SHIFT 5
-#define CS42L42_MIXER_PDN_MASK (1 << CS42L42_MIXER_PDN_SHIFT)
-#define CS42L42_EQ_PDN_SHIFT 4
-#define CS42L42_EQ_PDN_MASK (1 << CS42L42_EQ_PDN_SHIFT)
-#define CS42L42_HP_PDN_SHIFT 3
-#define CS42L42_HP_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT)
-#define CS42L42_ADC_PDN_SHIFT 2
-#define CS42L42_ADC_PDN_MASK (1 << CS42L42_ADC_PDN_SHIFT)
-#define CS42L42_PDN_ALL_SHIFT 0
-#define CS42L42_PDN_ALL_MASK (1 << CS42L42_PDN_ALL_SHIFT)
-
-#define CS42L42_PWR_CTL2 (CS42L42_PAGE_11 + 0x02)
-#define CS42L42_ADC_SRC_PDNB_SHIFT 0
-#define CS42L42_ADC_SRC_PDNB_MASK (1 << CS42L42_ADC_SRC_PDNB_SHIFT)
-#define CS42L42_DAC_SRC_PDNB_SHIFT 1
-#define CS42L42_DAC_SRC_PDNB_MASK (1 << CS42L42_DAC_SRC_PDNB_SHIFT)
-#define CS42L42_ASP_DAI1_PDN_SHIFT 2
-#define CS42L42_ASP_DAI1_PDN_MASK (1 << CS42L42_ASP_DAI1_PDN_SHIFT)
-#define CS42L42_SRC_PDN_OVERRIDE_SHIFT 3
-#define CS42L42_SRC_PDN_OVERRIDE_MASK (1 << CS42L42_SRC_PDN_OVERRIDE_SHIFT)
-#define CS42L42_DISCHARGE_FILT_SHIFT 4
-#define CS42L42_DISCHARGE_FILT_MASK (1 << CS42L42_DISCHARGE_FILT_SHIFT)
-
-#define CS42L42_PWR_CTL3 (CS42L42_PAGE_11 + 0x03)
-#define CS42L42_RING_SENSE_PDNB_SHIFT 1
-#define CS42L42_RING_SENSE_PDNB_MASK (1 << \
- CS42L42_RING_SENSE_PDNB_SHIFT)
-#define CS42L42_VPMON_PDNB_SHIFT 2
-#define CS42L42_VPMON_PDNB_MASK (1 << \
- CS42L42_VPMON_PDNB_SHIFT)
-#define CS42L42_SW_CLK_STP_STAT_SEL_SHIFT 5
-#define CS42L42_SW_CLK_STP_STAT_SEL_MASK (3 << \
- CS42L42_SW_CLK_STP_STAT_SEL_SHIFT)
-
-#define CS42L42_RSENSE_CTL1 (CS42L42_PAGE_11 + 0x04)
-#define CS42L42_RS_TRIM_R_SHIFT 0
-#define CS42L42_RS_TRIM_R_MASK (1 << \
- CS42L42_RS_TRIM_R_SHIFT)
-#define CS42L42_RS_TRIM_T_SHIFT 1
-#define CS42L42_RS_TRIM_T_MASK (1 << \
- CS42L42_RS_TRIM_T_SHIFT)
-#define CS42L42_HPREF_RS_SHIFT 2
-#define CS42L42_HPREF_RS_MASK (1 << \
- CS42L42_HPREF_RS_SHIFT)
-#define CS42L42_HSBIAS_FILT_REF_RS_SHIFT 3
-#define CS42L42_HSBIAS_FILT_REF_RS_MASK (1 << \
- CS42L42_HSBIAS_FILT_REF_RS_SHIFT)
-#define CS42L42_RING_SENSE_PU_HIZ_SHIFT 6
-#define CS42L42_RING_SENSE_PU_HIZ_MASK (1 << \
- CS42L42_RING_SENSE_PU_HIZ_SHIFT)
-
-#define CS42L42_RSENSE_CTL2 (CS42L42_PAGE_11 + 0x05)
-#define CS42L42_TS_RS_GATE_SHIFT 7
-#define CS42L42_TS_RS_GATE_MAS (1 << CS42L42_TS_RS_GATE_SHIFT)
-
-#define CS42L42_OSC_SWITCH (CS42L42_PAGE_11 + 0x07)
-#define CS42L42_SCLK_PRESENT_SHIFT 0
-#define CS42L42_SCLK_PRESENT_MASK (1 << CS42L42_SCLK_PRESENT_SHIFT)
-
-#define CS42L42_OSC_SWITCH_STATUS (CS42L42_PAGE_11 + 0x09)
-#define CS42L42_OSC_SW_SEL_STAT_SHIFT 0
-#define CS42L42_OSC_SW_SEL_STAT_MASK (3 << CS42L42_OSC_SW_SEL_STAT_SHIFT)
-#define CS42L42_OSC_PDNB_STAT_SHIFT 2
-#define CS42L42_OSC_PDNB_STAT_MASK (1 << CS42L42_OSC_SW_SEL_STAT_SHIFT)
-
-#define CS42L42_RSENSE_CTL3 (CS42L42_PAGE_11 + 0x12)
-#define CS42L42_RS_RISE_DBNCE_TIME_SHIFT 0
-#define CS42L42_RS_RISE_DBNCE_TIME_MASK (7 << \
- CS42L42_RS_RISE_DBNCE_TIME_SHIFT)
-#define CS42L42_RS_FALL_DBNCE_TIME_SHIFT 3
-#define CS42L42_RS_FALL_DBNCE_TIME_MASK (7 << \
- CS42L42_RS_FALL_DBNCE_TIME_SHIFT)
-#define CS42L42_RS_PU_EN_SHIFT 6
-#define CS42L42_RS_PU_EN_MASK (1 << \
- CS42L42_RS_PU_EN_SHIFT)
-#define CS42L42_RS_INV_SHIFT 7
-#define CS42L42_RS_INV_MASK (1 << \
- CS42L42_RS_INV_SHIFT)
-
-#define CS42L42_TSENSE_CTL (CS42L42_PAGE_11 + 0x13)
-#define CS42L42_TS_RISE_DBNCE_TIME_SHIFT 0
-#define CS42L42_TS_RISE_DBNCE_TIME_MASK (7 << \
- CS42L42_TS_RISE_DBNCE_TIME_SHIFT)
-#define CS42L42_TS_FALL_DBNCE_TIME_SHIFT 3
-#define CS42L42_TS_FALL_DBNCE_TIME_MASK (7 << \
- CS42L42_TS_FALL_DBNCE_TIME_SHIFT)
-#define CS42L42_TS_INV_SHIFT 7
-#define CS42L42_TS_INV_MASK (1 << \
- CS42L42_TS_INV_SHIFT)
-
-#define CS42L42_TSRS_INT_DISABLE (CS42L42_PAGE_11 + 0x14)
-#define CS42L42_D_RS_PLUG_DBNC_SHIFT 0
-#define CS42L42_D_RS_PLUG_DBNC_MASK (1 << CS42L42_D_RS_PLUG_DBNC_SHIFT)
-#define CS42L42_D_RS_UNPLUG_DBNC_SHIFT 1
-#define CS42L42_D_RS_UNPLUG_DBNC_MASK (1 << CS42L42_D_RS_UNPLUG_DBNC_SHIFT)
-#define CS42L42_D_TS_PLUG_DBNC_SHIFT 2
-#define CS42L42_D_TS_PLUG_DBNC_MASK (1 << CS42L42_D_TS_PLUG_DBNC_SHIFT)
-#define CS42L42_D_TS_UNPLUG_DBNC_SHIFT 3
-#define CS42L42_D_TS_UNPLUG_DBNC_MASK (1 << CS42L42_D_TS_UNPLUG_DBNC_SHIFT)
-
-#define CS42L42_TRSENSE_STATUS (CS42L42_PAGE_11 + 0x15)
-#define CS42L42_RS_PLUG_DBNC_SHIFT 0
-#define CS42L42_RS_PLUG_DBNC_MASK (1 << CS42L42_RS_PLUG_DBNC_SHIFT)
-#define CS42L42_RS_UNPLUG_DBNC_SHIFT 1
-#define CS42L42_RS_UNPLUG_DBNC_MASK (1 << CS42L42_RS_UNPLUG_DBNC_SHIFT)
-#define CS42L42_TS_PLUG_DBNC_SHIFT 2
-#define CS42L42_TS_PLUG_DBNC_MASK (1 << CS42L42_TS_PLUG_DBNC_SHIFT)
-#define CS42L42_TS_UNPLUG_DBNC_SHIFT 3
-#define CS42L42_TS_UNPLUG_DBNC_MASK (1 << CS42L42_TS_UNPLUG_DBNC_SHIFT)
-
-#define CS42L42_HSDET_CTL1 (CS42L42_PAGE_11 + 0x1F)
-#define CS42L42_HSDET_COMP1_LVL_SHIFT 0
-#define CS42L42_HSDET_COMP1_LVL_MASK (15 << CS42L42_HSDET_COMP1_LVL_SHIFT)
-#define CS42L42_HSDET_COMP2_LVL_SHIFT 4
-#define CS42L42_HSDET_COMP2_LVL_MASK (15 << CS42L42_HSDET_COMP2_LVL_SHIFT)
-
-#define CS42L42_HSDET_COMP1_LVL_VAL 12 /* 1.25V Comparator */
-#define CS42L42_HSDET_COMP2_LVL_VAL 2 /* 1.75V Comparator */
-#define CS42L42_HSDET_COMP1_LVL_DEFAULT 7 /* 1V Comparator */
-#define CS42L42_HSDET_COMP2_LVL_DEFAULT 7 /* 2V Comparator */
-
-#define CS42L42_HSDET_CTL2 (CS42L42_PAGE_11 + 0x20)
-#define CS42L42_HSDET_AUTO_TIME_SHIFT 0
-#define CS42L42_HSDET_AUTO_TIME_MASK (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)
-#define CS42L42_HSBIAS_REF_SHIFT 3
-#define CS42L42_HSBIAS_REF_MASK (1 << CS42L42_HSBIAS_REF_SHIFT)
-#define CS42L42_HSDET_SET_SHIFT 4
-#define CS42L42_HSDET_SET_MASK (3 << CS42L42_HSDET_SET_SHIFT)
-#define CS42L42_HSDET_CTRL_SHIFT 6
-#define CS42L42_HSDET_CTRL_MASK (3 << CS42L42_HSDET_CTRL_SHIFT)
-
-#define CS42L42_HS_SWITCH_CTL (CS42L42_PAGE_11 + 0x21)
-#define CS42L42_SW_GNDHS_HS4_SHIFT 0
-#define CS42L42_SW_GNDHS_HS4_MASK (1 << CS42L42_SW_GNDHS_HS4_SHIFT)
-#define CS42L42_SW_GNDHS_HS3_SHIFT 1
-#define CS42L42_SW_GNDHS_HS3_MASK (1 << CS42L42_SW_GNDHS_HS3_SHIFT)
-#define CS42L42_SW_HSB_HS4_SHIFT 2
-#define CS42L42_SW_HSB_HS4_MASK (1 << CS42L42_SW_HSB_HS4_SHIFT)
-#define CS42L42_SW_HSB_HS3_SHIFT 3
-#define CS42L42_SW_HSB_HS3_MASK (1 << CS42L42_SW_HSB_HS3_SHIFT)
-#define CS42L42_SW_HSB_FILT_HS4_SHIFT 4
-#define CS42L42_SW_HSB_FILT_HS4_MASK (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT)
-#define CS42L42_SW_HSB_FILT_HS3_SHIFT 5
-#define CS42L42_SW_HSB_FILT_HS3_MASK (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT)
-#define CS42L42_SW_REF_HS4_SHIFT 6
-#define CS42L42_SW_REF_HS4_MASK (1 << CS42L42_SW_REF_HS4_SHIFT)
-#define CS42L42_SW_REF_HS3_SHIFT 7
-#define CS42L42_SW_REF_HS3_MASK (1 << CS42L42_SW_REF_HS3_SHIFT)
-
-#define CS42L42_HS_DET_STATUS (CS42L42_PAGE_11 + 0x24)
-#define CS42L42_HSDET_TYPE_SHIFT 0
-#define CS42L42_HSDET_TYPE_MASK (3 << CS42L42_HSDET_TYPE_SHIFT)
-#define CS42L42_HSDET_COMP1_OUT_SHIFT 6
-#define CS42L42_HSDET_COMP1_OUT_MASK (1 << CS42L42_HSDET_COMP1_OUT_SHIFT)
-#define CS42L42_HSDET_COMP2_OUT_SHIFT 7
-#define CS42L42_HSDET_COMP2_OUT_MASK (1 << CS42L42_HSDET_COMP2_OUT_SHIFT)
-#define CS42L42_PLUG_CTIA 0
-#define CS42L42_PLUG_OMTP 1
-#define CS42L42_PLUG_HEADPHONE 2
-#define CS42L42_PLUG_INVALID 3
-
-#define CS42L42_HSDET_SW_COMP1 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
- (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
- (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
- (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
- (0 << CS42L42_SW_REF_HS4_SHIFT) | \
- (1 << CS42L42_SW_REF_HS3_SHIFT))
-#define CS42L42_HSDET_SW_COMP2 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
- (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
- (1 << CS42L42_SW_HSB_HS3_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
- (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
- (1 << CS42L42_SW_REF_HS4_SHIFT) | \
- (0 << CS42L42_SW_REF_HS3_SHIFT))
-#define CS42L42_HSDET_SW_TYPE1 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
- (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
- (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
- (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
- (0 << CS42L42_SW_REF_HS4_SHIFT) | \
- (1 << CS42L42_SW_REF_HS3_SHIFT))
-#define CS42L42_HSDET_SW_TYPE2 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
- (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
- (1 << CS42L42_SW_HSB_HS3_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
- (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
- (1 << CS42L42_SW_REF_HS4_SHIFT) | \
- (0 << CS42L42_SW_REF_HS3_SHIFT))
-#define CS42L42_HSDET_SW_TYPE3 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
- (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
- (1 << CS42L42_SW_REF_HS4_SHIFT) | \
- (1 << CS42L42_SW_REF_HS3_SHIFT))
-#define CS42L42_HSDET_SW_TYPE4 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
- (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
- (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
- (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
- (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
- (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
- (0 << CS42L42_SW_REF_HS4_SHIFT) | \
- (1 << CS42L42_SW_REF_HS3_SHIFT))
-
-#define CS42L42_HSDET_COMP_TYPE1 1
-#define CS42L42_HSDET_COMP_TYPE2 2
-#define CS42L42_HSDET_COMP_TYPE3 0
-#define CS42L42_HSDET_COMP_TYPE4 3
-
-#define CS42L42_HS_CLAMP_DISABLE (CS42L42_PAGE_11 + 0x29)
-#define CS42L42_HS_CLAMP_DISABLE_SHIFT 0
-#define CS42L42_HS_CLAMP_DISABLE_MASK (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)
-
-/* Page 0x12 Clocking Registers */
-#define CS42L42_MCLK_SRC_SEL (CS42L42_PAGE_12 + 0x01)
-#define CS42L42_MCLKDIV_SHIFT 1
-#define CS42L42_MCLKDIV_MASK (1 << CS42L42_MCLKDIV_SHIFT)
-#define CS42L42_MCLK_SRC_SEL_SHIFT 0
-#define CS42L42_MCLK_SRC_SEL_MASK (1 << CS42L42_MCLK_SRC_SEL_SHIFT)
-
-#define CS42L42_SPDIF_CLK_CFG (CS42L42_PAGE_12 + 0x02)
-#define CS42L42_FSYNC_PW_LOWER (CS42L42_PAGE_12 + 0x03)
-
-#define CS42L42_FSYNC_PW_UPPER (CS42L42_PAGE_12 + 0x04)
-#define CS42L42_FSYNC_PULSE_WIDTH_SHIFT 0
-#define CS42L42_FSYNC_PULSE_WIDTH_MASK (0xff << \
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT)
-
-#define CS42L42_FSYNC_P_LOWER (CS42L42_PAGE_12 + 0x05)
-
-#define CS42L42_FSYNC_P_UPPER (CS42L42_PAGE_12 + 0x06)
-#define CS42L42_FSYNC_PERIOD_SHIFT 0
-#define CS42L42_FSYNC_PERIOD_MASK (0xff << CS42L42_FSYNC_PERIOD_SHIFT)
-
-#define CS42L42_ASP_CLK_CFG (CS42L42_PAGE_12 + 0x07)
-#define CS42L42_ASP_SCLK_EN_SHIFT 5
-#define CS42L42_ASP_SCLK_EN_MASK (1 << CS42L42_ASP_SCLK_EN_SHIFT)
-#define CS42L42_ASP_MASTER_MODE 0x01
-#define CS42L42_ASP_SLAVE_MODE 0x00
-#define CS42L42_ASP_MODE_SHIFT 4
-#define CS42L42_ASP_MODE_MASK (1 << CS42L42_ASP_MODE_SHIFT)
-#define CS42L42_ASP_SCPOL_SHIFT 2
-#define CS42L42_ASP_SCPOL_MASK (3 << CS42L42_ASP_SCPOL_SHIFT)
-#define CS42L42_ASP_SCPOL_NOR 3
-#define CS42L42_ASP_LCPOL_SHIFT 0
-#define CS42L42_ASP_LCPOL_MASK (3 << CS42L42_ASP_LCPOL_SHIFT)
-#define CS42L42_ASP_LCPOL_INV 3
-
-#define CS42L42_ASP_FRM_CFG (CS42L42_PAGE_12 + 0x08)
-#define CS42L42_ASP_STP_SHIFT 4
-#define CS42L42_ASP_STP_MASK (1 << CS42L42_ASP_STP_SHIFT)
-#define CS42L42_ASP_5050_SHIFT 3
-#define CS42L42_ASP_5050_MASK (1 << CS42L42_ASP_5050_SHIFT)
-#define CS42L42_ASP_FSD_SHIFT 0
-#define CS42L42_ASP_FSD_MASK (7 << CS42L42_ASP_FSD_SHIFT)
-#define CS42L42_ASP_FSD_0_5 1
-#define CS42L42_ASP_FSD_1_0 2
-#define CS42L42_ASP_FSD_1_5 3
-#define CS42L42_ASP_FSD_2_0 4
-
-#define CS42L42_FS_RATE_EN (CS42L42_PAGE_12 + 0x09)
-#define CS42L42_FS_EN_SHIFT 0
-#define CS42L42_FS_EN_MASK (0xf << CS42L42_FS_EN_SHIFT)
-#define CS42L42_FS_EN_IASRC_96K 0x1
-#define CS42L42_FS_EN_OASRC_96K 0x2
-
-#define CS42L42_IN_ASRC_CLK (CS42L42_PAGE_12 + 0x0A)
-#define CS42L42_CLK_IASRC_SEL_SHIFT 0
-#define CS42L42_CLK_IASRC_SEL_MASK (1 << CS42L42_CLK_IASRC_SEL_SHIFT)
-#define CS42L42_CLK_IASRC_SEL_6 0
-#define CS42L42_CLK_IASRC_SEL_12 1
-
-#define CS42L42_OUT_ASRC_CLK (CS42L42_PAGE_12 + 0x0B)
-#define CS42L42_CLK_OASRC_SEL_SHIFT 0
-#define CS42L42_CLK_OASRC_SEL_MASK (1 << CS42L42_CLK_OASRC_SEL_SHIFT)
-#define CS42L42_CLK_OASRC_SEL_12 1
-
-#define CS42L42_PLL_DIV_CFG1 (CS42L42_PAGE_12 + 0x0C)
-#define CS42L42_SCLK_PREDIV_SHIFT 0
-#define CS42L42_SCLK_PREDIV_MASK (3 << CS42L42_SCLK_PREDIV_SHIFT)
-
-/* Page 0x13 Interrupt Registers */
-/* Interrupts */
-#define CS42L42_ADC_OVFL_STATUS (CS42L42_PAGE_13 + 0x01)
-#define CS42L42_MIXER_STATUS (CS42L42_PAGE_13 + 0x02)
-#define CS42L42_SRC_STATUS (CS42L42_PAGE_13 + 0x03)
-#define CS42L42_ASP_RX_STATUS (CS42L42_PAGE_13 + 0x04)
-#define CS42L42_ASP_TX_STATUS (CS42L42_PAGE_13 + 0x05)
-#define CS42L42_CODEC_STATUS (CS42L42_PAGE_13 + 0x08)
-#define CS42L42_DET_INT_STATUS1 (CS42L42_PAGE_13 + 0x09)
-#define CS42L42_DET_INT_STATUS2 (CS42L42_PAGE_13 + 0x0A)
-#define CS42L42_SRCPL_INT_STATUS (CS42L42_PAGE_13 + 0x0B)
-#define CS42L42_VPMON_STATUS (CS42L42_PAGE_13 + 0x0D)
-#define CS42L42_PLL_LOCK_STATUS (CS42L42_PAGE_13 + 0x0E)
-#define CS42L42_TSRS_PLUG_STATUS (CS42L42_PAGE_13 + 0x0F)
-/* Masks */
-#define CS42L42_ADC_OVFL_INT_MASK (CS42L42_PAGE_13 + 0x16)
-#define CS42L42_ADC_OVFL_SHIFT 0
-#define CS42L42_ADC_OVFL_MASK (1 << CS42L42_ADC_OVFL_SHIFT)
-#define CS42L42_ADC_OVFL_VAL_MASK CS42L42_ADC_OVFL_MASK
-
-#define CS42L42_MIXER_INT_MASK (CS42L42_PAGE_13 + 0x17)
-#define CS42L42_MIX_CHB_OVFL_SHIFT 0
-#define CS42L42_MIX_CHB_OVFL_MASK (1 << CS42L42_MIX_CHB_OVFL_SHIFT)
-#define CS42L42_MIX_CHA_OVFL_SHIFT 1
-#define CS42L42_MIX_CHA_OVFL_MASK (1 << CS42L42_MIX_CHA_OVFL_SHIFT)
-#define CS42L42_EQ_OVFL_SHIFT 2
-#define CS42L42_EQ_OVFL_MASK (1 << CS42L42_EQ_OVFL_SHIFT)
-#define CS42L42_EQ_BIQUAD_OVFL_SHIFT 3
-#define CS42L42_EQ_BIQUAD_OVFL_MASK (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT)
-#define CS42L42_MIXER_VAL_MASK (CS42L42_MIX_CHB_OVFL_MASK | \
- CS42L42_MIX_CHA_OVFL_MASK | \
- CS42L42_EQ_OVFL_MASK | \
- CS42L42_EQ_BIQUAD_OVFL_MASK)
-
-#define CS42L42_SRC_INT_MASK (CS42L42_PAGE_13 + 0x18)
-#define CS42L42_SRC_ILK_SHIFT 0
-#define CS42L42_SRC_ILK_MASK (1 << CS42L42_SRC_ILK_SHIFT)
-#define CS42L42_SRC_OLK_SHIFT 1
-#define CS42L42_SRC_OLK_MASK (1 << CS42L42_SRC_OLK_SHIFT)
-#define CS42L42_SRC_IUNLK_SHIFT 2
-#define CS42L42_SRC_IUNLK_MASK (1 << CS42L42_SRC_IUNLK_SHIFT)
-#define CS42L42_SRC_OUNLK_SHIFT 3
-#define CS42L42_SRC_OUNLK_MASK (1 << CS42L42_SRC_OUNLK_SHIFT)
-#define CS42L42_SRC_VAL_MASK (CS42L42_SRC_ILK_MASK | \
- CS42L42_SRC_OLK_MASK | \
- CS42L42_SRC_IUNLK_MASK | \
- CS42L42_SRC_OUNLK_MASK)
-
-#define CS42L42_ASP_RX_INT_MASK (CS42L42_PAGE_13 + 0x19)
-#define CS42L42_ASPRX_NOLRCK_SHIFT 0
-#define CS42L42_ASPRX_NOLRCK_MASK (1 << CS42L42_ASPRX_NOLRCK_SHIFT)
-#define CS42L42_ASPRX_EARLY_SHIFT 1
-#define CS42L42_ASPRX_EARLY_MASK (1 << CS42L42_ASPRX_EARLY_SHIFT)
-#define CS42L42_ASPRX_LATE_SHIFT 2
-#define CS42L42_ASPRX_LATE_MASK (1 << CS42L42_ASPRX_LATE_SHIFT)
-#define CS42L42_ASPRX_ERROR_SHIFT 3
-#define CS42L42_ASPRX_ERROR_MASK (1 << CS42L42_ASPRX_ERROR_SHIFT)
-#define CS42L42_ASPRX_OVLD_SHIFT 4
-#define CS42L42_ASPRX_OVLD_MASK (1 << CS42L42_ASPRX_OVLD_SHIFT)
-#define CS42L42_ASP_RX_VAL_MASK (CS42L42_ASPRX_NOLRCK_MASK | \
- CS42L42_ASPRX_EARLY_MASK | \
- CS42L42_ASPRX_LATE_MASK | \
- CS42L42_ASPRX_ERROR_MASK | \
- CS42L42_ASPRX_OVLD_MASK)
-
-#define CS42L42_ASP_TX_INT_MASK (CS42L42_PAGE_13 + 0x1A)
-#define CS42L42_ASPTX_NOLRCK_SHIFT 0
-#define CS42L42_ASPTX_NOLRCK_MASK (1 << CS42L42_ASPTX_NOLRCK_SHIFT)
-#define CS42L42_ASPTX_EARLY_SHIFT 1
-#define CS42L42_ASPTX_EARLY_MASK (1 << CS42L42_ASPTX_EARLY_SHIFT)
-#define CS42L42_ASPTX_LATE_SHIFT 2
-#define CS42L42_ASPTX_LATE_MASK (1 << CS42L42_ASPTX_LATE_SHIFT)
-#define CS42L42_ASPTX_SMERROR_SHIFT 3
-#define CS42L42_ASPTX_SMERROR_MASK (1 << CS42L42_ASPTX_SMERROR_SHIFT)
-#define CS42L42_ASP_TX_VAL_MASK (CS42L42_ASPTX_NOLRCK_MASK | \
- CS42L42_ASPTX_EARLY_MASK | \
- CS42L42_ASPTX_LATE_MASK | \
- CS42L42_ASPTX_SMERROR_MASK)
-
-#define CS42L42_CODEC_INT_MASK (CS42L42_PAGE_13 + 0x1B)
-#define CS42L42_PDN_DONE_SHIFT 0
-#define CS42L42_PDN_DONE_MASK (1 << CS42L42_PDN_DONE_SHIFT)
-#define CS42L42_HSDET_AUTO_DONE_SHIFT 1
-#define CS42L42_HSDET_AUTO_DONE_MASK (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)
-#define CS42L42_CODEC_VAL_MASK (CS42L42_PDN_DONE_MASK | \
- CS42L42_HSDET_AUTO_DONE_MASK)
-
-#define CS42L42_SRCPL_INT_MASK (CS42L42_PAGE_13 + 0x1C)
-#define CS42L42_SRCPL_ADC_LK_SHIFT 0
-#define CS42L42_SRCPL_ADC_LK_MASK (1 << CS42L42_SRCPL_ADC_LK_SHIFT)
-#define CS42L42_SRCPL_DAC_LK_SHIFT 2
-#define CS42L42_SRCPL_DAC_LK_MASK (1 << CS42L42_SRCPL_DAC_LK_SHIFT)
-#define CS42L42_SRCPL_ADC_UNLK_SHIFT 5
-#define CS42L42_SRCPL_ADC_UNLK_MASK (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT)
-#define CS42L42_SRCPL_DAC_UNLK_SHIFT 6
-#define CS42L42_SRCPL_DAC_UNLK_MASK (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT)
-#define CS42L42_SRCPL_VAL_MASK (CS42L42_SRCPL_ADC_LK_MASK | \
- CS42L42_SRCPL_DAC_LK_MASK | \
- CS42L42_SRCPL_ADC_UNLK_MASK | \
- CS42L42_SRCPL_DAC_UNLK_MASK)
-
-#define CS42L42_VPMON_INT_MASK (CS42L42_PAGE_13 + 0x1E)
-#define CS42L42_VPMON_SHIFT 0
-#define CS42L42_VPMON_MASK (1 << CS42L42_VPMON_SHIFT)
-#define CS42L42_VPMON_VAL_MASK CS42L42_VPMON_MASK
-
-#define CS42L42_PLL_LOCK_INT_MASK (CS42L42_PAGE_13 + 0x1F)
-#define CS42L42_PLL_LOCK_SHIFT 0
-#define CS42L42_PLL_LOCK_MASK (1 << CS42L42_PLL_LOCK_SHIFT)
-#define CS42L42_PLL_LOCK_VAL_MASK CS42L42_PLL_LOCK_MASK
-
-#define CS42L42_TSRS_PLUG_INT_MASK (CS42L42_PAGE_13 + 0x20)
-#define CS42L42_RS_PLUG_SHIFT 0
-#define CS42L42_RS_PLUG_MASK (1 << CS42L42_RS_PLUG_SHIFT)
-#define CS42L42_RS_UNPLUG_SHIFT 1
-#define CS42L42_RS_UNPLUG_MASK (1 << CS42L42_RS_UNPLUG_SHIFT)
-#define CS42L42_TS_PLUG_SHIFT 2
-#define CS42L42_TS_PLUG_MASK (1 << CS42L42_TS_PLUG_SHIFT)
-#define CS42L42_TS_UNPLUG_SHIFT 3
-#define CS42L42_TS_UNPLUG_MASK (1 << CS42L42_TS_UNPLUG_SHIFT)
-#define CS42L42_TSRS_PLUG_VAL_MASK (CS42L42_RS_PLUG_MASK | \
- CS42L42_RS_UNPLUG_MASK | \
- CS42L42_TS_PLUG_MASK | \
- CS42L42_TS_UNPLUG_MASK)
-#define CS42L42_TS_PLUG 3
-#define CS42L42_TS_UNPLUG 0
-#define CS42L42_TS_TRANS 1
-
-/*
- * NOTE: PLL_START must be 0 while both ADC_PDN=1 and HP_PDN=1.
- * Otherwise it will prevent FILT+ from charging properly.
- */
-#define CS42L42_PLL_CTL1 (CS42L42_PAGE_15 + 0x01)
-#define CS42L42_PLL_START_SHIFT 0
-#define CS42L42_PLL_START_MASK (1 << CS42L42_PLL_START_SHIFT)
-
-#define CS42L42_PLL_DIV_FRAC0 (CS42L42_PAGE_15 + 0x02)
-#define CS42L42_PLL_DIV_FRAC_SHIFT 0
-#define CS42L42_PLL_DIV_FRAC_MASK (0xff << CS42L42_PLL_DIV_FRAC_SHIFT)
-
-#define CS42L42_PLL_DIV_FRAC1 (CS42L42_PAGE_15 + 0x03)
-#define CS42L42_PLL_DIV_FRAC2 (CS42L42_PAGE_15 + 0x04)
-
-#define CS42L42_PLL_DIV_INT (CS42L42_PAGE_15 + 0x05)
-#define CS42L42_PLL_DIV_INT_SHIFT 0
-#define CS42L42_PLL_DIV_INT_MASK (0xff << CS42L42_PLL_DIV_INT_SHIFT)
-
-#define CS42L42_PLL_CTL3 (CS42L42_PAGE_15 + 0x08)
-#define CS42L42_PLL_DIVOUT_SHIFT 0
-#define CS42L42_PLL_DIVOUT_MASK (0xff << CS42L42_PLL_DIVOUT_SHIFT)
-
-#define CS42L42_PLL_CAL_RATIO (CS42L42_PAGE_15 + 0x0A)
-#define CS42L42_PLL_CAL_RATIO_SHIFT 0
-#define CS42L42_PLL_CAL_RATIO_MASK (0xff << CS42L42_PLL_CAL_RATIO_SHIFT)
-
-#define CS42L42_PLL_CTL4 (CS42L42_PAGE_15 + 0x1B)
-#define CS42L42_PLL_MODE_SHIFT 0
-#define CS42L42_PLL_MODE_MASK (3 << CS42L42_PLL_MODE_SHIFT)
-
-/* Page 0x19 HP Load Detect Registers */
-#define CS42L42_LOAD_DET_RCSTAT (CS42L42_PAGE_19 + 0x25)
-#define CS42L42_RLA_STAT_SHIFT 0
-#define CS42L42_RLA_STAT_MASK (3 << CS42L42_RLA_STAT_SHIFT)
-#define CS42L42_RLA_STAT_15_OHM 0
-
-#define CS42L42_LOAD_DET_DONE (CS42L42_PAGE_19 + 0x26)
-#define CS42L42_HPLOAD_DET_DONE_SHIFT 0
-#define CS42L42_HPLOAD_DET_DONE_MASK (1 << CS42L42_HPLOAD_DET_DONE_SHIFT)
-
-#define CS42L42_LOAD_DET_EN (CS42L42_PAGE_19 + 0x27)
-#define CS42L42_HP_LD_EN_SHIFT 0
-#define CS42L42_HP_LD_EN_MASK (1 << CS42L42_HP_LD_EN_SHIFT)
-
-/* Page 0x1B Headset Interface Registers */
-#define CS42L42_HSBIAS_SC_AUTOCTL (CS42L42_PAGE_1B + 0x70)
-#define CS42L42_HSBIAS_SENSE_TRIP_SHIFT 0
-#define CS42L42_HSBIAS_SENSE_TRIP_MASK (7 << \
- CS42L42_HSBIAS_SENSE_TRIP_SHIFT)
-#define CS42L42_TIP_SENSE_EN_SHIFT 5
-#define CS42L42_TIP_SENSE_EN_MASK (1 << \
- CS42L42_TIP_SENSE_EN_SHIFT)
-#define CS42L42_AUTO_HSBIAS_HIZ_SHIFT 6
-#define CS42L42_AUTO_HSBIAS_HIZ_MASK (1 << \
- CS42L42_AUTO_HSBIAS_HIZ_SHIFT)
-#define CS42L42_HSBIAS_SENSE_EN_SHIFT 7
-#define CS42L42_HSBIAS_SENSE_EN_MASK (1 << \
- CS42L42_HSBIAS_SENSE_EN_SHIFT)
-
-#define CS42L42_WAKE_CTL (CS42L42_PAGE_1B + 0x71)
-#define CS42L42_WAKEB_CLEAR_SHIFT 0
-#define CS42L42_WAKEB_CLEAR_MASK (1 << CS42L42_WAKEB_CLEAR_SHIFT)
-#define CS42L42_WAKEB_MODE_SHIFT 5
-#define CS42L42_WAKEB_MODE_MASK (1 << CS42L42_WAKEB_MODE_SHIFT)
-#define CS42L42_M_HP_WAKE_SHIFT 6
-#define CS42L42_M_HP_WAKE_MASK (1 << CS42L42_M_HP_WAKE_SHIFT)
-#define CS42L42_M_MIC_WAKE_SHIFT 7
-#define CS42L42_M_MIC_WAKE_MASK (1 << CS42L42_M_MIC_WAKE_SHIFT)
-
-#define CS42L42_ADC_DISABLE_MUTE (CS42L42_PAGE_1B + 0x72)
-#define CS42L42_ADC_DISABLE_S0_MUTE_SHIFT 7
-#define CS42L42_ADC_DISABLE_S0_MUTE_MASK (1 << \
- CS42L42_ADC_DISABLE_S0_MUTE_SHIFT)
-
-#define CS42L42_TIPSENSE_CTL (CS42L42_PAGE_1B + 0x73)
-#define CS42L42_TIP_SENSE_DEBOUNCE_SHIFT 0
-#define CS42L42_TIP_SENSE_DEBOUNCE_MASK (3 << \
- CS42L42_TIP_SENSE_DEBOUNCE_SHIFT)
-#define CS42L42_TIP_SENSE_INV_SHIFT 5
-#define CS42L42_TIP_SENSE_INV_MASK (1 << \
- CS42L42_TIP_SENSE_INV_SHIFT)
-#define CS42L42_TIP_SENSE_CTRL_SHIFT 6
-#define CS42L42_TIP_SENSE_CTRL_MASK (3 << \
- CS42L42_TIP_SENSE_CTRL_SHIFT)
-
-/*
- * NOTE: DETECT_MODE must be 0 while both ADC_PDN=1 and HP_PDN=1.
- * Otherwise it will prevent FILT+ from charging properly.
- */
-#define CS42L42_MISC_DET_CTL (CS42L42_PAGE_1B + 0x74)
-#define CS42L42_PDN_MIC_LVL_DET_SHIFT 0
-#define CS42L42_PDN_MIC_LVL_DET_MASK (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)
-#define CS42L42_HSBIAS_CTL_SHIFT 1
-#define CS42L42_HSBIAS_CTL_MASK (3 << CS42L42_HSBIAS_CTL_SHIFT)
-#define CS42L42_DETECT_MODE_SHIFT 3
-#define CS42L42_DETECT_MODE_MASK (3 << CS42L42_DETECT_MODE_SHIFT)
-
-#define CS42L42_MIC_DET_CTL1 (CS42L42_PAGE_1B + 0x75)
-#define CS42L42_HS_DET_LEVEL_SHIFT 0
-#define CS42L42_HS_DET_LEVEL_MASK (0x3F << CS42L42_HS_DET_LEVEL_SHIFT)
-#define CS42L42_EVENT_STAT_SEL_SHIFT 6
-#define CS42L42_EVENT_STAT_SEL_MASK (1 << CS42L42_EVENT_STAT_SEL_SHIFT)
-#define CS42L42_LATCH_TO_VP_SHIFT 7
-#define CS42L42_LATCH_TO_VP_MASK (1 << CS42L42_LATCH_TO_VP_SHIFT)
-
-#define CS42L42_MIC_DET_CTL2 (CS42L42_PAGE_1B + 0x76)
-#define CS42L42_DEBOUNCE_TIME_SHIFT 5
-#define CS42L42_DEBOUNCE_TIME_MASK (0x07 << CS42L42_DEBOUNCE_TIME_SHIFT)
-
-#define CS42L42_DET_STATUS1 (CS42L42_PAGE_1B + 0x77)
-#define CS42L42_HSBIAS_HIZ_MODE_SHIFT 6
-#define CS42L42_HSBIAS_HIZ_MODE_MASK (1 << CS42L42_HSBIAS_HIZ_MODE_SHIFT)
-#define CS42L42_TIP_SENSE_SHIFT 7
-#define CS42L42_TIP_SENSE_MASK (1 << CS42L42_TIP_SENSE_SHIFT)
-
-#define CS42L42_DET_STATUS2 (CS42L42_PAGE_1B + 0x78)
-#define CS42L42_SHORT_TRUE_SHIFT 0
-#define CS42L42_SHORT_TRUE_MASK (1 << CS42L42_SHORT_TRUE_SHIFT)
-#define CS42L42_HS_TRUE_SHIFT 1
-#define CS42L42_HS_TRUE_MASK (1 << CS42L42_HS_TRUE_SHIFT)
-
-#define CS42L42_DET_INT1_MASK (CS42L42_PAGE_1B + 0x79)
-#define CS42L42_TIP_SENSE_UNPLUG_SHIFT 5
-#define CS42L42_TIP_SENSE_UNPLUG_MASK (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT)
-#define CS42L42_TIP_SENSE_PLUG_SHIFT 6
-#define CS42L42_TIP_SENSE_PLUG_MASK (1 << CS42L42_TIP_SENSE_PLUG_SHIFT)
-#define CS42L42_HSBIAS_SENSE_SHIFT 7
-#define CS42L42_HSBIAS_SENSE_MASK (1 << CS42L42_HSBIAS_SENSE_SHIFT)
-#define CS42L42_DET_INT_VAL1_MASK (CS42L42_TIP_SENSE_UNPLUG_MASK | \
- CS42L42_TIP_SENSE_PLUG_MASK | \
- CS42L42_HSBIAS_SENSE_MASK)
-
-#define CS42L42_DET_INT2_MASK (CS42L42_PAGE_1B + 0x7A)
-#define CS42L42_M_SHORT_DET_SHIFT 0
-#define CS42L42_M_SHORT_DET_MASK (1 << \
- CS42L42_M_SHORT_DET_SHIFT)
-#define CS42L42_M_SHORT_RLS_SHIFT 1
-#define CS42L42_M_SHORT_RLS_MASK (1 << \
- CS42L42_M_SHORT_RLS_SHIFT)
-#define CS42L42_M_HSBIAS_HIZ_SHIFT 2
-#define CS42L42_M_HSBIAS_HIZ_MASK (1 << \
- CS42L42_M_HSBIAS_HIZ_SHIFT)
-#define CS42L42_M_DETECT_FT_SHIFT 6
-#define CS42L42_M_DETECT_FT_MASK (1 << \
- CS42L42_M_DETECT_FT_SHIFT)
-#define CS42L42_M_DETECT_TF_SHIFT 7
-#define CS42L42_M_DETECT_TF_MASK (1 << \
- CS42L42_M_DETECT_TF_SHIFT)
-#define CS42L42_DET_INT_VAL2_MASK (CS42L42_M_SHORT_DET_MASK | \
- CS42L42_M_SHORT_RLS_MASK | \
- CS42L42_M_HSBIAS_HIZ_MASK | \
- CS42L42_M_DETECT_FT_MASK | \
- CS42L42_M_DETECT_TF_MASK)
-
-/* Page 0x1C Headset Bias Registers */
-#define CS42L42_HS_BIAS_CTL (CS42L42_PAGE_1C + 0x03)
-#define CS42L42_HSBIAS_RAMP_SHIFT 0
-#define CS42L42_HSBIAS_RAMP_MASK (3 << CS42L42_HSBIAS_RAMP_SHIFT)
-#define CS42L42_HSBIAS_PD_SHIFT 4
-#define CS42L42_HSBIAS_PD_MASK (1 << CS42L42_HSBIAS_PD_SHIFT)
-#define CS42L42_HSBIAS_CAPLESS_SHIFT 7
-#define CS42L42_HSBIAS_CAPLESS_MASK (1 << CS42L42_HSBIAS_CAPLESS_SHIFT)
-
-/* Page 0x1D ADC Registers */
-#define CS42L42_ADC_CTL (CS42L42_PAGE_1D + 0x01)
-#define CS42L42_ADC_NOTCH_DIS_SHIFT 5
-#define CS42L42_ADC_FORCE_WEAK_VCM_SHIFT 4
-#define CS42L42_ADC_INV_SHIFT 2
-#define CS42L42_ADC_DIG_BOOST_SHIFT 0
-
-#define CS42L42_ADC_VOLUME (CS42L42_PAGE_1D + 0x03)
-#define CS42L42_ADC_VOL_SHIFT 0
-
-#define CS42L42_ADC_WNF_HPF_CTL (CS42L42_PAGE_1D + 0x04)
-#define CS42L42_ADC_WNF_CF_SHIFT 4
-#define CS42L42_ADC_WNF_EN_SHIFT 3
-#define CS42L42_ADC_HPF_CF_SHIFT 1
-#define CS42L42_ADC_HPF_EN_SHIFT 0
-
-/* Page 0x1F DAC Registers */
-#define CS42L42_DAC_CTL1 (CS42L42_PAGE_1F + 0x01)
-#define CS42L42_DACB_INV_SHIFT 1
-#define CS42L42_DACA_INV_SHIFT 0
-
-#define CS42L42_DAC_CTL2 (CS42L42_PAGE_1F + 0x06)
-#define CS42L42_HPOUT_PULLDOWN_SHIFT 4
-#define CS42L42_HPOUT_PULLDOWN_MASK (15 << CS42L42_HPOUT_PULLDOWN_SHIFT)
-#define CS42L42_HPOUT_LOAD_SHIFT 3
-#define CS42L42_HPOUT_LOAD_MASK (1 << CS42L42_HPOUT_LOAD_SHIFT)
-#define CS42L42_HPOUT_CLAMP_SHIFT 2
-#define CS42L42_HPOUT_CLAMP_MASK (1 << CS42L42_HPOUT_CLAMP_SHIFT)
-#define CS42L42_DAC_HPF_EN_SHIFT 1
-#define CS42L42_DAC_HPF_EN_MASK (1 << CS42L42_DAC_HPF_EN_SHIFT)
-#define CS42L42_DAC_MON_EN_SHIFT 0
-#define CS42L42_DAC_MON_EN_MASK (1 << CS42L42_DAC_MON_EN_SHIFT)
-
-/* Page 0x20 HP CTL Registers */
-#define CS42L42_HP_CTL (CS42L42_PAGE_20 + 0x01)
-#define CS42L42_HP_ANA_BMUTE_SHIFT 3
-#define CS42L42_HP_ANA_BMUTE_MASK (1 << CS42L42_HP_ANA_BMUTE_SHIFT)
-#define CS42L42_HP_ANA_AMUTE_SHIFT 2
-#define CS42L42_HP_ANA_AMUTE_MASK (1 << CS42L42_HP_ANA_AMUTE_SHIFT)
-#define CS42L42_HP_FULL_SCALE_VOL_SHIFT 1
-#define CS42L42_HP_FULL_SCALE_VOL_MASK (1 << CS42L42_HP_FULL_SCALE_VOL_SHIFT)
-
-/* Page 0x21 Class H Registers */
-#define CS42L42_CLASSH_CTL (CS42L42_PAGE_21 + 0x01)
-
-/* Page 0x23 Mixer Volume Registers */
-#define CS42L42_MIXER_CHA_VOL (CS42L42_PAGE_23 + 0x01)
-#define CS42L42_MIXER_ADC_VOL (CS42L42_PAGE_23 + 0x02)
-
-#define CS42L42_MIXER_CHB_VOL (CS42L42_PAGE_23 + 0x03)
-#define CS42L42_MIXER_CH_VOL_SHIFT 0
-#define CS42L42_MIXER_CH_VOL_MASK (0x3f << CS42L42_MIXER_CH_VOL_SHIFT)
-
-/* Page 0x24 EQ Registers */
-#define CS42L42_EQ_COEF_IN0 (CS42L42_PAGE_24 + 0x01)
-#define CS42L42_EQ_COEF_IN1 (CS42L42_PAGE_24 + 0x02)
-#define CS42L42_EQ_COEF_IN2 (CS42L42_PAGE_24 + 0x03)
-#define CS42L42_EQ_COEF_IN3 (CS42L42_PAGE_24 + 0x04)
-#define CS42L42_EQ_COEF_RW (CS42L42_PAGE_24 + 0x06)
-#define CS42L42_EQ_COEF_OUT0 (CS42L42_PAGE_24 + 0x07)
-#define CS42L42_EQ_COEF_OUT1 (CS42L42_PAGE_24 + 0x08)
-#define CS42L42_EQ_COEF_OUT2 (CS42L42_PAGE_24 + 0x09)
-#define CS42L42_EQ_COEF_OUT3 (CS42L42_PAGE_24 + 0x0A)
-#define CS42L42_EQ_INIT_STAT (CS42L42_PAGE_24 + 0x0B)
-#define CS42L42_EQ_START_FILT (CS42L42_PAGE_24 + 0x0C)
-#define CS42L42_EQ_MUTE_CTL (CS42L42_PAGE_24 + 0x0E)
-
-/* Page 0x25 Audio Port Registers */
-#define CS42L42_SP_RX_CH_SEL (CS42L42_PAGE_25 + 0x01)
-#define CS42L42_SP_RX_CHB_SEL_SHIFT 2
-#define CS42L42_SP_RX_CHB_SEL_MASK (3 << CS42L42_SP_RX_CHB_SEL_SHIFT)
-
-#define CS42L42_SP_RX_ISOC_CTL (CS42L42_PAGE_25 + 0x02)
-#define CS42L42_SP_RX_RSYNC_SHIFT 6
-#define CS42L42_SP_RX_RSYNC_MASK (1 << CS42L42_SP_RX_RSYNC_SHIFT)
-#define CS42L42_SP_RX_NSB_POS_SHIFT 3
-#define CS42L42_SP_RX_NSB_POS_MASK (7 << CS42L42_SP_RX_NSB_POS_SHIFT)
-#define CS42L42_SP_RX_NFS_NSBB_SHIFT 2
-#define CS42L42_SP_RX_NFS_NSBB_MASK (1 << CS42L42_SP_RX_NFS_NSBB_SHIFT)
-#define CS42L42_SP_RX_ISOC_MODE_SHIFT 0
-#define CS42L42_SP_RX_ISOC_MODE_MASK (3 << CS42L42_SP_RX_ISOC_MODE_SHIFT)
-
-#define CS42L42_SP_RX_FS (CS42L42_PAGE_25 + 0x03)
-#define CS42l42_SPDIF_CH_SEL (CS42L42_PAGE_25 + 0x04)
-#define CS42L42_SP_TX_ISOC_CTL (CS42L42_PAGE_25 + 0x05)
-#define CS42L42_SP_TX_FS (CS42L42_PAGE_25 + 0x06)
-#define CS42L42_SPDIF_SW_CTL1 (CS42L42_PAGE_25 + 0x07)
-
-/* Page 0x26 SRC Registers */
-#define CS42L42_SRC_SDIN_FS (CS42L42_PAGE_26 + 0x01)
-#define CS42L42_SRC_SDIN_FS_SHIFT 0
-#define CS42L42_SRC_SDIN_FS_MASK (0x1f << CS42L42_SRC_SDIN_FS_SHIFT)
-
-#define CS42L42_SRC_SDOUT_FS (CS42L42_PAGE_26 + 0x09)
-
-/* Page 0x28 S/PDIF Registers */
-#define CS42L42_SPDIF_CTL1 (CS42L42_PAGE_28 + 0x01)
-#define CS42L42_SPDIF_CTL2 (CS42L42_PAGE_28 + 0x02)
-#define CS42L42_SPDIF_CTL3 (CS42L42_PAGE_28 + 0x03)
-#define CS42L42_SPDIF_CTL4 (CS42L42_PAGE_28 + 0x04)
-
-/* Page 0x29 Serial Port TX Registers */
-#define CS42L42_ASP_TX_SZ_EN (CS42L42_PAGE_29 + 0x01)
-#define CS42L42_ASP_TX_EN_SHIFT 0
-#define CS42L42_ASP_TX_CH_EN (CS42L42_PAGE_29 + 0x02)
-#define CS42L42_ASP_TX0_CH2_SHIFT 1
-#define CS42L42_ASP_TX0_CH1_SHIFT 0
-
-#define CS42L42_ASP_TX_CH_AP_RES (CS42L42_PAGE_29 + 0x03)
-#define CS42L42_ASP_TX_CH1_AP_SHIFT 7
-#define CS42L42_ASP_TX_CH1_AP_MASK (1 << CS42L42_ASP_TX_CH1_AP_SHIFT)
-#define CS42L42_ASP_TX_CH2_AP_SHIFT 6
-#define CS42L42_ASP_TX_CH2_AP_MASK (1 << CS42L42_ASP_TX_CH2_AP_SHIFT)
-#define CS42L42_ASP_TX_CH2_RES_SHIFT 2
-#define CS42L42_ASP_TX_CH2_RES_MASK (3 << CS42L42_ASP_TX_CH2_RES_SHIFT)
-#define CS42L42_ASP_TX_CH1_RES_SHIFT 0
-#define CS42L42_ASP_TX_CH1_RES_MASK (3 << CS42L42_ASP_TX_CH1_RES_SHIFT)
-#define CS42L42_ASP_TX_CH1_BIT_MSB (CS42L42_PAGE_29 + 0x04)
-#define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05)
-#define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06)
-#define CS42L42_ASP_TX_CH2_BIT_MSB (CS42L42_PAGE_29 + 0x0A)
-#define CS42L42_ASP_TX_CH2_BIT_LSB (CS42L42_PAGE_29 + 0x0B)
-
-/* Page 0x2A Serial Port RX Registers */
-#define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01)
-#define CS42L42_ASP_RX0_CH_EN_SHIFT 2
-#define CS42L42_ASP_RX0_CH_EN_MASK (0xf << CS42L42_ASP_RX0_CH_EN_SHIFT)
-#define CS42L42_ASP_RX0_CH1_SHIFT 2
-#define CS42L42_ASP_RX0_CH2_SHIFT 3
-#define CS42L42_ASP_RX0_CH3_SHIFT 4
-#define CS42L42_ASP_RX0_CH4_SHIFT 5
-
-#define CS42L42_ASP_RX_DAI0_CH1_AP_RES (CS42L42_PAGE_2A + 0x02)
-#define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03)
-#define CS42L42_ASP_RX_DAI0_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x04)
-#define CS42L42_ASP_RX_DAI0_CH2_AP_RES (CS42L42_PAGE_2A + 0x05)
-#define CS42L42_ASP_RX_DAI0_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x06)
-#define CS42L42_ASP_RX_DAI0_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x07)
-#define CS42L42_ASP_RX_DAI0_CH3_AP_RES (CS42L42_PAGE_2A + 0x08)
-#define CS42L42_ASP_RX_DAI0_CH3_BIT_MSB (CS42L42_PAGE_2A + 0x09)
-#define CS42L42_ASP_RX_DAI0_CH3_BIT_LSB (CS42L42_PAGE_2A + 0x0A)
-#define CS42L42_ASP_RX_DAI0_CH4_AP_RES (CS42L42_PAGE_2A + 0x0B)
-#define CS42L42_ASP_RX_DAI0_CH4_BIT_MSB (CS42L42_PAGE_2A + 0x0C)
-#define CS42L42_ASP_RX_DAI0_CH4_BIT_LSB (CS42L42_PAGE_2A + 0x0D)
-#define CS42L42_ASP_RX_DAI1_CH1_AP_RES (CS42L42_PAGE_2A + 0x0E)
-#define CS42L42_ASP_RX_DAI1_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x0F)
-#define CS42L42_ASP_RX_DAI1_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x10)
-#define CS42L42_ASP_RX_DAI1_CH2_AP_RES (CS42L42_PAGE_2A + 0x11)
-#define CS42L42_ASP_RX_DAI1_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x12)
-#define CS42L42_ASP_RX_DAI1_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x13)
-
-#define CS42L42_ASP_RX_CH_AP_SHIFT 6
-#define CS42L42_ASP_RX_CH_AP_MASK (1 << CS42L42_ASP_RX_CH_AP_SHIFT)
-#define CS42L42_ASP_RX_CH_AP_LOW 0
-#define CS42L42_ASP_RX_CH_AP_HI 1
-#define CS42L42_ASP_RX_CH_RES_SHIFT 0
-#define CS42L42_ASP_RX_CH_RES_MASK (3 << CS42L42_ASP_RX_CH_RES_SHIFT)
-#define CS42L42_ASP_RX_CH_RES_32 3
-#define CS42L42_ASP_RX_CH_RES_16 1
-#define CS42L42_ASP_RX_CH_BIT_ST_SHIFT 0
-#define CS42L42_ASP_RX_CH_BIT_ST_MASK (0xff << CS42L42_ASP_RX_CH_BIT_ST_SHIFT)
-
-/* Page 0x30 ID Registers */
-#define CS42L42_SUB_REVID (CS42L42_PAGE_30 + 0x14)
-#define CS42L42_MAX_REGISTER (CS42L42_PAGE_30 + 0x14)
-
-/* Defines for fracturing values spread across multiple registers */
-#define CS42L42_FRAC0_VAL(val) ((val) & 0x0000ff)
-#define CS42L42_FRAC1_VAL(val) (((val) & 0x00ff00) >> 8)
-#define CS42L42_FRAC2_VAL(val) (((val) & 0xff0000) >> 16)
-
-#define CS42L42_NUM_SUPPLIES 5
-#define CS42L42_BOOT_TIME_US 3000
-#define CS42L42_PLL_DIVOUT_TIME_US 800
-#define CS42L42_CLOCK_SWITCH_DELAY_US 150
-#define CS42L42_PLL_LOCK_POLL_US 250
-#define CS42L42_PLL_LOCK_TIMEOUT_US 1250
-#define CS42L42_HP_ADC_EN_TIME_US 20000
-#define CS42L42_PDN_DONE_POLL_US 1000
-#define CS42L42_PDN_DONE_TIMEOUT_US 200000
-#define CS42L42_PDN_DONE_TIME_MS 100
-#define CS42L42_FILT_DISCHARGE_TIME_MS 46
+#include <sound/cs42l42.h>
static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
"VA",
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index 3cb21a2ba29f..3613fb12d623 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -19,8 +19,7 @@ static struct i2c_device_id cs42l51_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
-static int cs42l51_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cs42l51_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config config;
@@ -46,7 +45,7 @@ static struct i2c_driver cs42l51_i2c_driver = {
.of_match_table = cs42l51_of_match,
.pm = &cs42l51_pm_ops,
},
- .probe = cs42l51_i2c_probe,
+ .probe_new = 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 e9c3cb4e2bfc..aff618513c75 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -51,11 +51,8 @@ struct cs42l51_private {
struct regmap *regmap;
};
-#define CS42L51_FORMATS ( \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 80161151b3f2..9b182b585be4 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1086,8 +1086,7 @@ static const struct regmap_config cs42l52_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l52_private *cs42l52;
struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1226,7 +1225,7 @@ static struct i2c_driver cs42l52_i2c_driver = {
.of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
- .probe = cs42l52_i2c_probe,
+ .probe_new = cs42l52_i2c_probe,
};
module_i2c_driver(cs42l52_i2c_driver);
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index 3cf8a0b4478c..dc23007336c5 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1167,8 +1167,7 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l56_private *cs42l56;
struct cs42l56_platform_data *pdata =
@@ -1246,7 +1245,7 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
if (ret) {
dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
- return ret;
+ goto err_enable;
}
devid = reg & CS42L56_CHIP_ID_MASK;
@@ -1350,7 +1349,7 @@ static struct i2c_driver cs42l56_i2c_driver = {
.of_match_table = cs42l56_of_match,
},
.id_table = cs42l56_id,
- .probe = cs42l56_i2c_probe,
+ .probe_new = cs42l56_i2c_probe,
.remove = cs42l56_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 018463f34e12..5a9166289f36 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1274,8 +1274,7 @@ static const struct regmap_config cs42l73_regmap = {
.use_single_write = true,
};
-static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs42l73_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l73_private *cs42l73;
struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
@@ -1386,7 +1385,7 @@ static struct i2c_driver cs42l73_i2c_driver = {
.of_match_table = cs42l73_of_match,
},
.id_table = cs42l73_id,
- .probe = cs42l73_i2c_probe,
+ .probe_new = cs42l73_i2c_probe,
};
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
index 0214e3ab9da0..cb06a06d48b0 100644
--- a/sound/soc/codecs/cs42xx8-i2c.c
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -17,8 +17,7 @@
#include "cs42xx8.h"
-static int cs42xx8_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cs42xx8_i2c_probe(struct i2c_client *i2c)
{
int ret = cs42xx8_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
@@ -51,7 +50,7 @@ static struct i2c_driver cs42xx8_i2c_driver = {
.pm = &cs42xx8_pm,
.of_match_table = cs42xx8_of_match,
},
- .probe = cs42xx8_i2c_probe,
+ .probe_new = 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 44b20c1ef851..a2bce0f9f247 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -712,30 +712,30 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
case CS43130_ASP_PCM_DAI:
case CS43130_ASP_DOP_DAI:
regmap_write(cs43130->regmap, CS43130_ASP_DEN_1,
- (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+ (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
CS43130_SP_M_LSB_DATA_SHIFT);
regmap_write(cs43130->regmap, CS43130_ASP_DEN_2,
- (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >>
+ (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
CS43130_SP_M_MSB_DATA_SHIFT);
regmap_write(cs43130->regmap, CS43130_ASP_NUM_1,
- (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >>
+ (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
CS43130_SP_N_LSB_DATA_SHIFT);
regmap_write(cs43130->regmap, CS43130_ASP_NUM_2,
- (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >>
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
CS43130_SP_N_MSB_DATA_SHIFT);
break;
case CS43130_XSP_DOP_DAI:
regmap_write(cs43130->regmap, CS43130_XSP_DEN_1,
- (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+ (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >>
CS43130_SP_M_LSB_DATA_SHIFT);
regmap_write(cs43130->regmap, CS43130_XSP_DEN_2,
- (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >>
+ (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >>
CS43130_SP_M_MSB_DATA_SHIFT);
regmap_write(cs43130->regmap, CS43130_XSP_NUM_1,
- (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >>
+ (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >>
CS43130_SP_N_LSB_DATA_SHIFT);
regmap_write(cs43130->regmap, CS43130_XSP_NUM_2,
- (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >>
+ (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >>
CS43130_SP_N_MSB_DATA_SHIFT);
break;
default:
@@ -2303,7 +2303,7 @@ static int cs43130_probe(struct snd_soc_component *component)
}
ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK,
- &cs43130->jack, NULL, 0);
+ &cs43130->jack);
if (ret < 0) {
dev_err(component->dev, "Cannot create jack\n");
return ret;
@@ -2418,8 +2418,7 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs43130_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs43130_i2c_probe(struct i2c_client *client)
{
struct cs43130_private *cs43130;
int ret;
@@ -2702,7 +2701,7 @@ static struct i2c_driver cs43130_i2c_driver = {
.pm = &cs43130_runtime_pm,
},
.id_table = cs43130_i2c_id,
- .probe = cs43130_i2c_probe,
+ .probe_new = cs43130_i2c_probe,
.remove = cs43130_i2c_remove,
};
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
index e62d671e95bb..1dd893674313 100644
--- a/sound/soc/codecs/cs43130.h
+++ b/sound/soc/codecs/cs43130.h
@@ -10,6 +10,8 @@
#ifndef __CS43130_H__
#define __CS43130_H__
+#include <linux/math.h>
+
/* CS43130 registers addresses */
/* all reg address is shifted by a byte for control byte to be LSB */
#define CS43130_FIRSTREG 0x010000
@@ -372,97 +374,96 @@ enum cs43130_dai_id {
};
struct cs43130_clk_gen {
- unsigned int mclk_int;
- int fs;
- u16 den;
- u16 num;
+ unsigned int mclk_int;
+ int fs;
+ struct u16_fract v;
};
/* frm_size = 16 */
static const struct cs43130_clk_gen cs43130_16_clk_gen[] = {
- {22579200, 32000, 441, 10,},
- {22579200, 44100, 32, 1,},
- {22579200, 48000, 147, 5,},
- {22579200, 88200, 16, 1,},
- {22579200, 96000, 147, 10,},
- {22579200, 176400, 8, 1,},
- {22579200, 192000, 147, 20,},
- {22579200, 352800, 4, 1,},
- {22579200, 384000, 147, 40,},
- {24576000, 32000, 48, 1,},
- {24576000, 44100, 5120, 147,},
- {24576000, 48000, 32, 1,},
- {24576000, 88200, 2560, 147,},
- {24576000, 96000, 16, 1,},
- {24576000, 176400, 1280, 147,},
- {24576000, 192000, 8, 1,},
- {24576000, 352800, 640, 147,},
- {24576000, 384000, 4, 1,},
+ { 22579200, 32000, .v = { 441, 10, }, },
+ { 22579200, 44100, .v = { 32, 1, }, },
+ { 22579200, 48000, .v = { 147, 5, }, },
+ { 22579200, 88200, .v = { 16, 1, }, },
+ { 22579200, 96000, .v = { 147, 10, }, },
+ { 22579200, 176400, .v = { 8, 1, }, },
+ { 22579200, 192000, .v = { 147, 20, }, },
+ { 22579200, 352800, .v = { 4, 1, }, },
+ { 22579200, 384000, .v = { 147, 40, }, },
+ { 24576000, 32000, .v = { 48, 1, }, },
+ { 24576000, 44100, .v = { 5120, 147, }, },
+ { 24576000, 48000, .v = { 32, 1, }, },
+ { 24576000, 88200, .v = { 2560, 147, }, },
+ { 24576000, 96000, .v = { 16, 1, }, },
+ { 24576000, 176400, .v = { 1280, 147, }, },
+ { 24576000, 192000, .v = { 8, 1, }, },
+ { 24576000, 352800, .v = { 640, 147, }, },
+ { 24576000, 384000, .v = { 4, 1, }, },
};
/* frm_size = 32 */
static const struct cs43130_clk_gen cs43130_32_clk_gen[] = {
- {22579200, 32000, 441, 20,},
- {22579200, 44100, 16, 1,},
- {22579200, 48000, 147, 10,},
- {22579200, 88200, 8, 1,},
- {22579200, 96000, 147, 20,},
- {22579200, 176400, 4, 1,},
- {22579200, 192000, 147, 40,},
- {22579200, 352800, 2, 1,},
- {22579200, 384000, 147, 80,},
- {24576000, 32000, 24, 1,},
- {24576000, 44100, 2560, 147,},
- {24576000, 48000, 16, 1,},
- {24576000, 88200, 1280, 147,},
- {24576000, 96000, 8, 1,},
- {24576000, 176400, 640, 147,},
- {24576000, 192000, 4, 1,},
- {24576000, 352800, 320, 147,},
- {24576000, 384000, 2, 1,},
+ { 22579200, 32000, .v = { 441, 20, }, },
+ { 22579200, 44100, .v = { 16, 1, }, },
+ { 22579200, 48000, .v = { 147, 10, }, },
+ { 22579200, 88200, .v = { 8, 1, }, },
+ { 22579200, 96000, .v = { 147, 20, }, },
+ { 22579200, 176400, .v = { 4, 1, }, },
+ { 22579200, 192000, .v = { 147, 40, }, },
+ { 22579200, 352800, .v = { 2, 1, }, },
+ { 22579200, 384000, .v = { 147, 80, }, },
+ { 24576000, 32000, .v = { 24, 1, }, },
+ { 24576000, 44100, .v = { 2560, 147, }, },
+ { 24576000, 48000, .v = { 16, 1, }, },
+ { 24576000, 88200, .v = { 1280, 147, }, },
+ { 24576000, 96000, .v = { 8, 1, }, },
+ { 24576000, 176400, .v = { 640, 147, }, },
+ { 24576000, 192000, .v = { 4, 1, }, },
+ { 24576000, 352800, .v = { 320, 147, }, },
+ { 24576000, 384000, .v = { 2, 1, }, },
};
/* frm_size = 48 */
static const struct cs43130_clk_gen cs43130_48_clk_gen[] = {
- {22579200, 32000, 147, 100,},
- {22579200, 44100, 32, 3,},
- {22579200, 48000, 49, 5,},
- {22579200, 88200, 16, 3,},
- {22579200, 96000, 49, 10,},
- {22579200, 176400, 8, 3,},
- {22579200, 192000, 49, 20,},
- {22579200, 352800, 4, 3,},
- {22579200, 384000, 49, 40,},
- {24576000, 32000, 16, 1,},
- {24576000, 44100, 5120, 441,},
- {24576000, 48000, 32, 3,},
- {24576000, 88200, 2560, 441,},
- {24576000, 96000, 16, 3,},
- {24576000, 176400, 1280, 441,},
- {24576000, 192000, 8, 3,},
- {24576000, 352800, 640, 441,},
- {24576000, 384000, 4, 3,},
+ { 22579200, 32000, .v = { 147, 100, }, },
+ { 22579200, 44100, .v = { 32, 3, }, },
+ { 22579200, 48000, .v = { 49, 5, }, },
+ { 22579200, 88200, .v = { 16, 3, }, },
+ { 22579200, 96000, .v = { 49, 10, }, },
+ { 22579200, 176400, .v = { 8, 3, }, },
+ { 22579200, 192000, .v = { 49, 20, }, },
+ { 22579200, 352800, .v = { 4, 3, }, },
+ { 22579200, 384000, .v = { 49, 40, }, },
+ { 24576000, 32000, .v = { 16, 1, }, },
+ { 24576000, 44100, .v = { 5120, 441, }, },
+ { 24576000, 48000, .v = { 32, 3, }, },
+ { 24576000, 88200, .v = { 2560, 441, }, },
+ { 24576000, 96000, .v = { 16, 3, }, },
+ { 24576000, 176400, .v = { 1280, 441, }, },
+ { 24576000, 192000, .v = { 8, 3, }, },
+ { 24576000, 352800, .v = { 640, 441, }, },
+ { 24576000, 384000, .v = { 4, 3, }, },
};
/* frm_size = 64 */
static const struct cs43130_clk_gen cs43130_64_clk_gen[] = {
- {22579200, 32000, 441, 40,},
- {22579200, 44100, 8, 1,},
- {22579200, 48000, 147, 20,},
- {22579200, 88200, 4, 1,},
- {22579200, 96000, 147, 40,},
- {22579200, 176400, 2, 1,},
- {22579200, 192000, 147, 80,},
- {22579200, 352800, 1, 1,},
- {24576000, 32000, 12, 1,},
- {24576000, 44100, 1280, 147,},
- {24576000, 48000, 8, 1,},
- {24576000, 88200, 640, 147,},
- {24576000, 96000, 4, 1,},
- {24576000, 176400, 320, 147,},
- {24576000, 192000, 2, 1,},
- {24576000, 352800, 160, 147,},
- {24576000, 384000, 1, 1,},
+ { 22579200, 32000, .v = { 441, 40, }, },
+ { 22579200, 44100, .v = { 8, 1, }, },
+ { 22579200, 48000, .v = { 147, 20, }, },
+ { 22579200, 88200, .v = { 4, 1, }, },
+ { 22579200, 96000, .v = { 147, 40, }, },
+ { 22579200, 176400, .v = { 2, 1, }, },
+ { 22579200, 192000, .v = { 147, 80, }, },
+ { 22579200, 352800, .v = { 1, 1, }, },
+ { 24576000, 32000, .v = { 12, 1, }, },
+ { 24576000, 44100, .v = { 1280, 147, }, },
+ { 24576000, 48000, .v = { 8, 1, }, },
+ { 24576000, 88200, .v = { 640, 147, }, },
+ { 24576000, 96000, .v = { 4, 1, }, },
+ { 24576000, 176400, .v = { 320, 147, }, },
+ { 24576000, 192000, .v = { 2, 1, }, },
+ { 24576000, 352800, .v = { 160, 147, }, },
+ { 24576000, 384000, .v = { 1, 1, }, },
};
struct cs43130_bitwidth_map {
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
index 29d05e32d341..8ac043f1aae0 100644
--- a/sound/soc/codecs/cs4341.c
+++ b/sound/soc/codecs/cs4341.c
@@ -225,8 +225,7 @@ static int cs4341_probe(struct device *dev)
}
#if IS_ENABLED(CONFIG_I2C)
-static int cs4341_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cs4341_i2c_probe(struct i2c_client *i2c)
{
struct cs4341_priv *cs4341;
@@ -260,7 +259,7 @@ static struct i2c_driver cs4341_i2c_driver = {
.name = "cs4341-i2c",
.of_match_table = of_match_ptr(cs4341_dt_ids),
},
- .probe = cs4341_i2c_probe,
+ .probe_new = cs4341_i2c_probe,
.id_table = cs4341_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index 786c69a8ec4a..7069e9b54857 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -223,12 +223,9 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
{"OutputB", NULL, "HiFi DAC"},
};
-#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
+#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000
@@ -278,8 +275,7 @@ static const struct regmap_config cs4349_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int cs4349_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4349_i2c_probe(struct i2c_client *client)
{
struct cs4349_private *cs4349;
int ret;
@@ -382,7 +378,7 @@ static struct i2c_driver cs4349_i2c_driver = {
.pm = &cs4349_runtime_pm,
},
.id_table = cs4349_i2c_id,
- .probe = cs4349_i2c_probe,
+ .probe_new = cs4349_i2c_probe,
.remove = cs4349_i2c_remove,
};
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index f2087bd38dbc..703545273900 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -918,8 +918,7 @@ static struct regmap_config cs53l30_regmap = {
.use_single_write = true,
};
-static int cs53l30_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs53l30_i2c_probe(struct i2c_client *client)
{
const struct device_node *np = client->dev.of_node;
struct device *dev = &client->dev;
@@ -1125,7 +1124,7 @@ static struct i2c_driver cs53l30_i2c_driver = {
.pm = &cs53l30_runtime_pm,
},
.id_table = cs53l30_id,
- .probe = cs53l30_i2c_probe,
+ .probe_new = cs53l30_i2c_probe,
.remove = cs53l30_i2c_remove,
};
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index 1f5c57fab1d8..b35debb5818d 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -1527,6 +1527,7 @@ static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
.num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets),
.dapm_routes = cx2072x_intercon,
.num_dapm_routes = ARRAY_SIZE(cx2072x_intercon),
+ .endianness = 1,
};
/*
@@ -1626,8 +1627,7 @@ static int __maybe_unused cx2072x_runtime_resume(struct device *dev)
return clk_prepare_enable(cx2072x->mclk);
}
-static int cx2072x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cx2072x_i2c_probe(struct i2c_client *i2c)
{
struct cx2072x_priv *cx2072x;
unsigned int ven_id, rev_id;
@@ -1710,7 +1710,7 @@ static struct i2c_driver cx2072x_i2c_driver = {
.acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
.pm = &cx2072x_runtime_pm,
},
- .probe = cx2072x_i2c_probe,
+ .probe_new = 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 8af344b2fdbf..3fa3042e4424 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1206,8 +1206,7 @@ static const struct regmap_config da7210_regmap_config_i2c = {
.cache_type = REGCACHE_RBTREE,
};
-static int da7210_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7210_i2c_probe(struct i2c_client *i2c)
{
struct da7210_priv *da7210;
int ret;
@@ -1250,7 +1249,7 @@ static struct i2c_driver da7210_i2c_driver = {
.driver = {
.name = "da7210",
},
- .probe = da7210_i2c_probe,
+ .probe_new = da7210_i2c_probe,
.id_table = da7210_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 3ab89387b4e6..2e645dc60eda 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1946,8 +1946,7 @@ static const char *da7213_supply_names[DA7213_NUM_SUPPLIES] = {
[DA7213_SUPPLY_VDDIO] = "VDDIO",
};
-static int da7213_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7213_i2c_probe(struct i2c_client *i2c)
{
struct da7213_priv *da7213;
int i, ret;
@@ -2040,7 +2039,7 @@ static struct i2c_driver da7213_i2c_driver = {
.acpi_match_table = ACPI_PTR(da7213_acpi_match),
.pm = &da7213_pm,
},
- .probe = da7213_i2c_probe,
+ .probe_new = da7213_i2c_probe,
.id_table = da7213_i2c_id,
};
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index ea426d986d4c..a5d7c350a3de 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -3258,8 +3258,19 @@ static const struct regmap_config da7218_regmap_config = {
* I2C layer
*/
-static int da7218_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id da7218_i2c_id[];
+
+static inline int da7218_i2c_get_id(struct i2c_client *i2c)
+{
+ const struct i2c_device_id *id = i2c_match_id(da7218_i2c_id, i2c);
+
+ if (id)
+ return (uintptr_t)id->driver_data;
+ else
+ return -EINVAL;
+}
+
+static int da7218_i2c_probe(struct i2c_client *i2c)
{
struct da7218_priv *da7218;
int ret;
@@ -3273,7 +3284,7 @@ static int da7218_i2c_probe(struct i2c_client *i2c,
if (i2c->dev.of_node)
da7218->dev_id = da7218_of_get_id(&i2c->dev);
else
- da7218->dev_id = id->driver_data;
+ da7218->dev_id = da7218_i2c_get_id(i2c);
if ((da7218->dev_id != DA7217_DEV_ID) &&
(da7218->dev_id != DA7218_DEV_ID)) {
@@ -3311,7 +3322,7 @@ static struct i2c_driver da7218_i2c_driver = {
.name = "da7218",
.of_match_table = da7218_of_match,
},
- .probe = da7218_i2c_probe,
+ .probe_new = da7218_i2c_probe,
.id_table = da7218_i2c_id,
};
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index c7493549a9a5..7fdef38ed8cd 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -2655,8 +2655,7 @@ static const struct snd_soc_component_driver soc_component_dev_da7219 = {
* I2C layer
*/
-static int da7219_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7219_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct da7219_priv *da7219;
@@ -2711,7 +2710,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 = da7219_i2c_probe,
+ .probe_new = da7219_i2c_probe,
.remove = da7219_i2c_remove,
.id_table = da7219_i2c_id,
};
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 42d6a3fc3af5..f14cddf23f42 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1506,8 +1506,7 @@ static const struct snd_soc_component_driver soc_component_dev_da732x = {
.non_legacy_dai_naming = 1,
};
-static int da732x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da732x_i2c_probe(struct i2c_client *i2c)
{
struct da732x_priv *da732x;
unsigned int reg;
@@ -1562,7 +1561,7 @@ static struct i2c_driver da732x_i2c_driver = {
.driver = {
.name = "da7320",
},
- .probe = da732x_i2c_probe,
+ .probe_new = da732x_i2c_probe,
.remove = da732x_i2c_remove,
.id_table = da732x_i2c_id,
};
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index a9676b261129..9d8c8adc5d76 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1473,8 +1473,7 @@ static const struct regmap_config da9055_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int da9055_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da9055_i2c_probe(struct i2c_client *i2c)
{
struct da9055_priv *da9055;
struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
@@ -1533,7 +1532,7 @@ static struct i2c_driver da9055_i2c_driver = {
.name = "da9055-codec",
.of_match_table = of_match_ptr(da9055_of_match),
},
- .probe = da9055_i2c_probe,
+ .probe_new = da9055_i2c_probe,
.id_table = da9055_i2c_id,
};
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 5d079d90fd3b..d1a30ca4571a 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -82,7 +82,10 @@ static struct snd_soc_dai_driver dmic_dai = {
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
- | SNDRV_PCM_FMTBIT_S16_LE,
+ | SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U8
+ | SNDRV_PCM_FMTBIT_DSD_U16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U32_LE,
},
.ops = &dmic_dai_ops,
};
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index ff33eab6f9de..4407166bb338 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -789,8 +789,7 @@ static const struct regmap_config es8316_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int es8316_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int es8316_i2c_probe(struct i2c_client *i2c_client)
{
struct device *dev = &i2c_client->dev;
struct es8316_priv *es8316;
@@ -852,7 +851,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 = es8316_i2c_probe,
+ .probe_new = es8316_i2c_probe,
.id_table = es8316_i2c_id,
};
module_i2c_driver(es8316_i2c_driver);
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index 6b0df0d750dc..68072e99fcc7 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -29,8 +29,7 @@ static const struct of_device_id es8328_of_match[] = {
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
-static int es8328_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int es8328_i2c_probe(struct i2c_client *i2c)
{
return es8328_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
@@ -41,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
- .probe = es8328_i2c_probe,
+ .probe_new = es8328_i2c_probe,
.id_table = es8328_id,
};
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index a9f61c7e44ee..8debcee59224 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -571,13 +571,14 @@ static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = {
};
static const struct snd_soc_component_driver hdac_hda_codec = {
- .probe = hdac_hda_codec_probe,
- .remove = hdac_hda_codec_remove,
- .idle_bias_on = false,
- .dapm_widgets = hdac_hda_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets),
- .dapm_routes = hdac_hda_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes),
+ .probe = hdac_hda_codec_probe,
+ .remove = hdac_hda_codec_remove,
+ .dapm_widgets = hdac_hda_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets),
+ .dapm_routes = hdac_hda_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes),
+ .idle_bias_on = false,
+ .endianness = 1,
};
static int hdac_hda_dev_probe(struct hdac_device *hdev)
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index b07607a9ecea..b773466619b2 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -727,10 +727,8 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000)
-#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
/*
* This list is only for formats allowed on the I2S bus. So there is
@@ -740,12 +738,9 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
* problems, we should add the video side driver an option to disable
* them.
*/
-#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
- SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
static struct snd_kcontrol_new hdmi_codec_controls[] = {
{
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index 1d86b6a0eb9d..39be31e1282e 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1108,8 +1108,7 @@ static const struct regmap_config isabelle_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int isabelle_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int isabelle_i2c_probe(struct i2c_client *i2c)
{
struct regmap *isabelle_regmap;
int ret = 0;
@@ -1144,7 +1143,7 @@ static struct i2c_driver isabelle_i2c_driver = {
.driver = {
.name = "isabelle",
},
- .probe = isabelle_i2c_probe,
+ .probe_new = isabelle_i2c_probe,
.id_table = isabelle_i2c_id,
};
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index 300b325e2fdd..dba161305de8 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -115,8 +115,7 @@ static const struct regmap_config lm4857_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
};
-static int lm4857_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm4857_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -138,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "lm4857",
},
- .probe = lm4857_i2c_probe,
+ .probe_new = lm4857_i2c_probe,
.id_table = lm4857_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 973d781b4b6a..bd0078e4499b 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1412,8 +1412,7 @@ static const struct regmap_config lm49453_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int lm49453_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm49453_i2c_probe(struct i2c_client *i2c)
{
struct lm49453_priv *lm49453;
int ret = 0;
@@ -1458,7 +1457,7 @@ static struct i2c_driver lm49453_i2c_driver = {
.driver = {
.name = "lm49453",
},
- .probe = lm49453_i2c_probe,
+ .probe_new = lm49453_i2c_probe,
.remove = lm49453_i2c_remove,
.id_table = lm49453_i2c_id,
};
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
index 54426a90bc0b..54a8ba7ed3c2 100644
--- a/sound/soc/codecs/lochnagar-sc.c
+++ b/sound/soc/codecs/lochnagar-sc.c
@@ -212,12 +212,13 @@ static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
};
static const struct snd_soc_component_driver lochnagar_sc_driver = {
- .non_legacy_dai_naming = 1,
-
.dapm_widgets = lochnagar_sc_widgets,
.num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
.dapm_routes = lochnagar_sc_routes,
.num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
+
+ .non_legacy_dai_naming = 1,
+ .endianness = 1,
};
static int lochnagar_sc_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c
index 6cede75ed3b5..1b9082d237c1 100644
--- a/sound/soc/codecs/lpass-macro-common.c
+++ b/sound/soc/codecs/lpass-macro-common.c
@@ -24,42 +24,45 @@ struct lpass_macro *lpass_macro_pds_init(struct device *dev)
return ERR_PTR(-ENOMEM);
l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro");
- if (IS_ERR_OR_NULL(l_pds->macro_pd))
- return NULL;
-
- ret = pm_runtime_get_sync(l_pds->macro_pd);
- if (ret < 0) {
- pm_runtime_put_noidle(l_pds->macro_pd);
+ if (IS_ERR_OR_NULL(l_pds->macro_pd)) {
+ ret = l_pds->macro_pd ? PTR_ERR(l_pds->macro_pd) : -ENODATA;
goto macro_err;
}
+ ret = pm_runtime_resume_and_get(l_pds->macro_pd);
+ if (ret < 0)
+ goto macro_sync_err;
+
l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec");
- if (IS_ERR_OR_NULL(l_pds->dcodec_pd))
+ if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) {
+ ret = l_pds->dcodec_pd ? PTR_ERR(l_pds->dcodec_pd) : -ENODATA;
goto dcodec_err;
+ }
- ret = pm_runtime_get_sync(l_pds->dcodec_pd);
- if (ret < 0) {
- pm_runtime_put_noidle(l_pds->dcodec_pd);
+ ret = pm_runtime_resume_and_get(l_pds->dcodec_pd);
+ if (ret < 0)
goto dcodec_sync_err;
- }
return l_pds;
dcodec_sync_err:
dev_pm_domain_detach(l_pds->dcodec_pd, false);
dcodec_err:
pm_runtime_put(l_pds->macro_pd);
-macro_err:
+macro_sync_err:
dev_pm_domain_detach(l_pds->macro_pd, false);
+macro_err:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(lpass_macro_pds_init);
void lpass_macro_pds_exit(struct lpass_macro *pds)
{
- pm_runtime_put(pds->macro_pd);
- dev_pm_domain_detach(pds->macro_pd, false);
- pm_runtime_put(pds->dcodec_pd);
- dev_pm_domain_detach(pds->dcodec_pd, false);
+ if (pds) {
+ pm_runtime_put(pds->macro_pd);
+ dev_pm_domain_detach(pds->macro_pd, false);
+ pm_runtime_put(pds->dcodec_pd);
+ dev_pm_domain_detach(pds->dcodec_pd, false);
+ }
}
EXPORT_SYMBOL_GPL(lpass_macro_pds_exit);
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index 39dda1b03b3d..d711eb1da0a8 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -167,8 +167,7 @@ static const struct regmap_config max9768_i2c_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9768_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max9768_i2c_probe(struct i2c_client *client)
{
struct max9768 *max9768;
struct max9768_pdata *pdata = client->dev.platform_data;
@@ -215,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = {
.driver = {
.name = "max9768",
},
- .probe = max9768_i2c_probe,
+ .probe_new = 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 429717d4ac5a..5ef2e1279ee7 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1737,11 +1737,18 @@ static const struct snd_soc_component_driver soc_component_dev_max98088 = {
.non_legacy_dai_naming = 1,
};
-static int max98088_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id max98088_i2c_id[] = {
+ { "max98088", MAX98088 },
+ { "max98089", MAX98089 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static int max98088_i2c_probe(struct i2c_client *i2c)
{
struct max98088_priv *max98088;
int ret;
+ const struct i2c_device_id *id;
max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
GFP_KERNEL);
@@ -1757,6 +1764,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
return PTR_ERR(max98088->mclk);
+ id = i2c_match_id(max98088_i2c_id, i2c);
max98088->devtype = id->driver_data;
i2c_set_clientdata(i2c, max98088);
@@ -1767,13 +1775,6 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
return ret;
}
-static const struct i2c_device_id max98088_i2c_id[] = {
- { "max98088", MAX98088 },
- { "max98089", MAX98089 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
-
#if defined(CONFIG_OF)
static const struct of_device_id max98088_of_match[] = {
{ .compatible = "maxim,max98088" },
@@ -1788,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = {
.name = "max98088",
.of_match_table = of_match_ptr(max98088_of_match),
},
- .probe = max98088_i2c_probe,
+ .probe_new = max98088_i2c_probe,
.id_table = max98088_i2c_id,
};
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 62b41ca050a2..576277a82d41 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -393,9 +393,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mask = (1 << fls(mc->max)) - 1;
- unsigned int sel = ucontrol->value.integer.value[0];
+ int sel_unchecked = ucontrol->value.integer.value[0];
+ unsigned int sel;
unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int *select;
+ int change;
switch (mc->reg) {
case M98090_REG_MIC1_INPUT_LEVEL:
@@ -413,9 +415,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
val = (val >> mc->shift) & mask;
- if (sel < 0 || sel > mc->max)
+ if (sel_unchecked < 0 || sel_unchecked > mc->max)
return -EINVAL;
+ sel = sel_unchecked;
+ change = *select != sel;
*select = sel;
/* Setting a volume is only valid if it is already On */
@@ -430,7 +434,7 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
mask << mc->shift,
sel << mc->shift);
- return *select != val;
+ return change;
}
static const char *max98090_perf_pwr_text[] =
@@ -2532,8 +2536,14 @@ static const struct regmap_config max98090_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98090_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static const struct i2c_device_id max98090_i2c_id[] = {
+ { "max98090", MAX98090 },
+ { "max98091", MAX98091 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
+
+static int max98090_i2c_probe(struct i2c_client *i2c)
{
struct max98090_priv *max98090;
const struct acpi_device_id *acpi_id;
@@ -2555,7 +2565,9 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
return -EINVAL;
}
driver_data = acpi_id->driver_data;
- } else if (i2c_id) {
+ } else {
+ const struct i2c_device_id *i2c_id =
+ i2c_match_id(max98090_i2c_id, i2c);
driver_data = i2c_id->driver_data;
}
@@ -2662,13 +2674,6 @@ static const struct dev_pm_ops max98090_pm = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
};
-static const struct i2c_device_id max98090_i2c_id[] = {
- { "max98090", MAX98090 },
- { "max98091", MAX98091 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
-
#ifdef CONFIG_OF
static const struct of_device_id max98090_of_match[] = {
{ .compatible = "maxim,max98090", },
@@ -2693,7 +2698,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 = max98090_i2c_probe,
+ .probe_new = max98090_i2c_probe,
.shutdown = max98090_i2c_shutdown,
.remove = max98090_i2c_remove,
.id_table = max98090_i2c_id,
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 4977b00ddf5f..7bca99fa61b5 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -2106,11 +2106,17 @@ static const struct snd_soc_component_driver soc_component_dev_max98095 = {
.non_legacy_dai_naming = 1,
};
-static int max98095_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id max98095_i2c_id[] = {
+ { "max98095", MAX98095 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static int max98095_i2c_probe(struct i2c_client *i2c)
{
struct max98095_priv *max98095;
int ret;
+ const struct i2c_device_id *id;
max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv),
GFP_KERNEL);
@@ -2126,6 +2132,7 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ id = i2c_match_id(max98095_i2c_id, i2c);
max98095->devtype = id->driver_data;
i2c_set_clientdata(i2c, max98095);
max98095->pdata = i2c->dev.platform_data;
@@ -2136,12 +2143,6 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
return ret;
}
-static const struct i2c_device_id max98095_i2c_id[] = {
- { "max98095", MAX98095 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
-
#ifdef CONFIG_OF
static const struct of_device_id max98095_of_match[] = {
{ .compatible = "maxim,max98095", },
@@ -2155,7 +2156,7 @@ static struct i2c_driver max98095_i2c_driver = {
.name = "max98095",
.of_match_table = of_match_ptr(max98095_of_match),
},
- .probe = max98095_i2c_probe,
+ .probe_new = max98095_i2c_probe,
.id_table = max98095_i2c_id,
};
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
index 8d42f523e420..800f2bca6a0f 100644
--- a/sound/soc/codecs/max98371.c
+++ b/sound/soc/codecs/max98371.c
@@ -365,8 +365,7 @@ static const struct regmap_config max98371_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98371_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98371_i2c_probe(struct i2c_client *i2c)
{
struct max98371_priv *max98371;
int ret, reg;
@@ -421,7 +420,7 @@ static struct i2c_driver max98371_i2c_driver = {
.name = "max98371",
.of_match_table = of_match_ptr(max98371_of_match),
},
- .probe = max98371_i2c_probe,
+ .probe_new = 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 ddb6436835d7..4fe065ece17c 100644
--- a/sound/soc/codecs/max98373-i2c.c
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -516,8 +516,7 @@ static const struct regmap_config max98373_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98373_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98373_i2c_probe(struct i2c_client *i2c)
{
int ret = 0;
int reg = 0;
@@ -622,7 +621,7 @@ static struct i2c_driver max98373_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98373_acpi_match),
.pm = &max98373_pm,
},
- .probe = max98373_i2c_probe,
+ .probe_new = max98373_i2c_probe,
.id_table = max98373_i2c_id,
};
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index 40fd6f363f35..2a6b1648c884 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -1014,14 +1014,14 @@ static void max98390_slot_config(struct i2c_client *i2c,
max98390->i_l_slot = 1;
}
-static int max98390_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98390_i2c_probe(struct i2c_client *i2c)
{
int ret = 0;
int reg = 0;
struct max98390_priv *max98390 = NULL;
struct i2c_adapter *adapter = i2c->adapter;
+ struct gpio_desc *reset_gpio;
ret = i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE
@@ -1073,6 +1073,17 @@ static int max98390_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+
+ /* Power on device */
+ if (reset_gpio) {
+ usleep_range(1000, 2000);
+ /* bring out of reset */
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ usleep_range(1000, 2000);
+ }
+
/* Check Revision ID */
ret = regmap_read(max98390->regmap,
MAX98390_R24FF_REV_ID, &reg);
@@ -1121,7 +1132,7 @@ static struct i2c_driver max98390_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98390_acpi_match),
.pm = &max98390_pm,
},
- .probe = max98390_i2c_probe,
+ .probe_new = max98390_i2c_probe,
.id_table = max98390_i2c_id,
};
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
new file mode 100644
index 000000000000..56eb62bb041f
--- /dev/null
+++ b/sound/soc/codecs/max98396.c
@@ -0,0 +1,1637 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <sound/tlv.h>
+#include "max98396.h"
+
+static struct reg_default max98396_reg[] = {
+ {MAX98396_R2000_SW_RESET, 0x00},
+ {MAX98396_R2001_INT_RAW1, 0x00},
+ {MAX98396_R2002_INT_RAW2, 0x00},
+ {MAX98396_R2003_INT_RAW3, 0x00},
+ {MAX98396_R2004_INT_RAW4, 0x00},
+ {MAX98396_R2006_INT_STATE1, 0x00},
+ {MAX98396_R2007_INT_STATE2, 0x00},
+ {MAX98396_R2008_INT_STATE3, 0x00},
+ {MAX98396_R2009_INT_STATE4, 0x00},
+ {MAX98396_R200B_INT_FLAG1, 0x00},
+ {MAX98396_R200C_INT_FLAG2, 0x00},
+ {MAX98396_R200D_INT_FLAG3, 0x00},
+ {MAX98396_R200E_INT_FLAG4, 0x00},
+ {MAX98396_R2010_INT_EN1, 0x02},
+ {MAX98396_R2011_INT_EN2, 0x00},
+ {MAX98396_R2012_INT_EN3, 0x00},
+ {MAX98396_R2013_INT_EN4, 0x00},
+ {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+ {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+ {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+ {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+ {MAX98396_R201F_IRQ_CTRL, 0x00},
+ {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+ {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+ {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+ {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+ {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+ {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+ {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+ {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+ {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+ {MAX98396_R2040_PIN_CFG, 0x55},
+ {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+ {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+ {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+ {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+ {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+ {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+ {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+ {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+ {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+ {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+ {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+ {MAX98396_R204C_PCM_TX_HIZ_CTRL_1, 0xFF},
+ {MAX98396_R204D_PCM_TX_HIZ_CTRL_2, 0xFF},
+ {MAX98396_R204E_PCM_TX_HIZ_CTRL_3, 0xFF},
+ {MAX98396_R204F_PCM_TX_HIZ_CTRL_4, 0xFF},
+ {MAX98396_R2050_PCM_TX_HIZ_CTRL_5, 0xFF},
+ {MAX98396_R2051_PCM_TX_HIZ_CTRL_6, 0xFF},
+ {MAX98396_R2052_PCM_TX_HIZ_CTRL_7, 0xFF},
+ {MAX98396_R2053_PCM_TX_HIZ_CTRL_8, 0xFF},
+ {MAX98396_R2055_PCM_RX_SRC1, 0x00},
+ {MAX98396_R2056_PCM_RX_SRC2, 0x00},
+ {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+ {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98396_R205E_PCM_RX_EN, 0x00},
+ {MAX98396_R205F_PCM_TX_EN, 0x00},
+ {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+ {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+ {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+ {MAX98396_R207F_ICC_EN, 0x00},
+ {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+ {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+ {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+ {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+ {MAX98396_R208F_TONE_GEN_EN, 0x00},
+ {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98396_R2091_AMP_PATH_GAIN, 0x0B},
+ {MAX98396_R2092_AMP_DSP_CFG, 0x23},
+ {MAX98396_R2093_SSM_CFG, 0x0D},
+ {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+ {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+ {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+ {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+ {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+ {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+ {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+ {MAX98396_R209C_SPK_EDGE_CTRL1, 0x0A},
+ {MAX98396_R209D_SPK_EDGE_CTRL2, 0xAA},
+ {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+ {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+ {MAX98396_R20A0_AMP_SUPPLY_CTL, 0x00},
+ {MAX98396_R20AF_AMP_EN, 0x00},
+ {MAX98396_R20B0_ADC_SR, 0x30},
+ {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+ {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+ {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+ {MAX98396_R20B4_ADC_READBACK_CTRL1, 0x00},
+ {MAX98396_R20B5_ADC_READBACK_CTRL2, 0x00},
+ {MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98396_R20B7_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0x00},
+ {MAX98396_R20B9_ADC_VBAT_READBACK_LSB, 0x00},
+ {MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98396_R20BB_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB, 0x00},
+ {MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB, 0x00},
+ {MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB, 0x00},
+ {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+ {MAX98396_R20C7_ADC_CFG, 0x00},
+ {MAX98396_R20D0_DHT_CFG1, 0x00},
+ {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98396_R20D3_DHT_CFG2, 0x14},
+ {MAX98396_R20D4_DHT_CFG3, 0x02},
+ {MAX98396_R20D5_DHT_CFG4, 0x04},
+ {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98396_R20DF_DHT_EN, 0x00},
+ {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+ {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+ {MAX98396_R20E5_BPE_STATE, 0x00},
+ {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+ {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+ {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+ {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+ {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+ {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+ {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+ {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+ {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+ {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+ {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+ {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+ {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+ {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+ {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+ {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+ {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+ {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+ {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+ {MAX98396_R210D_BPE_EN, 0x00},
+ {MAX98396_R210E_AUTO_RESTART, 0x00},
+ {MAX98396_R210F_GLOBAL_EN, 0x00},
+ {MAX98396_R21FF_REVISION_ID, 0x00},
+};
+
+static struct reg_default max98397_reg[] = {
+ {MAX98396_R2000_SW_RESET, 0x00},
+ {MAX98396_R2001_INT_RAW1, 0x00},
+ {MAX98396_R2002_INT_RAW2, 0x00},
+ {MAX98396_R2003_INT_RAW3, 0x00},
+ {MAX98396_R2004_INT_RAW4, 0x00},
+ {MAX98396_R2006_INT_STATE1, 0x00},
+ {MAX98396_R2007_INT_STATE2, 0x00},
+ {MAX98396_R2008_INT_STATE3, 0x00},
+ {MAX98396_R2009_INT_STATE4, 0x00},
+ {MAX98396_R200B_INT_FLAG1, 0x00},
+ {MAX98396_R200C_INT_FLAG2, 0x00},
+ {MAX98396_R200D_INT_FLAG3, 0x00},
+ {MAX98396_R200E_INT_FLAG4, 0x00},
+ {MAX98396_R2010_INT_EN1, 0x02},
+ {MAX98396_R2011_INT_EN2, 0x00},
+ {MAX98396_R2012_INT_EN3, 0x00},
+ {MAX98396_R2013_INT_EN4, 0x00},
+ {MAX98396_R2015_INT_FLAG_CLR1, 0x00},
+ {MAX98396_R2016_INT_FLAG_CLR2, 0x00},
+ {MAX98396_R2017_INT_FLAG_CLR3, 0x00},
+ {MAX98396_R2018_INT_FLAG_CLR4, 0x00},
+ {MAX98396_R201F_IRQ_CTRL, 0x00},
+ {MAX98396_R2020_THERM_WARN_THRESH, 0x46},
+ {MAX98396_R2021_THERM_WARN_THRESH2, 0x46},
+ {MAX98396_R2022_THERM_SHDN_THRESH, 0x64},
+ {MAX98396_R2023_THERM_HYSTERESIS, 0x02},
+ {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5},
+ {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01},
+ {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32},
+ {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00},
+ {MAX98396_R2038_CLK_MON_CTRL, 0x00},
+ {MAX98396_R2039_DATA_MON_CTRL, 0x00},
+ {MAX98397_R203A_SPK_MON_THRESH, 0x03},
+ {MAX98396_R203F_ENABLE_CTRLS, 0x0F},
+ {MAX98396_R2040_PIN_CFG, 0x55},
+ {MAX98396_R2041_PCM_MODE_CFG, 0xC0},
+ {MAX98396_R2042_PCM_CLK_SETUP, 0x04},
+ {MAX98396_R2043_PCM_SR_SETUP, 0x88},
+ {MAX98396_R2044_PCM_TX_CTRL_1, 0x00},
+ {MAX98396_R2045_PCM_TX_CTRL_2, 0x00},
+ {MAX98396_R2046_PCM_TX_CTRL_3, 0x00},
+ {MAX98396_R2047_PCM_TX_CTRL_4, 0x00},
+ {MAX98396_R2048_PCM_TX_CTRL_5, 0x00},
+ {MAX98396_R2049_PCM_TX_CTRL_6, 0x00},
+ {MAX98396_R204A_PCM_TX_CTRL_7, 0x00},
+ {MAX98396_R204B_PCM_TX_CTRL_8, 0x00},
+ {MAX98397_R204C_PCM_TX_CTRL_9, 0x00},
+ {MAX98397_R204D_PCM_TX_HIZ_CTRL_1, 0xFF},
+ {MAX98397_R204E_PCM_TX_HIZ_CTRL_2, 0xFF},
+ {MAX98397_R204F_PCM_TX_HIZ_CTRL_3, 0xFF},
+ {MAX98397_R2050_PCM_TX_HIZ_CTRL_4, 0xFF},
+ {MAX98397_R2051_PCM_TX_HIZ_CTRL_5, 0xFF},
+ {MAX98397_R2052_PCM_TX_HIZ_CTRL_6, 0xFF},
+ {MAX98397_R2053_PCM_TX_HIZ_CTRL_7, 0xFF},
+ {MAX98397_R2054_PCM_TX_HIZ_CTRL_8, 0xFF},
+ {MAX98397_R2056_PCM_RX_SRC1, 0x00},
+ {MAX98397_R2057_PCM_RX_SRC2, 0x00},
+ {MAX98396_R2058_PCM_BYPASS_SRC, 0x00},
+ {MAX98396_R205D_PCM_TX_SRC_EN, 0x00},
+ {MAX98396_R205E_PCM_RX_EN, 0x00},
+ {MAX98396_R205F_PCM_TX_EN, 0x00},
+ {MAX98397_R2060_PCM_TX_SUPPLY_SEL, 0x00},
+ {MAX98396_R2070_ICC_RX_EN_A, 0x00},
+ {MAX98396_R2071_ICC_RX_EN_B, 0x00},
+ {MAX98396_R2072_ICC_TX_CTRL, 0x00},
+ {MAX98396_R207F_ICC_EN, 0x00},
+ {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04},
+ {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00},
+ {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00},
+ {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00},
+ {MAX98396_R208F_TONE_GEN_EN, 0x00},
+ {MAX98396_R2090_AMP_VOL_CTRL, 0x00},
+ {MAX98396_R2091_AMP_PATH_GAIN, 0x12},
+ {MAX98396_R2092_AMP_DSP_CFG, 0x22},
+ {MAX98396_R2093_SSM_CFG, 0x08},
+ {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12},
+ {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17},
+ {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17},
+ {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00},
+ {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00},
+ {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03},
+ {MAX98396_R209A_SPK_EDGE_CTRL, 0x00},
+ {MAX98397_R209B_SPK_PATH_WB_ONLY, 0x00},
+ {MAX98396_R209C_SPK_EDGE_CTRL1, 0x03},
+ {MAX98396_R209D_SPK_EDGE_CTRL2, 0xFC},
+ {MAX98396_R209E_AMP_CLIP_GAIN, 0x00},
+ {MAX98396_R209F_BYPASS_PATH_CFG, 0x00},
+ {MAX98396_R20AF_AMP_EN, 0x00},
+ {MAX98396_R20B0_ADC_SR, 0x30},
+ {MAX98396_R20B1_ADC_PVDD_CFG, 0x00},
+ {MAX98396_R20B2_ADC_VBAT_CFG, 0x00},
+ {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00},
+ {MAX98397_R20B4_ADC_VDDH_CFG, 0x00},
+ {MAX98397_R20B5_ADC_READBACK_CTRL1, 0x00},
+ {MAX98397_R20B6_ADC_READBACK_CTRL2, 0x00},
+ {MAX98397_R20B7_ADC_PVDD_READBACK_MSB, 0x00},
+ {MAX98397_R20B8_ADC_PVDD_READBACK_LSB, 0x00},
+ {MAX98397_R20B9_ADC_VBAT_READBACK_MSB, 0x00},
+ {MAX98397_R20BA_ADC_VBAT_READBACK_LSB, 0x00},
+ {MAX98397_R20BB_ADC_TEMP_READBACK_MSB, 0x00},
+ {MAX98397_R20BC_ADC_TEMP_READBACK_LSB, 0x00},
+ {MAX98397_R20BD_ADC_VDDH__READBACK_MSB, 0x00},
+ {MAX98397_R20BE_ADC_VDDH_READBACK_LSB, 0x00},
+ {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00},
+ {MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB, 0x00},
+ {MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB, 0x00},
+ {MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE, 0x04},
+ {MAX98396_R20C7_ADC_CFG, 0x00},
+ {MAX98396_R20D0_DHT_CFG1, 0x00},
+ {MAX98396_R20D1_LIMITER_CFG1, 0x08},
+ {MAX98396_R20D2_LIMITER_CFG2, 0x00},
+ {MAX98396_R20D3_DHT_CFG2, 0x14},
+ {MAX98396_R20D4_DHT_CFG3, 0x02},
+ {MAX98396_R20D5_DHT_CFG4, 0x04},
+ {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07},
+ {MAX98396_R20DF_DHT_EN, 0x00},
+ {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04},
+ {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00},
+ {MAX98396_R20E5_BPE_STATE, 0x00},
+ {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00},
+ {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00},
+ {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00},
+ {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00},
+ {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00},
+ {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00},
+ {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00},
+ {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00},
+ {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00},
+ {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00},
+ {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00},
+ {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00},
+ {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00},
+ {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00},
+ {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00},
+ {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00},
+ {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00},
+ {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00},
+ {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00},
+ {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00},
+ {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00},
+ {MAX98396_R2109_BPE_LOW_STATE, 0x00},
+ {MAX98396_R210A_BPE_LOW_GAIN, 0x00},
+ {MAX98396_R210B_BPE_LOW_LIMITER, 0x00},
+ {MAX98396_R210D_BPE_EN, 0x00},
+ {MAX98396_R210E_AUTO_RESTART, 0x00},
+ {MAX98396_R210F_GLOBAL_EN, 0x00},
+ {MAX98397_R22FF_REVISION_ID, 0x00},
+};
+
+static void max98396_global_enable_onoff(struct regmap *regmap, bool onoff)
+{
+ regmap_write(regmap, MAX98396_R210F_GLOBAL_EN, onoff ? 1 : 0);
+ usleep_range(11000, 12000);
+}
+
+static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ unsigned int format = 0;
+ unsigned int bclk_pol = 0;
+ int ret, status;
+ int reg;
+ bool update = false;
+
+ dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE;
+ format = MAX98396_PCM_MODE_CFG_LRCLKEDGE;
+ break;
+
+ default:
+ dev_err(component->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= MAX98396_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= MAX98396_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= MAX98396_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format |= MAX98396_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (format != (reg & MAX98396_PCM_BCLKEDGE_BSEL_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (bclk_pol != (reg & MAX98396_PCM_MODE_CFG_BCLKEDGE))
+ update = true;
+ }
+ /* GLOBAL_EN OFF prior to pcm mode, clock configuration change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_BCLKEDGE_BSEL_MASK,
+ format);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_MODE_CFG_BCLKEDGE,
+ bclk_pol);
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98396_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 max98396_set_clock(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98396->ch_size;
+ int value;
+
+ if (!max98396->tdm_mode) {
+ /* BCLK configuration */
+ value = max98396_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+
+ return 0;
+}
+
+static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+ int ret, reg;
+ int status;
+ bool update = false;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98396->ch_size = snd_pcm_format_width(params_format(params));
+
+ dev_dbg(component->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98396_PCM_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98396_PCM_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98396_PCM_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98396_PCM_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98396_PCM_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98396_PCM_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98396_PCM_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98396_PCM_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98396_PCM_SR_48000;
+ break;
+ case 88200:
+ sampling_rate = MAX98396_PCM_SR_88200;
+ break;
+ case 96000:
+ sampling_rate = MAX98396_PCM_SR_96000;
+ break;
+ case 192000:
+ sampling_rate = MAX98396_PCM_SR_192000;
+ break;
+ default:
+ dev_err(component->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ goto err;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ goto err;
+ if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, &reg);
+ if (ret < 0)
+ goto err;
+ if (sampling_rate != (reg & MAX98396_PCM_SR_MASK))
+ update = true;
+ }
+
+ /* GLOBAL_EN OFF prior to channel size and sampling rate change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ /* set channel size */
+ regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_PCM_SR_MASK, sampling_rate);
+
+ /* set sampling rate of IV */
+ if (max98396->interleave_mode &&
+ sampling_rate > MAX98396_PCM_SR_16000)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_IVADC_SR_MASK,
+ (sampling_rate - 3)
+ << MAX98396_IVADC_SR_SHIFT);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2043_PCM_SR_SETUP,
+ MAX98396_IVADC_SR_MASK,
+ sampling_rate << MAX98396_IVADC_SR_SHIFT);
+
+ ret = max98396_set_clock(component, params);
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return ret;
+
+err:
+ return -EINVAL;
+}
+
+static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+ int bsel;
+ unsigned int chan_sz = 0;
+ int ret, status;
+ int reg;
+ bool update = false;
+
+ if (!tx_mask && !rx_mask && !slots && !slot_width)
+ max98396->tdm_mode = false;
+ else
+ max98396->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98396_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(component->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(component->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (status) {
+ ret = regmap_read(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (bsel != (reg & MAX98396_PCM_CLK_SETUP_BSEL_MASK)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK))
+ update = true;
+ }
+
+ /* GLOBAL_EN OFF prior to channel size and BCLK per LRCLK change */
+ if (update)
+ max98396_global_enable_onoff(max98396->regmap, false);
+ }
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP,
+ MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+ } else {
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH0_SRC_MASK,
+ rx_mask);
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2,
+ MAX98396_PCM_DMIX_CH1_SRC_MASK,
+ rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT);
+ }
+
+ /* Tx slot Hi-Z configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_write(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ ~tx_mask & 0xFF);
+ regmap_write(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ (~tx_mask & 0xFF00) >> 8);
+ } else {
+ regmap_write(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ ~tx_mask & 0xFF);
+ regmap_write(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ (~tx_mask & 0xFF00) >> 8);
+ }
+
+ if (status && update)
+ max98396_global_enable_onoff(max98396->regmap, true);
+
+ return 0;
+}
+
+#define MAX98396_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98396_dai_ops = {
+ .set_fmt = max98396_dai_set_fmt,
+ .hw_params = max98396_dai_hw_params,
+ .set_tdm_slot = max98396_dai_tdm_slot,
+};
+
+static int max98396_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ max98396_global_enable_onoff(max98396->regmap, true);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ max98396_global_enable_onoff(max98396->regmap, false);
+
+ max98396->tdm_mode = false;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static bool max98396_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+ case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+ case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+ case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+ case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+ case MAX98396_R2027_THERM_FOLDBACK_EN:
+ case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+ case MAX98396_R2033_NOISEGATE_MODE_EN:
+ case MAX98396_R2038_CLK_MON_CTRL ... MAX98396_R2039_DATA_MON_CTRL:
+ case MAX98396_R203F_ENABLE_CTRLS ... MAX98396_R2053_PCM_TX_HIZ_CTRL_8:
+ case MAX98396_R2055_PCM_RX_SRC1 ... MAX98396_R2056_PCM_RX_SRC2:
+ case MAX98396_R2058_PCM_BYPASS_SRC:
+ case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98396_R205F_PCM_TX_EN:
+ case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+ case MAX98396_R207F_ICC_EN:
+ case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+ case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209A_SPK_EDGE_CTRL:
+ case MAX98396_R209C_SPK_EDGE_CTRL1 ... MAX98396_R20A0_AMP_SUPPLY_CTL:
+ case MAX98396_R20AF_AMP_EN ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+ case MAX98396_R20C7_ADC_CFG:
+ case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98396_R20DF_DHT_EN:
+ case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+ case MAX98396_R20E4_IV_SENSE_PATH_EN
+ ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+ case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+ case MAX98396_R21FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98396_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2000_SW_RESET:
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2041_PCM_MODE_CFG:
+ case MAX98396_R20B6_ADC_PVDD_READBACK_MSB
+ ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB:
+ case MAX98396_R20E5_BPE_STATE:
+ case MAX98396_R2109_BPE_LOW_STATE
+ ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210F_GLOBAL_EN:
+ case MAX98396_R21FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98397_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4:
+ case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4:
+ case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4:
+ case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4:
+ case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET:
+ case MAX98396_R2027_THERM_FOLDBACK_EN:
+ case MAX98396_R2030_NOISEGATE_MODE_CTRL:
+ case MAX98396_R2033_NOISEGATE_MODE_EN:
+ case MAX98396_R2038_CLK_MON_CTRL ... MAX98397_R203A_SPK_MON_THRESH:
+ case MAX98396_R203F_ENABLE_CTRLS ... MAX98397_R2054_PCM_TX_HIZ_CTRL_8:
+ case MAX98397_R2056_PCM_RX_SRC1... MAX98396_R2058_PCM_BYPASS_SRC:
+ case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98397_R2060_PCM_TX_SUPPLY_SEL:
+ case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL:
+ case MAX98396_R207F_ICC_EN:
+ case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3:
+ case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209F_BYPASS_PATH_CFG:
+ case MAX98396_R20AF_AMP_EN ... MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE:
+ case MAX98396_R20C7_ADC_CFG:
+ case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG:
+ case MAX98396_R20DF_DHT_EN:
+ case MAX98396_R20E0_IV_SENSE_PATH_CFG:
+ case MAX98396_R20E4_IV_SENSE_PATH_EN
+ ... MAX98396_R2106_BPE_THRESH_HYSTERESIS:
+ case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN:
+ case MAX98397_R22FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98397_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4:
+ case MAX98396_R2041_PCM_MODE_CFG:
+ case MAX98397_R20B7_ADC_PVDD_READBACK_MSB
+ ... MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB:
+ case MAX98396_R20E5_BPE_STATE:
+ case MAX98396_R2109_BPE_LOW_STATE
+ ... MAX98396_R210B_BPE_LOW_LIMITER:
+ case MAX98396_R210F_GLOBAL_EN:
+ case MAX98397_R22FF_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98396_op_mod_text[] = {
+ "DG", "PVDD", "VBAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_op_mod_enum,
+ MAX98396_R2098_SPK_CLS_DG_MODE,
+ 0, max98396_op_mod_text);
+
+static DECLARE_TLV_DB_SCALE(max98396_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98396_spk_tlv,
+ 0, 0x11, TLV_DB_SCALE_ITEM(400, 100, 0),
+);
+static DECLARE_TLV_DB_RANGE(max98397_digital_tlv,
+ 0, 0x4A, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ 0x4B, 0xFF, TLV_DB_SCALE_ITEM(-9000, 50, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98397_spk_tlv,
+ 0, 0x15, TLV_DB_SCALE_ITEM(600, 100, 0),
+);
+
+static int max98396_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ int reg, val;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ reg = MAX98396_R2055_PCM_RX_SRC1;
+ else
+ reg = MAX98397_R2056_PCM_RX_SRC1;
+
+ regmap_read(max98396->regmap, reg, &val);
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int max98396_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ int reg, val;
+ int change;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ reg = MAX98396_R2055_PCM_RX_SRC1;
+ else
+ reg = MAX98397_R2056_PCM_RX_SRC1;
+
+ change = snd_soc_component_test_bits(component, reg,
+ MAX98396_PCM_RX_MASK, val);
+
+ if (change)
+ regmap_update_bits(max98396->regmap, reg,
+ MAX98396_PCM_RX_MASK, val);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const max98396_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static SOC_ENUM_SINGLE_DECL(dai_sel_enum, SND_SOC_NOPM, 0,
+ max98396_switch_text);
+
+static const struct snd_kcontrol_new max98396_dai_mux =
+ SOC_DAPM_ENUM_EXT("DAI Sel Mux", dai_sel_enum,
+ max98396_mux_get, max98396_mux_put);
+
+static const struct snd_kcontrol_new max98396_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98396_R205F_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget max98396_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98396_R20AF_AMP_EN, 0, 0, max98396_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98396_dai_mux),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98396_R20E4_IV_SENSE_PATH_EN, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98396_R20E4_IV_SENSE_PATH_EN, 1, 0),
+ SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98396_vi_control),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON"),
+ SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static const char * const max98396_thermal_thresh_text[] = {
+ "50C", "51C", "52C", "53C", "54C", "55C", "56C", "57C",
+ "58C", "59C", "60C", "61C", "62C", "63C", "64C", "65C",
+ "66C", "67C", "68C", "69C", "70C", "71C", "72C", "73C",
+ "74C", "75C", "76C", "77C", "78C", "79C", "80C", "81C",
+ "82C", "83C", "84C", "85C", "86C", "87C", "88C", "89C",
+ "90C", "91C", "92C", "93C", "94C", "95C", "96C", "97C",
+ "98C", "99C", "100C", "101C", "102C", "103C", "104C", "105C",
+ "106C", "107C", "108C", "109C", "110C", "111C", "112C", "113C",
+ "114C", "115C", "116C", "117C", "118C", "119C", "120C", "121C",
+ "122C", "123C", "124C", "125C", "126C", "127C", "128C", "129C",
+ "130C", "131C", "132C", "133C", "134C", "135C", "136C", "137C",
+ "138C", "139C", "140C", "141C", "142C", "143C", "144C", "145C",
+ "146C", "147C", "148C", "149C", "150C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh1_enum,
+ MAX98396_R2020_THERM_WARN_THRESH, 0,
+ max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh2_enum,
+ MAX98396_R2021_THERM_WARN_THRESH2, 0,
+ max98396_thermal_thresh_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_shdn_thresh_enum,
+ MAX98396_R2022_THERM_SHDN_THRESH, 0,
+ max98396_thermal_thresh_text);
+
+static const char * const max98396_thermal_hyteresis_text[] = {
+ "2C", "5C", "7C", "10C"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_hysteresis_enum,
+ MAX98396_R2023_THERM_HYSTERESIS, 0,
+ max98396_thermal_hyteresis_text);
+
+static const char * const max98396_foldback_slope_text[] = {
+ "0.25", "0.5", "1.0", "2.0"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope1_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_SLOPE1_SHIFT,
+ max98396_foldback_slope_text);
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope2_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_SLOPE2_SHIFT,
+ max98396_foldback_slope_text);
+
+static const char * const max98396_foldback_reltime_text[] = {
+ "3ms", "10ms", "100ms", "300ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_reltime_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_REL_SHIFT,
+ max98396_foldback_reltime_text);
+
+static const char * const max98396_foldback_holdtime_text[] = {
+ "0ms", "20ms", "40ms", "80ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_holdtime_enum,
+ MAX98396_R2024_THERM_FOLDBACK_SET,
+ MAX98396_THERM_FB_HOLD_SHIFT,
+ max98396_foldback_holdtime_text);
+
+static int max98396_adc_value_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u8 val[2];
+ int reg = mc->reg;
+
+ /* ADC value is not available if the device is powered down */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ goto exit;
+
+ if (max98396->device_id == CODEC_TYPE_MAX98397) {
+ switch (mc->reg) {
+ case MAX98396_R20B6_ADC_PVDD_READBACK_MSB:
+ reg = MAX98397_R20B7_ADC_PVDD_READBACK_MSB;
+ break;
+ case MAX98396_R20B8_ADC_VBAT_READBACK_MSB:
+ reg = MAX98397_R20B9_ADC_VBAT_READBACK_MSB;
+ break;
+ case MAX98396_R20BA_ADC_TEMP_READBACK_MSB:
+ reg = MAX98397_R20BB_ADC_TEMP_READBACK_MSB;
+ break;
+ default:
+ goto exit;
+ }
+ }
+
+ ret = regmap_raw_read(max98396->regmap, reg, &val, 2);
+ if (ret)
+ goto exit;
+
+ /* ADC readback bits[8:0] rearrangement */
+ ucontrol->value.integer.value[0] = (val[0] << 1) | (val[1] & 1);
+ return 0;
+
+exit:
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static const struct snd_kcontrol_new max98396_snd_controls[] = {
+ /* Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+ 0, 0x7F, 1, max98396_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+ 0, 0x11, 0, max98396_spk_tlv),
+ /* Volume Ramp Up/Down Enable*/
+ SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+ /* Clock Monitor Enable */
+ SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+ /* Speaker Safe Mode Enable */
+ SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+ /* Wideband Filter Enable */
+ SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+ /* Dynamic Headroom Tracking */
+ SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+ /* Brownout Protection Engine */
+ SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+ SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+ /* Bypass Path Enable */
+ SOC_SINGLE("Bypass Path Switch",
+ MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+ /* Speaker Operation Mode */
+ SOC_ENUM("OP Mode", max98396_op_mod_enum),
+ /* Auto Restart functions */
+ SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+ SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_OVC_RESTART_SHFT, 1, 0),
+ /* Thermal Threshold */
+ SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+ SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+ SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+ SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+ SOC_SINGLE("THERM Foldback Switch",
+ MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+ SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+ SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+ SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+ SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+ /* ADC */
+ SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+};
+
+static const struct snd_kcontrol_new max98397_snd_controls[] = {
+ /* Volume */
+ SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL,
+ 0, 0xFF, 1, max98397_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN,
+ 0, 0x15, 0, max98397_spk_tlv),
+ /* Volume Ramp Up/Down Enable*/
+ SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0),
+ /* Clock Monitor Enable */
+ SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS,
+ MAX98396_CTRL_CMON_EN_SHIFT, 1, 0),
+ /* Dither Enable */
+ SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0),
+ /* DC Blocker Enable */
+ SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0),
+ /* Speaker Safe Mode Enable */
+ SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0),
+ /* Wideband Filter Enable */
+ SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG,
+ MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0),
+ SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0),
+ /* Dynamic Headroom Tracking */
+ SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0),
+ /* Brownout Protection Engine */
+ SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0),
+ SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0),
+ /* Bypass Path Enable */
+ SOC_SINGLE("Bypass Path Switch",
+ MAX98396_R205E_PCM_RX_EN, 1, 1, 0),
+ /* Speaker Operation Mode */
+ SOC_ENUM("OP Mode", max98396_op_mod_enum),
+ /* Auto Restart functions */
+ SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0),
+ SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0),
+ SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART,
+ MAX98396_OVC_RESTART_SHFT, 1, 0),
+ /* Thermal Threshold */
+ SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum),
+ SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum),
+ SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum),
+ SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum),
+ SOC_SINGLE("THERM Foldback Switch",
+ MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0),
+ SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum),
+ SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum),
+ SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum),
+ SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum),
+ /* ADC */
+ SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+ SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0,
+ max98396_adc_value_get, NULL),
+};
+
+static const struct snd_soc_dapm_route max98396_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+};
+
+static struct snd_soc_dai_driver max98396_dai[] = {
+ {
+ .name = "max98396-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .ops = &max98396_dai_ops,
+ }
+};
+
+static struct snd_soc_dai_driver max98397_dai[] = {
+ {
+ .name = "max98397-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98396_RATES,
+ .formats = MAX98396_FORMATS,
+ },
+ .ops = &max98396_dai_ops,
+ }
+};
+
+static void max98396_reset(struct max98396_priv *max98396, struct device *dev)
+{
+ int ret, reg, count;
+
+ /* Software Reset */
+ ret = regmap_write(max98396->regmap,
+ MAX98396_R2000_SW_RESET, 1);
+ if (ret)
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(5000, 6000);
+ /* Software Reset Verification */
+ ret = regmap_read(max98396->regmap,
+ GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+ return;
+ }
+ count++;
+ }
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+}
+
+static int max98396_probe(struct snd_soc_component *component)
+{
+ struct max98396_priv *max98396 =
+ snd_soc_component_get_drvdata(component);
+
+ /* Software Reset */
+ max98396_reset(max98396, component->dev);
+
+ /* L/R mix configuration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396) {
+ regmap_write(max98396->regmap,
+ MAX98396_R2055_PCM_RX_SRC1, 0x02);
+ regmap_write(max98396->regmap,
+ MAX98396_R2056_PCM_RX_SRC2, 0x10);
+ } else {
+ regmap_write(max98396->regmap,
+ MAX98397_R2056_PCM_RX_SRC1, 0x02);
+ regmap_write(max98396->regmap,
+ MAX98397_R2057_PCM_RX_SRC2, 0x10);
+ }
+ /* Enable DC blocker */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2092_AMP_DSP_CFG, 1, 1);
+ /* Enable IV Monitor DC blocker */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20E0_IV_SENSE_PATH_CFG,
+ MAX98396_IV_SENSE_DCBLK_EN_MASK,
+ MAX98396_IV_SENSE_DCBLK_EN_MASK);
+ /* Configure default data output sources */
+ regmap_write(max98396->regmap,
+ MAX98396_R205D_PCM_TX_SRC_EN, 3);
+ /* Enable Wideband Filter */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2092_AMP_DSP_CFG, 0x40, 0x40);
+ /* Enable IV Wideband Filter */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20E0_IV_SENSE_PATH_CFG, 8, 8);
+
+ /* Enable Bypass Source */
+ regmap_write(max98396->regmap,
+ MAX98396_R2058_PCM_BYPASS_SRC,
+ max98396->bypass_slot);
+ /* Voltage, current slot configuration */
+ regmap_write(max98396->regmap,
+ MAX98396_R2044_PCM_TX_CTRL_1,
+ max98396->v_slot);
+ regmap_write(max98396->regmap,
+ MAX98396_R2045_PCM_TX_CTRL_2,
+ max98396->i_slot);
+
+ if (max98396->v_slot < 8)
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->v_slot, 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->v_slot, 0);
+ else
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->v_slot - 8), 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->v_slot - 8), 0);
+
+ if (max98396->i_slot < 8)
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2053_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->i_slot, 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2054_PCM_TX_HIZ_CTRL_8,
+ 1 << max98396->i_slot, 0);
+ else
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2052_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->i_slot - 8), 0);
+ else
+ regmap_update_bits(max98396->regmap,
+ MAX98397_R2053_PCM_TX_HIZ_CTRL_7,
+ 1 << (max98396->i_slot - 8), 0);
+
+ /* Set interleave mode */
+ if (max98396->interleave_mode)
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2041_PCM_MODE_CFG,
+ MAX98396_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98396_PCM_TX_CH_INTERLEAVE_MASK);
+
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R2038_CLK_MON_CTRL,
+ MAX98396_CLK_MON_AUTO_RESTART_MASK,
+ MAX98396_CLK_MON_AUTO_RESTART_MASK);
+
+ /* Speaker Amplifier PCM RX Enable by default */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R205E_PCM_RX_EN,
+ MAX98396_PCM_RX_EN_MASK, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98396_suspend(struct device *dev)
+{
+ struct max98396_priv *max98396 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98396->regmap, true);
+ regcache_mark_dirty(max98396->regmap);
+ return 0;
+}
+
+static int max98396_resume(struct device *dev)
+{
+ struct max98396_priv *max98396 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98396->regmap, false);
+ max98396_reset(max98396, dev);
+ regcache_sync(max98396->regmap);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98396_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume)
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98396 = {
+ .probe = max98396_probe,
+ .controls = max98396_snd_controls,
+ .num_controls = ARRAY_SIZE(max98396_snd_controls),
+ .dapm_widgets = max98396_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets),
+ .dapm_routes = max98396_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98396_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98397 = {
+ .probe = max98396_probe,
+ .controls = max98397_snd_controls,
+ .num_controls = ARRAY_SIZE(max98397_snd_controls),
+ .dapm_widgets = max98396_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets),
+ .dapm_routes = max98396_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98396_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct regmap_config max98396_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98396_R21FF_REVISION_ID,
+ .reg_defaults = max98396_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98396_reg),
+ .readable_reg = max98396_readable_register,
+ .volatile_reg = max98396_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config max98397_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98397_R22FF_REVISION_ID,
+ .reg_defaults = max98397_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98397_reg),
+ .readable_reg = max98397_readable_register,
+ .volatile_reg = max98397_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98396_read_device_property(struct device *dev,
+ struct max98396_priv *max98396)
+{
+ int value;
+
+ if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value))
+ max98396->v_slot = value & 0xF;
+ else
+ max98396->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "adi,imon-slot-no", &value))
+ max98396->i_slot = value & 0xF;
+ else
+ max98396->i_slot = 1;
+
+ if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value))
+ max98396->bypass_slot = value & 0xF;
+ else
+ max98396->bypass_slot = 0;
+}
+
+static int max98396_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98396_priv *max98396 = NULL;
+ int ret, reg;
+
+ max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL);
+
+ if (!max98396) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98396);
+
+ max98396->device_id = id->driver_data;
+
+ /* regmap initialization */
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ max98396->regmap = devm_regmap_init_i2c(i2c, &max98396_regmap);
+
+ else
+ max98396->regmap = devm_regmap_init_i2c(i2c, &max98397_regmap);
+
+ if (IS_ERR(max98396->regmap)) {
+ ret = PTR_ERR(max98396->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* update interleave mode info */
+ if (device_property_read_bool(&i2c->dev, "adi,interleave_mode"))
+ max98396->interleave_mode = true;
+ else
+ max98396->interleave_mode = false;
+
+ /* voltage/current slot & gpio configuration */
+ max98396_read_device_property(&i2c->dev, max98396);
+
+ /* Reset the Device */
+ max98396->reset_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(max98396->reset_gpio)) {
+ ret = PTR_ERR(max98396->reset_gpio);
+ dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret);
+ return ret;
+ }
+
+ if (max98396->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(max98396->reset_gpio, 0);
+ /* Wait for the hw reset done */
+ usleep_range(5000, 6000);
+ }
+
+ ret = regmap_read(max98396->regmap,
+ GET_REG_ADDR_REV_ID(max98396->device_id), &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s: failed to read revision of the device.\n", id->name);
+ return ret;
+ }
+ dev_info(&i2c->dev, "%s revision ID: 0x%02X\n", id->name, reg);
+
+ /* codec registration */
+ if (max98396->device_id == CODEC_TYPE_MAX98396)
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98396,
+ max98396_dai,
+ ARRAY_SIZE(max98396_dai));
+ else
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_max98397,
+ max98397_dai,
+ ARRAY_SIZE(max98397_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id max98396_i2c_id[] = {
+ { "max98396", CODEC_TYPE_MAX98396},
+ { "max98397", CODEC_TYPE_MAX98397},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98396_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98396_of_match[] = {
+ { .compatible = "adi,max98396", },
+ { .compatible = "adi,max98397", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98396_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98396_acpi_match[] = {
+ { "ADS8396", 0 },
+ { "ADS8397", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98396_acpi_match);
+#endif
+
+static struct i2c_driver max98396_i2c_driver = {
+ .driver = {
+ .name = "max98396",
+ .of_match_table = of_match_ptr(max98396_of_match),
+ .acpi_match_table = ACPI_PTR(max98396_acpi_match),
+ .pm = &max98396_pm,
+ },
+ .probe = max98396_i2c_probe,
+ .id_table = max98396_i2c_id,
+};
+
+module_i2c_driver(max98396_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98396 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h
new file mode 100644
index 000000000000..694411038597
--- /dev/null
+++ b/sound/soc/codecs/max98396.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * max98396.h -- MAX98396 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2022, Analog Devices Inc.
+ */
+
+#ifndef _MAX98396_H
+#define _MAX98396_H
+
+#define MAX98396_R2000_SW_RESET 0x2000
+#define MAX98396_R2001_INT_RAW1 0x2001
+#define MAX98396_R2002_INT_RAW2 0x2002
+#define MAX98396_R2003_INT_RAW3 0x2003
+#define MAX98396_R2004_INT_RAW4 0x2004
+#define MAX98396_R2006_INT_STATE1 0x2006
+#define MAX98396_R2007_INT_STATE2 0x2007
+#define MAX98396_R2008_INT_STATE3 0x2008
+#define MAX98396_R2009_INT_STATE4 0x2009
+#define MAX98396_R200B_INT_FLAG1 0x200B
+#define MAX98396_R200C_INT_FLAG2 0x200C
+#define MAX98396_R200D_INT_FLAG3 0x200D
+#define MAX98396_R200E_INT_FLAG4 0x200E
+#define MAX98396_R2010_INT_EN1 0x2010
+#define MAX98396_R2011_INT_EN2 0x2011
+#define MAX98396_R2012_INT_EN3 0x2012
+#define MAX98396_R2013_INT_EN4 0x2013
+#define MAX98396_R2015_INT_FLAG_CLR1 0x2015
+#define MAX98396_R2016_INT_FLAG_CLR2 0x2016
+#define MAX98396_R2017_INT_FLAG_CLR3 0x2017
+#define MAX98396_R2018_INT_FLAG_CLR4 0x2018
+#define MAX98396_R201F_IRQ_CTRL 0x201F
+#define MAX98396_R2020_THERM_WARN_THRESH 0x2020
+#define MAX98396_R2021_THERM_WARN_THRESH2 0x2021
+#define MAX98396_R2022_THERM_SHDN_THRESH 0x2022
+#define MAX98396_R2023_THERM_HYSTERESIS 0x2023
+#define MAX98396_R2024_THERM_FOLDBACK_SET 0x2024
+#define MAX98396_R2027_THERM_FOLDBACK_EN 0x2027
+#define MAX98396_R2030_NOISEGATE_MODE_CTRL 0x2030
+#define MAX98396_R2033_NOISEGATE_MODE_EN 0x2033
+#define MAX98396_R2038_CLK_MON_CTRL 0x2038
+#define MAX98396_R2039_DATA_MON_CTRL 0x2039
+#define MAX98396_R203F_ENABLE_CTRLS 0x203F
+#define MAX98396_R2040_PIN_CFG 0x2040
+#define MAX98396_R2041_PCM_MODE_CFG 0x2041
+#define MAX98396_R2042_PCM_CLK_SETUP 0x2042
+#define MAX98396_R2043_PCM_SR_SETUP 0x2043
+#define MAX98396_R2044_PCM_TX_CTRL_1 0x2044
+#define MAX98396_R2045_PCM_TX_CTRL_2 0x2045
+#define MAX98396_R2046_PCM_TX_CTRL_3 0x2046
+#define MAX98396_R2047_PCM_TX_CTRL_4 0x2047
+#define MAX98396_R2048_PCM_TX_CTRL_5 0x2048
+#define MAX98396_R2049_PCM_TX_CTRL_6 0x2049
+#define MAX98396_R204A_PCM_TX_CTRL_7 0x204A
+#define MAX98396_R204B_PCM_TX_CTRL_8 0x204B
+#define MAX98396_R204C_PCM_TX_HIZ_CTRL_1 0x204C
+#define MAX98396_R204D_PCM_TX_HIZ_CTRL_2 0x204D
+#define MAX98396_R204E_PCM_TX_HIZ_CTRL_3 0x204E
+#define MAX98396_R204F_PCM_TX_HIZ_CTRL_4 0x204F
+#define MAX98396_R2050_PCM_TX_HIZ_CTRL_5 0x2050
+#define MAX98396_R2051_PCM_TX_HIZ_CTRL_6 0x2051
+#define MAX98396_R2052_PCM_TX_HIZ_CTRL_7 0x2052
+#define MAX98396_R2053_PCM_TX_HIZ_CTRL_8 0x2053
+#define MAX98396_R2055_PCM_RX_SRC1 0x2055
+#define MAX98396_R2056_PCM_RX_SRC2 0x2056
+#define MAX98396_R2058_PCM_BYPASS_SRC 0x2058
+#define MAX98396_R205D_PCM_TX_SRC_EN 0x205D
+#define MAX98396_R205E_PCM_RX_EN 0x205E
+#define MAX98396_R205F_PCM_TX_EN 0x205F
+#define MAX98396_R2070_ICC_RX_EN_A 0x2070
+#define MAX98396_R2071_ICC_RX_EN_B 0x2071
+#define MAX98396_R2072_ICC_TX_CTRL 0x2072
+#define MAX98396_R207F_ICC_EN 0x207F
+#define MAX98396_R2083_TONE_GEN_DC_CFG 0x2083
+#define MAX98396_R2084_TONE_GEN_DC_LVL1 0x2084
+#define MAX98396_R2085_TONE_GEN_DC_LVL2 0x2085
+#define MAX98396_R2086_TONE_GEN_DC_LVL3 0x2086
+#define MAX98396_R208F_TONE_GEN_EN 0x208F
+#define MAX98396_R2090_AMP_VOL_CTRL 0x2090
+#define MAX98396_R2091_AMP_PATH_GAIN 0x2091
+#define MAX98396_R2092_AMP_DSP_CFG 0x2092
+#define MAX98396_R2093_SSM_CFG 0x2093
+#define MAX98396_R2094_SPK_CLS_DG_THRESH 0x2094
+#define MAX98396_R2095_SPK_CLS_DG_HDR 0x2095
+#define MAX98396_R2096_SPK_CLS_DG_HOLD_TIME 0x2096
+#define MAX98396_R2097_SPK_CLS_DG_DELAY 0x2097
+#define MAX98396_R2098_SPK_CLS_DG_MODE 0x2098
+#define MAX98396_R2099_SPK_CLS_DG_VBAT_LVL 0x2099
+#define MAX98396_R209A_SPK_EDGE_CTRL 0x209A
+#define MAX98396_R209C_SPK_EDGE_CTRL1 0x209C
+#define MAX98396_R209D_SPK_EDGE_CTRL2 0x209D
+#define MAX98396_R209E_AMP_CLIP_GAIN 0x209E
+#define MAX98396_R209F_BYPASS_PATH_CFG 0x209F
+#define MAX98396_R20A0_AMP_SUPPLY_CTL 0x20A0
+#define MAX98396_R20AF_AMP_EN 0x20AF
+#define MAX98396_R20B0_ADC_SR 0x20B0
+#define MAX98396_R20B1_ADC_PVDD_CFG 0x20B1
+#define MAX98396_R20B2_ADC_VBAT_CFG 0x20B2
+#define MAX98396_R20B3_ADC_THERMAL_CFG 0x20B3
+#define MAX98396_R20B4_ADC_READBACK_CTRL1 0x20B4
+#define MAX98396_R20B5_ADC_READBACK_CTRL2 0x20B5
+#define MAX98396_R20B6_ADC_PVDD_READBACK_MSB 0x20B6
+#define MAX98396_R20B7_ADC_PVDD_READBACK_LSB 0x20B7
+#define MAX98396_R20B8_ADC_VBAT_READBACK_MSB 0x20B8
+#define MAX98396_R20B9_ADC_VBAT_READBACK_LSB 0x20B9
+#define MAX98396_R20BA_ADC_TEMP_READBACK_MSB 0x20BA
+#define MAX98396_R20BB_ADC_TEMP_READBACK_LSB 0x20BB
+#define MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB 0x20BC
+#define MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB 0x20BD
+#define MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB 0x20BE
+#define MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB 0x20BF
+#define MAX98396_R20C7_ADC_CFG 0x20C7
+#define MAX98396_R20D0_DHT_CFG1 0x20D0
+#define MAX98396_R20D1_LIMITER_CFG1 0x20D1
+#define MAX98396_R20D2_LIMITER_CFG2 0x20D2
+#define MAX98396_R20D3_DHT_CFG2 0x20D3
+#define MAX98396_R20D4_DHT_CFG3 0x20D4
+#define MAX98396_R20D5_DHT_CFG4 0x20D5
+#define MAX98396_R20D6_DHT_HYSTERESIS_CFG 0x20D6
+#define MAX98396_R20DF_DHT_EN 0x20DF
+#define MAX98396_R20E0_IV_SENSE_PATH_CFG 0x20E0
+#define MAX98396_R20E4_IV_SENSE_PATH_EN 0x20E4
+#define MAX98396_R20E5_BPE_STATE 0x20E5
+#define MAX98396_R20E6_BPE_L3_THRESH_MSB 0x20E6
+#define MAX98396_R20E7_BPE_L3_THRESH_LSB 0x20E7
+#define MAX98396_R20E8_BPE_L2_THRESH_MSB 0x20E8
+#define MAX98396_R20E9_BPE_L2_THRESH_LSB 0x20E9
+#define MAX98396_R20EA_BPE_L1_THRESH_MSB 0x20EA
+#define MAX98396_R20EB_BPE_L1_THRESH_LSB 0x20EB
+#define MAX98396_R20EC_BPE_L0_THRESH_MSB 0x20EC
+#define MAX98396_R20ED_BPE_L0_THRESH_LSB 0x20ED
+#define MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME 0x20EE
+#define MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME 0x20EF
+#define MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME 0x20F0
+#define MAX98396_R20F1_BPE_L0_HOLD_TIME 0x20F1
+#define MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP 0x20F2
+#define MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP 0x20F3
+#define MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP 0x20F4
+#define MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP 0x20F5
+#define MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN 0x20F6
+#define MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN 0x20F7
+#define MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN 0x20F8
+#define MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN 0x20F9
+#define MAX98396_R20FA_BPE_L3_ATT_REL_RATE 0x20FA
+#define MAX98396_R20FB_BPE_L2_ATT_REL_RATE 0x20FB
+#define MAX98396_R20FC_BPE_L1_ATT_REL_RATE 0x20FC
+#define MAX98396_R20FD_BPE_L0_ATT_REL_RATE 0x20FD
+#define MAX98396_R20FE_BPE_L3_LIMITER_CFG 0x20FE
+#define MAX98396_R20FF_BPE_L2_LIMITER_CFG 0x20FF
+#define MAX98396_R2100_BPE_L1_LIMITER_CFG 0x2100
+#define MAX98396_R2101_BPE_L0_LIMITER_CFG 0x2101
+#define MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE 0x2102
+#define MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE 0x2103
+#define MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE 0x2104
+#define MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE 0x2105
+#define MAX98396_R2106_BPE_THRESH_HYSTERESIS 0x2106
+#define MAX98396_R2107_BPE_INFINITE_HOLD_CLR 0x2107
+#define MAX98396_R2108_BPE_SUPPLY_SRC 0x2108
+#define MAX98396_R2109_BPE_LOW_STATE 0x2109
+#define MAX98396_R210A_BPE_LOW_GAIN 0x210A
+#define MAX98396_R210B_BPE_LOW_LIMITER 0x210B
+#define MAX98396_R210D_BPE_EN 0x210D
+#define MAX98396_R210E_AUTO_RESTART 0x210E
+#define MAX98396_R210F_GLOBAL_EN 0x210F
+#define MAX98396_R21FF_REVISION_ID 0x21FF
+
+/* MAX98927 Registers */
+#define MAX98397_R203A_SPK_MON_THRESH 0x203A
+#define MAX98397_R204C_PCM_TX_CTRL_9 0x204C
+#define MAX98397_R204D_PCM_TX_HIZ_CTRL_1 0x204D
+#define MAX98397_R204E_PCM_TX_HIZ_CTRL_2 0x204E
+#define MAX98397_R204F_PCM_TX_HIZ_CTRL_3 0x204F
+#define MAX98397_R2050_PCM_TX_HIZ_CTRL_4 0x2050
+#define MAX98397_R2051_PCM_TX_HIZ_CTRL_5 0x2051
+#define MAX98397_R2052_PCM_TX_HIZ_CTRL_6 0x2052
+#define MAX98397_R2053_PCM_TX_HIZ_CTRL_7 0x2053
+#define MAX98397_R2054_PCM_TX_HIZ_CTRL_8 0x2054
+#define MAX98397_R2056_PCM_RX_SRC1 0x2056
+#define MAX98397_R2057_PCM_RX_SRC2 0x2057
+#define MAX98397_R2060_PCM_TX_SUPPLY_SEL 0x2060
+#define MAX98397_R209B_SPK_PATH_WB_ONLY 0x209B
+#define MAX98397_R20B4_ADC_VDDH_CFG 0x20B4
+#define MAX98397_R20B5_ADC_READBACK_CTRL1 0x20B5
+#define MAX98397_R20B6_ADC_READBACK_CTRL2 0x20B6
+#define MAX98397_R20B7_ADC_PVDD_READBACK_MSB 0x20B7
+#define MAX98397_R20B8_ADC_PVDD_READBACK_LSB 0x20B8
+#define MAX98397_R20B9_ADC_VBAT_READBACK_MSB 0x20B9
+#define MAX98397_R20BA_ADC_VBAT_READBACK_LSB 0x20BA
+#define MAX98397_R20BB_ADC_TEMP_READBACK_MSB 0x20BB
+#define MAX98397_R20BC_ADC_TEMP_READBACK_LSB 0x20BC
+#define MAX98397_R20BD_ADC_VDDH__READBACK_MSB 0x20BD
+#define MAX98397_R20BE_ADC_VDDH_READBACK_LSB 0x20BE
+#define MAX98397_R20BF_ADC_LO_PVDD_READBACK_MSB 0x20BF
+#define MAX98397_R20C0_ADC_LO_PVDD_READBACK_LSB 0x20C0
+#define MAX98397_R20C1_ADC_LO_VBAT_READBACK_MSB 0x20C1
+#define MAX98397_R20C2_ADC_LO_VBAT_READBACK_LSB 0x20C2
+#define MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB 0x20C3
+#define MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB 0x20C4
+#define MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE 0x20C5
+#define MAX98397_R22FF_REVISION_ID 0x22FF
+
+#define GET_REG_ADDR_REV_ID(x)\
+ ((x) > 0 ? MAX98397_R22FF_REVISION_ID : MAX98396_R21FF_REVISION_ID)
+
+/* MAX98396_R2024_THERM_FOLDBACK_SET */
+#define MAX98396_THERM_FB_SLOPE1_SHIFT (0)
+#define MAX98396_THERM_FB_SLOPE2_SHIFT (2)
+#define MAX98396_THERM_FB_REL_SHIFT (4)
+#define MAX98396_THERM_FB_HOLD_SHIFT (6)
+
+/* MAX98396_R2038_CLK_MON_CTRL */
+#define MAX98396_CLK_MON_AUTO_RESTART_MASK (0x1 << 0)
+#define MAX98396_CLK_MON_AUTO_RESTART_SHIFT (0)
+
+/* MAX98396_R203F_ENABLE_CTRLS */
+#define MAX98396_CTRL_CMON_EN_SHIFT (0)
+
+/* MAX98396_R2041_PCM_MODE_CFG */
+#define MAX98396_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98396_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98396_PCM_FORMAT_I2S (0x0 << 3)
+#define MAX98396_PCM_FORMAT_LJ (0x1 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE0 (0x3 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE1 (0x4 << 3)
+#define MAX98396_PCM_FORMAT_TDM_MODE2 (0x5 << 3)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98396_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+#define MAX98396_PCM_MODE_CFG_LRCLKEDGE (0x1 << 1)
+
+/* MAX98396_R2042_PCM_CLK_SETUP */
+#define MAX98396_PCM_MODE_CFG_BCLKEDGE (0x1 << 4)
+#define MAX98396_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+#define MAX98396_PCM_BCLKEDGE_BSEL_MASK (0x1F)
+
+/* MAX98396_R2043_PCM_SR_SETUP */
+#define MAX98396_PCM_SR_SHIFT (0)
+#define MAX98396_IVADC_SR_SHIFT (4)
+#define MAX98396_PCM_SR_MASK (0xF << MAX98396_PCM_SR_SHIFT)
+#define MAX98396_IVADC_SR_MASK (0xF << MAX98396_IVADC_SR_SHIFT)
+#define MAX98396_PCM_SR_8000 (0)
+#define MAX98396_PCM_SR_11025 (1)
+#define MAX98396_PCM_SR_12000 (2)
+#define MAX98396_PCM_SR_16000 (3)
+#define MAX98396_PCM_SR_22050 (4)
+#define MAX98396_PCM_SR_24000 (5)
+#define MAX98396_PCM_SR_32000 (6)
+#define MAX98396_PCM_SR_44100 (7)
+#define MAX98396_PCM_SR_48000 (8)
+#define MAX98396_PCM_SR_88200 (9)
+#define MAX98396_PCM_SR_96000 (10)
+#define MAX98396_PCM_SR_176400 (11)
+#define MAX98396_PCM_SR_192000 (12)
+
+/* MAX98396_R2055_PCM_RX_SRC1 */
+#define MAX98396_PCM_RX_MASK (0x3 << 0)
+
+/* MAX98396_R2056_PCM_RX_SRC2 */
+#define MAX98396_PCM_DMIX_CH1_SHIFT (0xF << 0)
+#define MAX98396_PCM_DMIX_CH0_SRC_MASK (0xF << 0)
+#define MAX98396_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98396_PCM_DMIX_CH1_SHIFT)
+
+/* MAX98396_R205E_PCM_RX_EN */
+#define MAX98396_PCM_RX_EN_MASK (0x1 << 0)
+#define MAX98396_PCM_RX_BYP_EN_MASK (0x1 << 1)
+
+/* MAX98396_R2092_AMP_DSP_CFG */
+#define MAX98396_DSP_SPK_DCBLK_EN_SHIFT (0)
+#define MAX98396_DSP_SPK_DITH_EN_SHIFT (1)
+#define MAX98396_DSP_SPK_INVERT_SHIFT (2)
+#define MAX98396_DSP_SPK_VOL_RMPUP_SHIFT (3)
+#define MAX98396_DSP_SPK_VOL_RMPDN_SHIFT (4)
+#define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5)
+#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6)
+
+/* MAX98396_R20E0_IV_SENSE_PATH_CFG */
+#define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0)
+#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0)
+#define MAX98396_IV_SENSE_DITH_EN_SHIFT (2)
+#define MAX98396_IV_SENSE_WB_FLT_EN_SHIFT (3)
+
+/* MAX98396_R210E_AUTO_RESTART_BEHAVIOR */
+#define MAX98396_PVDD_UVLO_RESTART_SHFT (0)
+#define MAX98396_VBAT_UVLO_RESTART_SHFT (1)
+#define MAX98396_THEM_SHDN_RESTART_SHFT (2)
+#define MAX98396_OVC_RESTART_SHFT (3)
+
+enum {
+ CODEC_TYPE_MAX98396,
+ CODEC_TYPE_MAX98397,
+};
+
+struct max98396_priv {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int bypass_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ bool tdm_mode;
+ int device_id;
+};
+#endif
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index e073f0e029be..9ca6fc254883 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -299,8 +299,7 @@ static const struct snd_soc_component_driver soc_component_dev_max9850 = {
.non_legacy_dai_naming = 1,
};
-static int max9850_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max9850_i2c_probe(struct i2c_client *i2c)
{
struct max9850_priv *max9850;
int ret;
@@ -331,7 +330,7 @@ static struct i2c_driver max9850_i2c_driver = {
.driver = {
.name = "max9850",
},
- .probe = max9850_i2c_probe,
+ .probe_new = max9850_i2c_probe,
.id_table = max9850_i2c_id,
};
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
index a5aa124c4a2e..0daa2a3dd621 100644
--- a/sound/soc/codecs/max98504.c
+++ b/sound/soc/codecs/max98504.c
@@ -291,6 +291,7 @@ static const struct snd_soc_component_driver max98504_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets),
.dapm_routes = max98504_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes),
+ .endianness = 1,
};
static const struct regmap_config max98504_regmap = {
@@ -304,8 +305,7 @@ static const struct regmap_config max98504_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98504_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max98504_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
@@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = {
.name = "max98504",
.of_match_table = of_match_ptr(max98504_of_match),
},
- .probe = max98504_i2c_probe,
+ .probe_new = 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 bb8649cd421c..f0f085ecab55 100644
--- a/sound/soc/codecs/max98520.c
+++ b/sound/soc/codecs/max98520.c
@@ -677,7 +677,7 @@ static void max98520_power_on(struct max98520_priv *max98520, bool poweron)
gpiod_set_value_cansleep(max98520->reset_gpio, !poweron);
}
-static int max98520_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+static int max98520_i2c_probe(struct i2c_client *i2c)
{
int ret;
int reg = 0;
@@ -757,7 +757,7 @@ static struct i2c_driver max98520_i2c_driver = {
.of_match_table = of_match_ptr(max98520_of_match),
.pm = &max98520_pm,
},
- .probe = max98520_i2c_probe,
+ .probe_new = max98520_i2c_probe,
.id_table = max98520_i2c_id,
};
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index c2b1151c75cc..eb628b7e84f5 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -613,8 +613,7 @@ static const struct regmap_config max9867_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9867_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max9867_i2c_probe(struct i2c_client *i2c)
{
struct max9867_priv *max9867;
int ret, reg;
@@ -662,7 +661,7 @@ static struct i2c_driver max9867_i2c_driver = {
.name = "max9867",
.of_match_table = of_match_ptr(max9867_of_match),
},
- .probe = max9867_i2c_probe,
+ .probe_new = max9867_i2c_probe,
.id_table = max9867_i2c_id,
};
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 71fede9224c4..3bf86328d5cd 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -133,8 +133,7 @@ static const struct regmap_config max9877_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9877_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max9877_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int i;
@@ -161,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = {
.driver = {
.name = "max9877",
},
- .probe = max9877_i2c_probe,
+ .probe_new = max9877_i2c_probe,
.id_table = max9877_i2c_id,
};
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index f34fa274ae4f..63849ebcfd35 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -558,8 +558,7 @@ static const struct regmap_config max98925_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98925_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98925_i2c_probe(struct i2c_client *i2c)
{
int ret, reg;
u32 value;
@@ -637,7 +636,7 @@ static struct i2c_driver max98925_i2c_driver = {
.name = "max98925",
.of_match_table = of_match_ptr(max98925_of_match),
},
- .probe = max98925_i2c_probe,
+ .probe_new = max98925_i2c_probe,
.id_table = max98925_i2c_id,
};
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index 1fbbc62bb0a2..56e0a87c7112 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -510,8 +510,7 @@ static const struct regmap_config max98926_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98926_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98926_i2c_probe(struct i2c_client *i2c)
{
int ret, reg;
u32 value;
@@ -584,7 +583,7 @@ static struct i2c_driver max98926_i2c_driver = {
.name = "max98926",
.of_match_table = of_match_ptr(max98926_of_match),
},
- .probe = max98926_i2c_probe,
+ .probe_new = max98926_i2c_probe,
.id_table = max98926_i2c_id,
};
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index bf78d3c98514..b7cff76d7b5b 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -863,8 +863,7 @@ static void max98927_slot_config(struct i2c_client *i2c,
max98927->i_l_slot = 1;
}
-static int max98927_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max98927_i2c_probe(struct i2c_client *i2c)
{
int ret = 0, value;
@@ -977,7 +976,7 @@ static struct i2c_driver max98927_i2c_driver = {
.acpi_match_table = ACPI_PTR(max98927_acpi_match),
.pm = &max98927_pm,
},
- .probe = max98927_i2c_probe,
+ .probe_new = 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 0823527e4a75..de8fcbdd85be 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -550,8 +550,7 @@ static const struct regmap_config ml26124_i2c_regmap = {
.write_flag_mask = 0x01,
};
-static int ml26124_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ml26124_i2c_probe(struct i2c_client *i2c)
{
struct ml26124_priv *priv;
int ret;
@@ -583,7 +582,7 @@ static struct i2c_driver ml26124_i2c_driver = {
.driver = {
.name = "ml26124",
},
- .probe = ml26124_i2c_probe,
+ .probe_new = ml26124_i2c_probe,
.id_table = ml26124_i2c_id,
};
diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c
index 5c0536eb1044..d2cf4847eead 100644
--- a/sound/soc/codecs/mt6351.c
+++ b/sound/soc/codecs/mt6351.c
@@ -282,12 +282,9 @@ static const struct snd_soc_dai_ops mt6351_codec_dai_ops = {
.hw_params = mt6351_codec_dai_hw_params,
};
-#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
- SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
- SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
- SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE)
+#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
static struct snd_soc_dai_driver mt6351_dai_driver[] = {
{
@@ -1448,6 +1445,7 @@ static const struct snd_soc_component_driver mt6351_soc_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets),
.dapm_routes = mt6351_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes),
+ .endianness = 1,
};
static int mt6351_codec_driver_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
index 4c7b5d940799..60b209efe52d 100644
--- a/sound/soc/codecs/mt6358.c
+++ b/sound/soc/codecs/mt6358.c
@@ -2340,12 +2340,9 @@ static const struct snd_soc_dai_ops mt6358_codec_dai_ops = {
.hw_params = mt6358_codec_dai_hw_params,
};
-#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
- SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
- SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
- SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE)
+#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
static struct snd_soc_dai_driver mt6358_dai_driver[] = {
{
@@ -2433,6 +2430,7 @@ static const struct snd_soc_component_driver mt6358_soc_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets),
.dapm_routes = mt6358_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes),
+ .endianness = 1,
};
static void mt6358_parse_dt(struct mt6358_priv *priv)
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
index f8532aa7e4aa..23709b180409 100644
--- a/sound/soc/codecs/mt6359.c
+++ b/sound/soc/codecs/mt6359.c
@@ -2576,12 +2576,9 @@ static const struct snd_soc_dai_ops mt6359_codec_dai_ops = {
.shutdown = mt6359_codec_dai_shutdown,
};
-#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
- SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
- SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
- SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE)
+#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
static struct snd_soc_dai_driver mt6359_dai_driver[] = {
{
@@ -2739,6 +2736,7 @@ static const struct snd_soc_component_driver mt6359_soc_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets),
.dapm_routes = mt6359_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes),
+ .endianness = 1,
};
static int mt6359_parse_dt(struct mt6359_priv *priv)
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
index 3a881523c30f..ba11555796ad 100644
--- a/sound/soc/codecs/mt6660.c
+++ b/sound/soc/codecs/mt6660.c
@@ -323,6 +323,7 @@ static const struct snd_soc_component_driver mt6660_component_driver = {
.num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes),
.idle_bias_on = false, /* idle_bias_off = true */
+ .endianness = 1,
};
static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream,
@@ -456,8 +457,7 @@ static int _mt6660_read_chip_revision(struct mt6660_chip *chip)
return 0;
}
-static int mt6660_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int mt6660_i2c_probe(struct i2c_client *client)
{
struct mt6660_chip *chip = NULL;
int ret;
@@ -567,7 +567,7 @@ static struct i2c_driver mt6660_i2c_driver = {
.of_match_table = of_match_ptr(mt6660_of_id),
.pm = &mt6660_dev_pm_ops,
},
- .probe = mt6660_i2c_probe,
+ .probe_new = 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 ace96995fedc..347c715e22a4 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -823,8 +823,7 @@ static const struct regmap_config nau8540_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
};
-static int nau8540_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8540_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8540 *nau8540 = dev_get_platdata(dev);
@@ -874,7 +873,7 @@ static struct i2c_driver nau8540_i2c_driver = {
.name = "nau8540",
.of_match_table = of_match_ptr(nau8540_of_ids),
},
- .probe = nau8540_i2c_probe,
+ .probe_new = 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 13676b544f58..7b3b1e4ac246 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -869,8 +869,7 @@ static const struct snd_soc_component_driver nau8810_component_driver = {
.non_legacy_dai_naming = 1,
};
-static int nau8810_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8810_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8810 *nau8810 = dev_get_platdata(dev);
@@ -916,7 +915,7 @@ static struct i2c_driver nau8810_i2c_driver = {
.name = "nau8810",
.of_match_table = of_match_ptr(nau8810_of_match),
},
- .probe = nau8810_i2c_probe,
+ .probe_new = nau8810_i2c_probe,
.id_table = nau8810_i2c_id,
};
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index d67dc27890a9..ce4e7f46bb06 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -1626,8 +1626,7 @@ static int nau8821_setup_irq(struct nau8821 *nau8821)
return 0;
}
-static int nau8821_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8821_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev);
@@ -1703,7 +1702,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 = nau8821_i2c_probe,
+ .probe_new = nau8821_i2c_probe,
.remove = nau8821_i2c_remove,
.id_table = nau8821_i2c_ids,
};
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
index 58123390c7a3..66bbd8f4f1ad 100644
--- a/sound/soc/codecs/nau8822.c
+++ b/sound/soc/codecs/nau8822.c
@@ -1083,8 +1083,7 @@ static const struct regmap_config nau8822_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults),
};
-static int nau8822_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8822_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8822 *nau8822 = dev_get_platdata(dev);
@@ -1141,7 +1140,7 @@ static struct i2c_driver nau8822_i2c_driver = {
.name = "nau8822",
.of_match_table = of_match_ptr(nau8822_of_match),
},
- .probe = nau8822_i2c_probe,
+ .probe_new = 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 d0dd1542f78a..2a7c93508535 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -1910,8 +1910,7 @@ const char *nau8824_components(void)
}
EXPORT_SYMBOL_GPL(nau8824_components);
-static int nau8824_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8824_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8824 *nau8824 = dev_get_platdata(dev);
@@ -1985,7 +1984,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 = nau8824_i2c_probe,
+ .probe_new = 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 7734bc35ab21..20e45a337b8f 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -2613,8 +2613,7 @@ static int nau8825_setup_irq(struct nau8825 *nau8825)
return 0;
}
-static int nau8825_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int nau8825_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
@@ -2703,7 +2702,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 = nau8825_i2c_probe,
+ .probe_new = nau8825_i2c_probe,
.remove = nau8825_i2c_remove,
.id_table = nau8825_i2c_ids,
};
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 9eb65f94fc4d..20eb04c8a41a 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -299,8 +299,7 @@ static const struct i2c_device_id pcm1681_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
-static int pcm1681_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm1681_i2c_probe(struct i2c_client *client)
{
int ret;
struct pcm1681_private *priv;
@@ -329,7 +328,7 @@ static struct i2c_driver pcm1681_i2c_driver = {
.of_match_table = of_match_ptr(pcm1681_dt_ids),
},
.id_table = pcm1681_i2c_id,
- .probe = pcm1681_i2c_probe,
+ .probe_new = 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 7a6be45f8149..1d2f7480a6e4 100644
--- a/sound/soc/codecs/pcm1789-i2c.c
+++ b/sound/soc/codecs/pcm1789-i2c.c
@@ -12,8 +12,7 @@
#include "pcm1789.h"
-static int pcm1789_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm1789_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -30,7 +29,9 @@ static int pcm1789_i2c_probe(struct i2c_client *client,
static int pcm1789_i2c_remove(struct i2c_client *client)
{
- return pcm1789_common_exit(&client->dev);
+ pcm1789_common_exit(&client->dev);
+
+ return 0;
}
#ifdef CONFIG_OF
@@ -53,7 +54,7 @@ static struct i2c_driver pcm1789_i2c_driver = {
.of_match_table = of_match_ptr(pcm1789_of_match),
},
.id_table = pcm1789_i2c_ids,
- .probe = pcm1789_i2c_probe,
+ .probe_new = pcm1789_i2c_probe,
.remove = pcm1789_i2c_remove,
};
diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c
index 620dec172ce7..35788b57e11f 100644
--- a/sound/soc/codecs/pcm1789.c
+++ b/sound/soc/codecs/pcm1789.c
@@ -259,13 +259,11 @@ int pcm1789_common_init(struct device *dev, struct regmap *regmap)
}
EXPORT_SYMBOL_GPL(pcm1789_common_init);
-int pcm1789_common_exit(struct device *dev)
+void pcm1789_common_exit(struct device *dev)
{
struct pcm1789_private *priv = dev_get_drvdata(dev);
flush_work(&priv->work);
-
- return 0;
}
EXPORT_SYMBOL_GPL(pcm1789_common_exit);
diff --git a/sound/soc/codecs/pcm1789.h b/sound/soc/codecs/pcm1789.h
index c446d789ed48..79439c8322b3 100644
--- a/sound/soc/codecs/pcm1789.h
+++ b/sound/soc/codecs/pcm1789.h
@@ -12,6 +12,6 @@
extern const struct regmap_config pcm1789_regmap_config;
int pcm1789_common_init(struct device *dev, struct regmap *regmap);
-int pcm1789_common_exit(struct device *dev);
+void pcm1789_common_exit(struct device *dev);
#endif
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
index 34a3d596f288..e20fe3a85af8 100644
--- a/sound/soc/codecs/pcm179x-i2c.c
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -14,8 +14,7 @@
#include "pcm179x.h"
-static int pcm179x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm179x_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -50,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 = pcm179x_i2c_probe,
+ .probe_new = 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 f8382b74391d..932c8d41c3ea 100644
--- a/sound/soc/codecs/pcm186x-i2c.c
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -22,9 +22,18 @@ static const struct of_device_id pcm186x_of_match[] = {
};
MODULE_DEVICE_TABLE(of, pcm186x_of_match);
-static int pcm186x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id pcm186x_i2c_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
+
+static int pcm186x_i2c_probe(struct i2c_client *i2c)
{
+ const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c);
const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
int irq = i2c->irq;
struct regmap *regmap;
@@ -36,17 +45,8 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c,
return pcm186x_probe(&i2c->dev, type, irq, regmap);
}
-static const struct i2c_device_id pcm186x_i2c_id[] = {
- { "pcm1862", PCM1862 },
- { "pcm1863", PCM1863 },
- { "pcm1864", PCM1864 },
- { "pcm1865", PCM1865 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
-
static struct i2c_driver pcm186x_i2c_driver = {
- .probe = pcm186x_i2c_probe,
+ .probe_new = 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 2c78dccb3f62..fda9d7ee3fe6 100644
--- a/sound/soc/codecs/pcm186x.c
+++ b/sound/soc/codecs/pcm186x.c
@@ -534,19 +534,14 @@ static int pcm186x_power_on(struct snd_soc_component *component)
static int pcm186x_power_off(struct snd_soc_component *component)
{
struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component);
- int ret;
snd_soc_component_update_bits(component, PCM186X_POWER_CTRL,
PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
regcache_cache_only(priv->regmap, true);
- ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
priv->supplies);
- if (ret)
- return ret;
-
- return 0;
}
static int pcm186x_set_bias_level(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c
index abcdeb922201..2388098eeca3 100644
--- a/sound/soc/codecs/pcm3060-i2c.c
+++ b/sound/soc/codecs/pcm3060-i2c.c
@@ -10,8 +10,7 @@
#include "pcm3060.h"
-static int pcm3060_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm3060_i2c_probe(struct i2c_client *i2c)
{
struct pcm3060_priv *priv;
@@ -50,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = {
#endif /* CONFIG_OF */
},
.id_table = pcm3060_i2c_id,
- .probe = pcm3060_i2c_probe,
+ .probe_new = pcm3060_i2c_probe,
};
module_i2c_driver(pcm3060_i2c_driver);
diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c
index 4e3bfb9fa444..586ec8c7246c 100644
--- a/sound/soc/codecs/pcm3060.c
+++ b/sound/soc/codecs/pcm3060.c
@@ -255,6 +255,7 @@ static const struct snd_soc_component_driver pcm3060_soc_comp_driver = {
.num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets),
.dapm_routes = pcm3060_dapm_map,
.num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map),
+ .endianness = 1,
};
/* regmap */
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
index 1f75933e74fa..c0fa0dc80e8f 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -15,8 +15,7 @@
#include "pcm3168a.h"
-static int pcm3168a_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm3168a_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -47,7 +46,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 = pcm3168a_i2c_probe,
+ .probe_new = 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 633f7ebe29a3..81754e141a55 100644
--- a/sound/soc/codecs/pcm512x-i2c.c
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -13,8 +13,7 @@
#include "pcm512x.h"
-static int pcm512x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm512x_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config = pcm512x_regmap;
@@ -68,7 +67,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match);
#endif
static struct i2c_driver pcm512x_i2c_driver = {
- .probe = pcm512x_i2c_probe,
+ .probe_new = 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 758d439e8c7a..86b679cf7aef 100644
--- a/sound/soc/codecs/rk3328_codec.c
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -481,7 +481,7 @@ static int rk3328_platform_probe(struct platform_device *pdev)
ret = clk_prepare_enable(rk3328->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable acodec pclk\n");
- return ret;
+ goto err_unprepare_mclk;
}
base = devm_platform_ioremap_resource(pdev, 0);
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index b62301a6281f..08dbaef84d4e 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -2433,8 +2433,7 @@ static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
return 0;
}
-static int rt1011_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt1011_i2c_probe(struct i2c_client *i2c)
{
struct rt1011_priv *rt1011;
int ret;
@@ -2485,7 +2484,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 = rt1011_i2c_probe,
+ .probe_new = 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 6a27dfacd81c..7a06f2654afb 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -1113,8 +1113,7 @@ static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev)
&rt1015->pdata.power_up_delay_ms);
}
-static int rt1015_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt1015_i2c_probe(struct i2c_client *i2c)
{
struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt1015_priv *rt1015;
@@ -1172,7 +1171,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 = rt1015_i2c_probe,
+ .probe_new = 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 9845cdddcb4c..e31c4736627f 100644
--- a/sound/soc/codecs/rt1016.c
+++ b/sound/soc/codecs/rt1016.c
@@ -631,8 +631,7 @@ static const struct acpi_device_id rt1016_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match);
#endif
-static int rt1016_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt1016_i2c_probe(struct i2c_client *i2c)
{
struct rt1016_priv *rt1016;
int ret;
@@ -685,7 +684,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 = rt1016_i2c_probe,
+ .probe_new = 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 80b7ca0e4e1e..f3f15fbe85d0 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -515,7 +515,7 @@ static struct snd_soc_dai_driver rt1019_dai[] = {
};
static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
- .probe = rt1019_probe,
+ .probe = rt1019_probe,
.controls = rt1019_snd_controls,
.num_controls = ARRAY_SIZE(rt1019_snd_controls),
.dapm_widgets = rt1019_dapm_widgets,
@@ -523,6 +523,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
.dapm_routes = rt1019_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt1019_dapm_routes),
.non_legacy_dai_naming = 1,
+ .endianness = 1,
};
static const struct regmap_config rt1019_regmap = {
@@ -558,8 +559,7 @@ static const struct acpi_device_id rt1019_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match);
#endif
-static int rt1019_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt1019_i2c_probe(struct i2c_client *i2c)
{
struct rt1019_priv *rt1019;
int ret;
@@ -599,7 +599,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 = rt1019_i2c_probe,
+ .probe_new = 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 a9c473537a91..58d97e3c5087 100644
--- a/sound/soc/codecs/rt1305.c
+++ b/sound/soc/codecs/rt1305.c
@@ -1117,8 +1117,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305)
regcache_cache_bypass(rt1305->regmap, false);
}
-static int rt1305_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt1305_i2c_probe(struct i2c_client *i2c)
{
struct rt1305_priv *rt1305;
int ret;
@@ -1172,7 +1171,7 @@ static struct i2c_driver rt1305_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt1305_acpi_match)
#endif
},
- .probe = rt1305_i2c_probe,
+ .probe_new = 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 1ef836a68a56..1c11b42dd76e 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -615,6 +615,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = {
.num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
.dapm_routes = rt1308_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+ .endianness = 1,
};
static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h
index c5ce75666dcc..6668e19d85d4 100644
--- a/sound/soc/codecs/rt1308-sdw.h
+++ b/sound/soc/codecs/rt1308-sdw.h
@@ -140,6 +140,7 @@ static const struct reg_default rt1308_reg_defaults[] = {
{ 0x3008, 0x02 },
{ 0x300a, 0x00 },
{ 0xc003 | (RT1308_DAC_SET << 4), 0x00 },
+ { 0xc000 | (RT1308_POWER << 4), 0x00 },
{ 0xc001 | (RT1308_POWER << 4), 0x00 },
{ 0xc002 | (RT1308_POWER << 4), 0x00 },
};
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
index c555b77b3c5c..eec2b1760408 100644
--- a/sound/soc/codecs/rt1308.c
+++ b/sound/soc/codecs/rt1308.c
@@ -814,8 +814,7 @@ static void rt1308_efuse(struct rt1308_priv *rt1308)
regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000);
}
-static int rt1308_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt1308_i2c_probe(struct i2c_client *i2c)
{
struct rt1308_priv *rt1308;
int ret;
@@ -864,7 +863,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 = rt1308_i2c_probe,
+ .probe_new = 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 c66d7b20cb4d..60baa9ff1907 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -597,6 +597,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = {
.num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets),
.dapm_routes = rt1316_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes),
+ .endianness = 1,
};
static const struct snd_soc_dai_ops rt1316_aif_dai_ops = {
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 0d3773c576f8..ab093bdb5552 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1114,8 +1114,7 @@ static const struct acpi_device_id rt274_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt274_acpi_match);
#endif
-static int rt274_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt274_i2c_probe(struct i2c_client *i2c)
{
struct rt274_priv *rt274;
@@ -1227,7 +1226,7 @@ static struct i2c_driver rt274_i2c_driver = {
.of_match_table = of_match_ptr(rt274_of_match),
#endif
},
- .probe = rt274_i2c_probe,
+ .probe_new = 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 caa720884e3e..ad8ea1fa7c23 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1134,8 +1134,7 @@ static const struct dmi_system_id dmi_dell[] = {
{ }
};
-static int rt286_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt286_i2c_probe(struct i2c_client *i2c)
{
struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt286_priv *rt286;
@@ -1271,7 +1270,7 @@ static struct i2c_driver rt286_i2c_driver = {
.name = "rt286",
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
- .probe = rt286_i2c_probe,
+ .probe_new = 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 c592c40a7ab3..c291786dc82d 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1176,8 +1176,7 @@ static const struct dmi_system_id force_combo_jack_table[] = {
{ }
};
-static int rt298_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt298_i2c_probe(struct i2c_client *i2c)
{
struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt298_priv *rt298;
@@ -1314,7 +1313,7 @@ static struct i2c_driver rt298_i2c_driver = {
.name = "rt298",
.acpi_match_table = ACPI_PTR(rt298_acpi_match),
},
- .probe = rt298_i2c_probe,
+ .probe_new = 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 577680df7052..be8ece4630df 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -419,7 +419,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
}
}
- return 0;
+ return 1;
}
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
@@ -1252,8 +1252,7 @@ static __maybe_unused int rt5514_i2c_resume(struct device *dev)
return 0;
}
-static int rt5514_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5514_i2c_probe(struct i2c_client *i2c)
{
struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5514_priv *rt5514;
@@ -1330,7 +1329,7 @@ static struct i2c_driver rt5514_i2c_driver = {
.of_match_table = of_match_ptr(rt5514_of_match),
.pm = &rt5514_i2_pm_ops,
},
- .probe = rt5514_i2c_probe,
+ .probe_new = 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 8e6414468a87..37f1bf552eff 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1337,8 +1337,7 @@ static const struct of_device_id rt5616_of_match[] = {
MODULE_DEVICE_TABLE(of, rt5616_of_match);
#endif
-static int rt5616_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5616_i2c_probe(struct i2c_client *i2c)
{
struct rt5616_priv *rt5616;
unsigned int val;
@@ -1408,7 +1407,7 @@ static struct i2c_driver rt5616_i2c_driver = {
.name = "rt5616",
.of_match_table = of_match_ptr(rt5616_of_match),
},
- .probe = rt5616_i2c_probe,
+ .probe_new = 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 38356ea2bd6e..c941e878471c 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1699,8 +1699,7 @@ static const struct regmap_config rt5631_regmap_config = {
.use_single_write = true,
};
-static int rt5631_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5631_i2c_probe(struct i2c_client *i2c)
{
struct rt5631_priv *rt5631;
int ret;
@@ -1732,7 +1731,7 @@ static struct i2c_driver rt5631_i2c_driver = {
.name = "rt5631",
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
},
- .probe = rt5631_i2c_probe,
+ .probe_new = 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 30c2e7cb7ed2..12da2bea1a7b 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -2927,8 +2927,7 @@ static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
return 0;
}
-static int rt5640_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5640_i2c_probe(struct i2c_client *i2c)
{
struct rt5640_priv *rt5640;
int ret;
@@ -3006,7 +3005,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 = rt5640_i2c_probe,
+ .probe_new = 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 197c56047947..507aba8de3cc 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3855,8 +3855,7 @@ static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
return 0;
}
-static int rt5645_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5645_i2c_probe(struct i2c_client *i2c)
{
struct rt5645_platform_data *pdata = NULL;
const struct dmi_system_id *dmi_data;
@@ -3944,7 +3943,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
/*
@@ -3975,7 +3974,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
ret = PTR_ERR(rt5645->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
- return ret;
+ goto err_enable;
}
regmap_write(rt5645->regmap, RT5645_RESET, 0);
@@ -4154,9 +4153,14 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
if (i2c->irq)
free_irq(i2c->irq, rt5645);
+ /*
+ * Since the rt5645_btn_check_callback() can queue jack_detect_work,
+ * the timer need to be delted first
+ */
+ del_timer_sync(&rt5645->btn_check_timer);
+
cancel_delayed_work_sync(&rt5645->jack_detect_work);
cancel_delayed_work_sync(&rt5645->rcclock_work);
- del_timer_sync(&rt5645->btn_check_timer);
regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
@@ -4183,7 +4187,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 = rt5645_i2c_probe,
+ .probe_new = 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 f302c25688d1..d11d201b1d03 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -2209,8 +2209,7 @@ MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
* Note this function MUST not look at device-properties, see the comment
* above rt5651_apply_properties().
*/
-static int rt5651_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5651_i2c_probe(struct i2c_client *i2c)
{
struct rt5651_priv *rt5651;
int ret;
@@ -2281,7 +2280,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 = rt5651_i2c_probe,
+ .probe_new = 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 e1503c2eee81..6efa90f46362 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -4093,8 +4093,7 @@ static void rt5659_intel_hd_header_probe_setup(struct rt5659_priv *rt5659)
RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN);
}
-static int rt5659_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5659_i2c_probe(struct i2c_client *i2c)
{
struct rt5659_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5659_priv *rt5659;
@@ -4342,7 +4341,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 = rt5659_i2c_probe,
+ .probe_new = 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 3b50fb29864e..d5f9926625d2 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1266,8 +1266,7 @@ static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev)
return 0;
}
-static int rt5660_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5660_i2c_probe(struct i2c_client *i2c)
{
struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5660_priv *rt5660;
@@ -1343,7 +1342,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 = rt5660_i2c_probe,
+ .probe_new = 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 3a8fba101b20..e51eed8a79ab 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3490,8 +3490,7 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
return 0;
}
-static int rt5663_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5663_i2c_probe(struct i2c_client *i2c)
{
struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5663_priv *rt5663;
@@ -3737,7 +3736,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 = rt5663_i2c_probe,
+ .probe_new = 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 33e889802ff8..4a8d62e1dd2b 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4757,8 +4757,7 @@ static void rt5665_calibrate_handler(struct work_struct *work)
rt5665_calibrate(rt5665);
}
-static int rt5665_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5665_i2c_probe(struct i2c_client *i2c)
{
struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5665_priv *rt5665;
@@ -4970,7 +4969,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 = rt5665_i2c_probe,
+ .probe_new = 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 5b12cbf2ba21..01566f036ca1 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2453,8 +2453,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668)
}
-static int rt5668_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5668_i2c_probe(struct i2c_client *i2c)
{
struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5668_priv *rt5668;
@@ -2620,7 +2619,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 = rt5668_i2c_probe,
+ .probe_new = 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 ce7684752bb0..8a97f6db04d5 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -3046,8 +3046,7 @@ const char *rt5670_components(void)
}
EXPORT_SYMBOL_GPL(rt5670_components);
-static int rt5670_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5670_i2c_probe(struct i2c_client *i2c)
{
struct rt5670_priv *rt5670;
int ret;
@@ -3334,7 +3333,7 @@ static struct i2c_driver rt5670_i2c_driver = {
.name = "rt5670",
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
- .probe = rt5670_i2c_probe,
+ .probe_new = rt5670_i2c_probe,
.remove = rt5670_i2c_remove,
.id_table = rt5670_i2c_id,
};
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index 20fc0f3766de..3f72f6093436 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -118,8 +118,7 @@ static void rt5682_i2c_disable_regulators(void *data)
regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies);
}
-static int rt5682_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5682_i2c_probe(struct i2c_client *i2c)
{
struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5682_priv *rt5682;
@@ -335,7 +334,7 @@ static struct i2c_driver rt5682_i2c_driver = {
.acpi_match_table = rt5682_acpi_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
- .probe = rt5682_i2c_probe,
+ .probe_new = rt5682_i2c_probe,
.remove = rt5682_i2c_remove,
.shutdown = rt5682_i2c_shutdown,
.id_table = rt5682_i2c_id,
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index b55f3ac3a267..4d44eddee901 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -42,8 +42,8 @@ static const struct rt5682s_platform_data i2s_default_platform_data = {
};
static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = {
- "AVDD",
- "MICVDD",
+ [RT5682S_SUPPLY_AVDD] = "AVDD",
+ [RT5682S_SUPPLY_MICVDD] = "MICVDD",
};
static const struct reg_sequence patch_list[] = {
@@ -3022,12 +3022,21 @@ static struct snd_soc_dai_driver rt5682s_dai[] = {
static void rt5682s_i2c_disable_regulators(void *data)
{
struct rt5682s_priv *rt5682s = data;
+ struct device *dev = regmap_get_device(rt5682s->regmap);
+ int ret;
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply AVDD: %d\n", ret);
- regulator_bulk_disable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+ usleep_range(1000, 1500);
+
+ ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+ if (ret)
+ dev_err(dev, "Failed to disable supply MICVDD: %d\n", ret);
}
-static int rt5682s_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5682s_i2c_probe(struct i2c_client *i2c)
{
struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5682s_priv *rt5682s;
@@ -3068,9 +3077,16 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- ret = regulator_bulk_enable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies);
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to enable supply MICVDD: %d\n", ret);
+ return ret;
+ }
+ usleep_range(1000, 1500);
+
+ ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer);
if (ret) {
- dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to enable supply AVDD: %d\n", ret);
return ret;
}
@@ -3211,7 +3227,7 @@ static struct i2c_driver rt5682s_i2c_driver = {
.acpi_match_table = rt5682s_acpi_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
- .probe = rt5682s_i2c_probe,
+ .probe_new = rt5682s_i2c_probe,
.remove = rt5682s_i2c_remove,
.shutdown = rt5682s_i2c_shutdown,
.id_table = rt5682s_i2c_id,
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
index 397a2531b6f6..7353831c73dd 100644
--- a/sound/soc/codecs/rt5682s.h
+++ b/sound/soc/codecs/rt5682s.h
@@ -1434,7 +1434,11 @@ struct pll_calc_map {
bool sel_ps;
};
-#define RT5682S_NUM_SUPPLIES 2
+enum {
+ RT5682S_SUPPLY_AVDD,
+ RT5682S_SUPPLY_MICVDD,
+ RT5682S_NUM_SUPPLIES,
+};
struct rt5682s_priv {
struct snd_soc_component *component;
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
index e61a8257bf64..af32295fa9b9 100644
--- a/sound/soc/codecs/rt700.c
+++ b/sound/soc/codecs/rt700.c
@@ -853,6 +853,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt700 = {
.dapm_routes = rt700_audio_map,
.num_dapm_routes = ARRAY_SIZE(rt700_audio_map),
.set_jack = rt700_set_jack_detect,
+ .endianness = 1,
};
static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index bdb1375f0338..57629c18db38 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -1208,6 +1208,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
.num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map),
.set_jack = rt711_sdca_set_jack_detect,
.remove = rt711_sdca_remove,
+ .endianness = 1,
};
static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
index ea25fd58d43a..9838fb4d5b9c 100644
--- a/sound/soc/codecs/rt711.c
+++ b/sound/soc/codecs/rt711.c
@@ -950,6 +950,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
.num_dapm_routes = ARRAY_SIZE(rt711_audio_map),
.set_jack = rt711_set_jack_detect,
.remove = rt711_remove,
+ .endianness = 1,
};
static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index a5c673f43d82..0ecd2948f7aa 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -181,8 +181,6 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
{
struct regmap *mbq_regmap, *regmap;
- slave->ops = &rt715_sdca_slave_ops;
-
/* Regmap Initialization */
mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap);
if (IS_ERR(mbq_regmap))
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
index bfa536bd7196..5857d0866307 100644
--- a/sound/soc/codecs/rt715-sdca.c
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -765,6 +765,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
.num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets),
.dapm_routes = rt715_sdca_audio_map,
.num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map),
+ .endianness = 1,
};
static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
index a64d11a74751..418e006b19ef 100644
--- a/sound/soc/codecs/rt715.c
+++ b/sound/soc/codecs/rt715.c
@@ -745,6 +745,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715 = {
.num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets),
.dapm_routes = rt715_audio_map,
.num_dapm_routes = ARRAY_SIZE(rt715_audio_map),
+ .endianness = 1,
};
static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c
index 6e0d7cf0c8c9..da495bdc8415 100644
--- a/sound/soc/codecs/rt9120.c
+++ b/sound/soc/codecs/rt9120.c
@@ -7,6 +7,7 @@
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
@@ -75,6 +76,7 @@ enum {
struct rt9120_data {
struct device *dev;
struct regmap *regmap;
+ struct gpio_desc *pwdnn_gpio;
int chip_idx;
};
@@ -160,6 +162,8 @@ static int rt9120_codec_probe(struct snd_soc_component *comp)
snd_soc_component_init_regmap(comp, data->regmap);
+ pm_runtime_get_sync(comp->dev);
+
/* Internal setting */
if (data->chip_idx == CHIP_IDX_RT9120S) {
snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde);
@@ -167,6 +171,9 @@ static int rt9120_codec_probe(struct snd_soc_component *comp)
} else
snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04);
+ pm_runtime_mark_last_busy(comp->dev);
+ pm_runtime_put(comp->dev);
+
return 0;
}
@@ -178,6 +185,7 @@ static const struct snd_soc_component_driver rt9120_component_driver = {
.num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets),
.dapm_routes = rt9120_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes),
+ .endianness = 1,
};
static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
@@ -337,6 +345,18 @@ static const struct regmap_access_table rt9120_wr_table = {
.n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges),
};
+static bool rt9120_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x01:
+ case 0x10:
+ case 0x30 ... 0x40:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int rt9120_get_reg_size(unsigned int reg)
{
switch (reg) {
@@ -371,7 +391,7 @@ static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val)
*val = be32_to_cpup((__be32 *)raw);
break;
case 3:
- *val = raw[0] << 16 | raw[1] << 8 | raw[0];
+ *val = raw[0] << 16 | raw[1] << 8 | raw[2];
break;
case 2:
*val = be16_to_cpup((__be16 *)raw);
@@ -396,14 +416,49 @@ static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val)
return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs);
}
+static const struct reg_default rt9120_reg_defaults[] = {
+ { .reg = 0x02, .def = 0x02 },
+ { .reg = 0x03, .def = 0xf2 },
+ { .reg = 0x04, .def = 0x01 },
+ { .reg = 0x05, .def = 0xc0 },
+ { .reg = 0x06, .def = 0x28 },
+ { .reg = 0x07, .def = 0x04 },
+ { .reg = 0x08, .def = 0xff },
+ { .reg = 0x09, .def = 0x01 },
+ { .reg = 0x0a, .def = 0x01 },
+ { .reg = 0x0b, .def = 0x00 },
+ { .reg = 0x0c, .def = 0x04 },
+ { .reg = 0x11, .def = 0x30 },
+ { .reg = 0x12, .def = 0x08 },
+ { .reg = 0x13, .def = 0x12 },
+ { .reg = 0x14, .def = 0x09 },
+ { .reg = 0x15, .def = 0x00 },
+ { .reg = 0x20, .def = 0x7ff },
+ { .reg = 0x21, .def = 0x180 },
+ { .reg = 0x22, .def = 0x180 },
+ { .reg = 0x23, .def = 0x00 },
+ { .reg = 0x24, .def = 0x80 },
+ { .reg = 0x25, .def = 0x180 },
+ { .reg = 0x26, .def = 0x640 },
+ { .reg = 0x27, .def = 0x180 },
+ { .reg = 0x63, .def = 0x5e },
+ { .reg = 0x65, .def = 0x66 },
+ { .reg = 0x6c, .def = 0xe0 },
+ { .reg = 0xf8, .def = 0x44 },
+};
+
static const struct regmap_config rt9120_regmap_config = {
.reg_bits = 8,
.val_bits = 32,
.max_register = RT9120_REG_DIGCFG,
+ .reg_defaults = rt9120_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt9120_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
.reg_read = rt9120_reg_read,
.reg_write = rt9120_reg_write,
+ .volatile_reg = rt9120_volatile_reg,
.wr_table = &rt9120_wr_table,
.rd_table = &rt9120_rd_table,
};
@@ -449,7 +504,6 @@ static int rt9120_do_register_reset(struct rt9120_data *data)
static int rt9120_probe(struct i2c_client *i2c)
{
struct rt9120_data *data;
- struct gpio_desc *pwdnn_gpio;
struct regulator *dvdd_supply;
int dvdd_supply_volt, ret;
@@ -460,12 +514,12 @@ static int rt9120_probe(struct i2c_client *i2c)
data->dev = &i2c->dev;
i2c_set_clientdata(i2c, data);
- pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
- GPIOD_OUT_HIGH);
- if (IS_ERR(pwdnn_gpio)) {
+ data->pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->pwdnn_gpio)) {
dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n");
- return PTR_ERR(pwdnn_gpio);
- } else if (pwdnn_gpio) {
+ return PTR_ERR(data->pwdnn_gpio);
+ } else if (data->pwdnn_gpio) {
dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n");
msleep(RT9120_CHIPON_WAITMS);
}
@@ -507,11 +561,55 @@ static int rt9120_probe(struct i2c_client *i2c)
}
}
+ pm_runtime_set_autosuspend_delay(&i2c->dev, 1000);
+ pm_runtime_use_autosuspend(&i2c->dev);
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_mark_last_busy(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+
return devm_snd_soc_register_component(&i2c->dev,
&rt9120_component_driver,
&rt9120_dai, 1);
}
+static int rt9120_remove(struct i2c_client *i2c)
+{
+ pm_runtime_disable(&i2c->dev);
+ pm_runtime_set_suspended(&i2c->dev);
+ return 0;
+}
+
+static int __maybe_unused rt9120_runtime_suspend(struct device *dev)
+{
+ struct rt9120_data *data = dev_get_drvdata(dev);
+
+ if (data->pwdnn_gpio) {
+ regcache_cache_only(data->regmap, true);
+ regcache_mark_dirty(data->regmap);
+ gpiod_set_value(data->pwdnn_gpio, 0);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused rt9120_runtime_resume(struct device *dev)
+{
+ struct rt9120_data *data = dev_get_drvdata(dev);
+
+ if (data->pwdnn_gpio) {
+ gpiod_set_value(data->pwdnn_gpio, 1);
+ msleep(RT9120_CHIPON_WAITMS);
+ regcache_cache_only(data->regmap, false);
+ regcache_sync(data->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt9120_pm_ops = {
+ SET_RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL)
+};
+
static const struct of_device_id __maybe_unused rt9120_device_table[] = {
{ .compatible = "richtek,rt9120", },
{ }
@@ -522,8 +620,10 @@ static struct i2c_driver rt9120_driver = {
.driver = {
.name = "rt9120",
.of_match_table = rt9120_device_table,
+ .pm = &rt9120_pm_ops,
},
.probe_new = rt9120_probe,
+ .remove = rt9120_remove,
};
module_i2c_driver(rt9120_driver);
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index 7c612aaf31c7..288b55368d2a 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -38,6 +38,7 @@ static void sdw_mockup_component_remove(struct snd_soc_component *component)
static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
.probe = sdw_mockup_component_probe,
.remove = sdw_mockup_component_remove,
+ .endianness = 1,
};
static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 8eebf27d0ea2..2aa48aef6a97 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1579,8 +1579,7 @@ static void sgtl5000_fill_defaults(struct i2c_client *client)
}
}
-static int sgtl5000_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int sgtl5000_i2c_probe(struct i2c_client *client)
{
struct sgtl5000_priv *sgtl5000;
int ret, reg, rev;
@@ -1821,7 +1820,7 @@ static struct i2c_driver sgtl5000_i2c_driver = {
.name = "sgtl5000",
.of_match_table = sgtl5000_dt_ids,
},
- .probe = sgtl5000_i2c_probe,
+ .probe_new = sgtl5000_i2c_probe,
.remove = sgtl5000_i2c_remove,
.id_table = sgtl5000_id,
};
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 09449c6c4024..83acbdbb8e0d 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -735,8 +735,7 @@ static const struct regmap_config ssm2518_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
};
-static int ssm2518_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ssm2518_i2c_probe(struct i2c_client *i2c)
{
struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
struct ssm2518 *ssm2518;
@@ -815,7 +814,7 @@ static struct i2c_driver ssm2518_driver = {
.name = "ssm2518",
.of_match_table = of_match_ptr(ssm2518_dt_ids),
},
- .probe = ssm2518_i2c_probe,
+ .probe_new = 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 afab81383d3a..3c85772901f5 100644
--- a/sound/soc/codecs/ssm2602-i2c.c
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -13,15 +13,17 @@
#include "ssm2602.h"
+static const struct i2c_device_id ssm2602_i2c_id[];
+
/*
* ssm2602 2 wire address is determined by GPIO5
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
-static int ssm2602_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ssm2602_i2c_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client);
return ssm2602_probe(&client->dev, id->driver_data,
devm_regmap_init_i2c(client, &ssm2602_regmap_config));
}
@@ -47,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = {
.name = "ssm2602",
.of_match_table = ssm2602_of_match,
},
- .probe = ssm2602_i2c_probe,
+ .probe_new = ssm2602_i2c_probe,
.id_table = ssm2602_i2c_id,
};
module_i2c_driver(ssm2602_i2c_driver);
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 811b1a2c404a..08ced09ef001 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -444,8 +444,7 @@ static const struct regmap_config ssm4567_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
};
-static int ssm4567_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ssm4567_i2c_probe(struct i2c_client *i2c)
{
struct ssm4567 *ssm4567;
int ret;
@@ -502,7 +501,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 = ssm4567_i2c_probe,
+ .probe_new = 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 86528b930de8..8585cbef4c9b 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -48,12 +48,9 @@
SNDRV_PCM_RATE_192000)
#define STA32X_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
/* Power-up register defaults */
static const struct reg_default sta32x_regs[] = {
@@ -1094,8 +1091,7 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
}
#endif
-static int sta32x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta32x_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct sta32x_priv *sta32x;
@@ -1175,7 +1171,7 @@ static struct i2c_driver sta32x_i2c_driver = {
.name = "sta32x",
.of_match_table = of_match_ptr(st32x_dt_ids),
},
- .probe = sta32x_i2c_probe,
+ .probe_new = sta32x_i2c_probe,
.id_table = sta32x_i2c_id,
};
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 75d3b0618ab5..9189fb3648f7 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -48,12 +48,9 @@
SNDRV_PCM_RATE_192000)
#define STA350_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
/* Power-up register defaults */
static const struct reg_default sta350_regs[] = {
@@ -1187,8 +1184,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
}
#endif
-static int sta350_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta350_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct sta350_priv *sta350;
@@ -1263,7 +1259,7 @@ static struct i2c_driver sta350_i2c_driver = {
.name = "sta350",
.of_match_table = of_match_ptr(st350_dt_ids),
},
- .probe = sta350_i2c_probe,
+ .probe_new = 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 97b5f34027c0..d90e5512a731 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -337,8 +337,7 @@ static const struct regmap_config sta529_regmap = {
.num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults),
};
-static int sta529_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta529_i2c_probe(struct i2c_client *i2c)
{
struct sta529 *sta529;
int ret;
@@ -381,7 +380,7 @@ static struct i2c_driver sta529_i2c_driver = {
.name = "sta529",
.of_match_table = sta529_of_match,
},
- .probe = sta529_i2c_probe,
+ .probe_new = sta529_i2c_probe,
.id_table = sta529_i2c_id,
};
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index 700baa6314aa..b5c9c61ff5a8 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -681,8 +681,7 @@ static const struct regmap_config tas2552_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int tas2552_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas2552_probe(struct i2c_client *client)
{
struct device *dev;
struct tas2552_data *data;
@@ -764,7 +763,7 @@ static struct i2c_driver tas2552_i2c_driver = {
.of_match_table = of_match_ptr(tas2552_of_match),
.pm = &tas2552_pm,
},
- .probe = tas2552_probe,
+ .probe_new = 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 10302552195e..e62a3da16aed 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -754,17 +754,27 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
return ret;
}
-static int tas2562_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct i2c_device_id tas2562_id[] = {
+ { "tas2562", TAS2562 },
+ { "tas2563", TAS2563 },
+ { "tas2564", TAS2564 },
+ { "tas2110", TAS2110 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2562_id);
+
+static int tas2562_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct tas2562_data *data;
int ret;
+ const struct i2c_device_id *id;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
+ id = i2c_match_id(tas2562_id, client);
data->client = client;
data->dev = &client->dev;
data->model_id = id->driver_data;
@@ -792,15 +802,6 @@ static int tas2562_probe(struct i2c_client *client,
}
-static const struct i2c_device_id tas2562_id[] = {
- { "tas2562", TAS2562 },
- { "tas2563", TAS2563 },
- { "tas2564", TAS2564 },
- { "tas2110", TAS2110 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tas2562_id);
-
#ifdef CONFIG_OF
static const struct of_device_id tas2562_of_match[] = {
{ .compatible = "ti,tas2562", },
@@ -817,7 +818,7 @@ static struct i2c_driver tas2562_i2c_driver = {
.name = "tas2562",
.of_match_table = of_match_ptr(tas2562_of_match),
},
- .probe = tas2562_probe,
+ .probe_new = tas2562_probe,
.id_table = tas2562_id,
};
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 9265af41c235..d395feffb30b 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -621,8 +621,7 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
return 0;
}
-static int tas2764_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas2764_i2c_probe(struct i2c_client *client)
{
struct tas2764_priv *tas2764;
int result;
@@ -678,7 +677,7 @@ static struct i2c_driver tas2764_i2c_driver = {
.name = "tas2764",
.of_match_table = of_match_ptr(tas2764_of_match),
},
- .probe = tas2764_i2c_probe,
+ .probe_new = 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 c5ea3b115966..c1dbd978d550 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -672,8 +672,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
return 0;
}
-static int tas2770_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas2770_i2c_probe(struct i2c_client *client)
{
struct tas2770_priv *tas2770;
int result;
@@ -739,7 +738,7 @@ static struct i2c_driver tas2770_i2c_driver = {
.name = "tas2770",
.of_match_table = of_match_ptr(tas2770_of_match),
},
- .probe = tas2770_i2c_probe,
+ .probe_new = tas2770_i2c_probe,
.id_table = tas2770_i2c_id,
};
module_i2c_driver(tas2770_i2c_driver);
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index 7831c96d0d83..5c0df3cd4832 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -911,8 +911,7 @@ static const struct regmap_config tas5086_regmap = {
.reg_write = tas5086_reg_write,
};
-static int tas5086_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tas5086_i2c_probe(struct i2c_client *i2c)
{
struct tas5086_private *priv;
struct device *dev = &i2c->dev;
@@ -994,7 +993,7 @@ static struct i2c_driver tas5086_i2c_driver = {
.of_match_table = of_match_ptr(tas5086_dt_ids),
},
.id_table = tas5086_i2c_id,
- .probe = tas5086_i2c_probe,
+ .probe_new = tas5086_i2c_probe,
.remove = tas5086_i2c_remove,
};
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index a3e682376946..7b599664db20 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -774,9 +774,9 @@ static struct snd_soc_dai_driver tas571x_dai = {
};
static const struct of_device_id tas571x_of_match[] __maybe_unused;
+static const struct i2c_device_id tas571x_i2c_id[];
-static int tas571x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas571x_i2c_probe(struct i2c_client *client)
{
struct tas571x_private *priv;
struct device *dev = &client->dev;
@@ -791,8 +791,11 @@ static int tas571x_i2c_probe(struct i2c_client *client,
of_id = of_match_device(tas571x_of_match, dev);
if (of_id)
priv->chip = of_id->data;
- else
+ else {
+ const struct i2c_device_id *id =
+ i2c_match_id(tas571x_i2c_id, client);
priv->chip = (void *) id->driver_data;
+ }
priv->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
@@ -830,7 +833,8 @@ static int tas571x_i2c_probe(struct i2c_client *client,
if (IS_ERR(priv->pdn_gpio)) {
dev_err(dev, "error requesting pdn_gpio: %ld\n",
PTR_ERR(priv->pdn_gpio));
- return PTR_ERR(priv->pdn_gpio);
+ ret = PTR_ERR(priv->pdn_gpio);
+ goto disable_regs;
}
priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -838,7 +842,8 @@ static int tas571x_i2c_probe(struct i2c_client *client,
if (IS_ERR(priv->reset_gpio)) {
dev_err(dev, "error requesting reset_gpio: %ld\n",
PTR_ERR(priv->reset_gpio));
- return PTR_ERR(priv->reset_gpio);
+ ret = PTR_ERR(priv->reset_gpio);
+ goto disable_regs;
} else if (priv->reset_gpio) {
/* pulse the active low reset line for ~100us */
usleep_range(100, 200);
@@ -914,7 +919,7 @@ static struct i2c_driver tas571x_i2c_driver = {
.name = "tas571x",
.of_match_table = of_match_ptr(tas571x_of_match),
},
- .probe = tas571x_i2c_probe,
+ .probe_new = 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 9ff644ddb470..17034abef568 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -633,12 +633,19 @@ static struct snd_soc_dai_driver tas5720_dai[] = {
},
};
-static int tas5720_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct i2c_device_id tas5720_id[] = {
+ { "tas5720", TAS5720 },
+ { "tas5722", TAS5722 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5720_id);
+
+static int tas5720_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct tas5720_data *data;
const struct regmap_config *regmap_config;
+ const struct i2c_device_id *id;
int ret;
int i;
@@ -646,6 +653,7 @@ static int tas5720_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
+ id = i2c_match_id(tas5720_id, client);
data->tas5720_client = client;
data->devtype = id->driver_data;
@@ -704,13 +712,6 @@ static int tas5720_probe(struct i2c_client *client,
return 0;
}
-static const struct i2c_device_id tas5720_id[] = {
- { "tas5720", TAS5720 },
- { "tas5722", TAS5722 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tas5720_id);
-
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id tas5720_of_match[] = {
{ .compatible = "ti,tas5720", },
@@ -725,7 +726,7 @@ static struct i2c_driver tas5720_i2c_driver = {
.name = "tas5720",
.of_match_table = of_match_ptr(tas5720_of_match),
},
- .probe = tas5720_probe,
+ .probe_new = tas5720_probe,
.id_table = tas5720_id,
};
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
index 59543d392110..22b53856e691 100644
--- a/sound/soc/codecs/tas6424.c
+++ b/sound/soc/codecs/tas6424.c
@@ -682,8 +682,7 @@ static const struct of_device_id tas6424_of_ids[] = {
MODULE_DEVICE_TABLE(of, tas6424_of_ids);
#endif
-static int tas6424_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas6424_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct tas6424_data *tas6424;
@@ -757,7 +756,7 @@ static int tas6424_i2c_probe(struct i2c_client *client,
TAS6424_RESET, TAS6424_RESET);
if (ret) {
dev_err(dev, "unable to reset device: %d\n", ret);
- return ret;
+ goto disable_regs;
}
INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work);
@@ -766,10 +765,14 @@ static int tas6424_i2c_probe(struct i2c_client *client,
tas6424_dai, ARRAY_SIZE(tas6424_dai));
if (ret < 0) {
dev_err(dev, "unable to register codec: %d\n", ret);
- return ret;
+ goto disable_regs;
}
return 0;
+
+disable_regs:
+ regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies);
+ return ret;
}
static int tas6424_i2c_remove(struct i2c_client *client)
@@ -786,10 +789,8 @@ static int tas6424_i2c_remove(struct i2c_client *client)
ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
tas6424->supplies);
- if (ret < 0) {
+ if (ret < 0)
dev_err(dev, "unable to disable supplies: %d\n", ret);
- return ret;
- }
return 0;
}
@@ -805,7 +806,7 @@ static struct i2c_driver tas6424_i2c_driver = {
.name = "tas6424",
.of_match_table = of_match_ptr(tas6424_of_ids),
},
- .probe = tas6424_i2c_probe,
+ .probe_new = 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 83d220054c96..d964e5207569 100644
--- a/sound/soc/codecs/tda7419.c
+++ b/sound/soc/codecs/tda7419.c
@@ -571,8 +571,7 @@ static const struct snd_soc_component_driver tda7419_component_driver = {
.num_dapm_routes = ARRAY_SIZE(tda7419_dapm_routes),
};
-static int tda7419_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tda7419_probe(struct i2c_client *i2c)
{
struct tda7419_data *tda7419;
int i, ret;
@@ -630,7 +629,7 @@ static struct i2c_driver tda7419_driver = {
.name = "tda7419",
.of_match_table = tda7419_of_match,
},
- .probe = tda7419_probe,
+ .probe_new = tda7419_probe,
.id_table = tda7419_i2c_id,
};
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
index ae18982ac310..82532ad00c3c 100644
--- a/sound/soc/codecs/tlv320adc3xxx.c
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -1152,20 +1152,20 @@ static int adc3xxx_hw_params(struct snd_pcm_substream *substream,
return i;
/* select data word length */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_width(params)) {
+ case 16:
iface_len = ADC3XXX_IFACE_16BITS;
width = 16;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
+ case 20:
iface_len = ADC3XXX_IFACE_20BITS;
width = 20;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
+ case 24:
iface_len = ADC3XXX_IFACE_24BITS;
width = 24;
break;
- case SNDRV_PCM_FORMAT_S32_LE:
+ case 32:
iface_len = ADC3XXX_IFACE_32BITS;
width = 32;
break;
@@ -1335,13 +1335,21 @@ static const struct snd_soc_component_driver soc_component_dev_adc3xxx = {
.num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets),
.dapm_routes = adc3xxx_intercon,
.num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon),
+ .endianness = 1,
};
-static int adc3xxx_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id adc3xxx_i2c_id[] = {
+ { "tlv320adc3001", ADC3001 },
+ { "tlv320adc3101", ADC3101 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id);
+
+static int adc3xxx_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct adc3xxx *adc3xxx = NULL;
+ const struct i2c_device_id *id;
int ret;
adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL);
@@ -1394,6 +1402,7 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, adc3xxx);
+ id = i2c_match_id(adc3xxx_i2c_id, i2c);
adc3xxx->type = id->driver_data;
/* Reset codec chip */
@@ -1436,19 +1445,12 @@ static const struct of_device_id tlv320adc3xxx_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match);
-static const struct i2c_device_id adc3xxx_i2c_id[] = {
- { "tlv320adc3001", ADC3001 },
- { "tlv320adc3101", ADC3101 },
- {}
-};
-MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id);
-
static struct i2c_driver adc3xxx_i2c_driver = {
.driver = {
.name = "tlv320adc3xxx-codec",
.of_match_table = tlv320adc3xxx_of_match,
},
- .probe = adc3xxx_i2c_probe,
+ .probe_new = adc3xxx_i2c_probe,
.remove = adc3xxx_i2c_remove,
.id_table = adc3xxx_i2c_id,
};
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 32b120d624b2..b55f0b836932 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -1083,8 +1083,14 @@ static const struct of_device_id tlv320adcx140_of_match[] = {
MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match);
#endif
-static int adcx140_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static void adcx140_disable_regulator(void *arg)
+{
+ struct adcx140_priv *adcx140 = arg;
+
+ regulator_disable(adcx140->supply_areg);
+}
+
+static int adcx140_i2c_probe(struct i2c_client *i2c)
{
struct adcx140_priv *adcx140;
int ret;
@@ -1113,6 +1119,10 @@ static int adcx140_i2c_probe(struct i2c_client *i2c,
dev_err(adcx140->dev, "Failed to enable areg\n");
return ret;
}
+
+ ret = devm_add_action_or_reset(&i2c->dev, adcx140_disable_regulator, adcx140);
+ if (ret)
+ return ret;
}
adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap);
@@ -1143,7 +1153,7 @@ static struct i2c_driver adcx140_i2c_driver = {
.name = "tlv320adcx140-codec",
.of_match_table = of_match_ptr(tlv320adcx140_of_match),
},
- .probe = adcx140_i2c_probe,
+ .probe_new = 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 dbb8f969274c..1f97673a1cc0 100644
--- a/sound/soc/codecs/tlv320aic23-i2c.c
+++ b/sound/soc/codecs/tlv320aic23-i2c.c
@@ -16,8 +16,7 @@
#include "tlv320aic23.h"
-static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static int tlv320aic23_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -48,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = {
.name = "tlv320aic23-codec",
.of_match_table = of_match_ptr(tlv320aic23_of_match),
},
- .probe = tlv320aic23_i2c_probe,
+ .probe_new = tlv320aic23_i2c_probe,
.id_table = tlv320aic23_id,
};
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 8331dc26bcd2..b2e59581c17a 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1628,11 +1628,24 @@ static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
}
}
-static int aic31xx_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id aic31xx_i2c_id[] = {
+ { "tlv320aic310x", AIC3100 },
+ { "tlv320aic311x", AIC3110 },
+ { "tlv320aic3100", AIC3100 },
+ { "tlv320aic3110", AIC3110 },
+ { "tlv320aic3120", AIC3120 },
+ { "tlv320aic3111", AIC3111 },
+ { "tlv320dac3100", DAC3100 },
+ { "tlv320dac3101", DAC3101 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
+
+static int aic31xx_i2c_probe(struct i2c_client *i2c)
{
struct aic31xx_priv *aic31xx;
unsigned int micbias_value = MICBIAS_2_0V;
+ const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c);
int i, ret;
dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
@@ -1729,26 +1742,13 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
ARRAY_SIZE(aic31xx_dai_driver));
}
-static const struct i2c_device_id aic31xx_i2c_id[] = {
- { "tlv320aic310x", AIC3100 },
- { "tlv320aic311x", AIC3110 },
- { "tlv320aic3100", AIC3100 },
- { "tlv320aic3110", AIC3110 },
- { "tlv320aic3120", AIC3120 },
- { "tlv320aic3111", AIC3111 },
- { "tlv320dac3100", DAC3100 },
- { "tlv320dac3101", DAC3101 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
-
static struct i2c_driver aic31xx_i2c_driver = {
.driver = {
.name = "tlv320aic31xx-codec",
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
.acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
},
- .probe = aic31xx_i2c_probe,
+ .probe_new = 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 ed70e3d9baf2..0645239901b1 100644
--- a/sound/soc/codecs/tlv320aic32x4-i2c.c
+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c
@@ -17,9 +17,9 @@
#include "tlv320aic32x4.h"
static const struct of_device_id aic32x4_of_id[];
+static const struct i2c_device_id aic32x4_i2c_id[];
-static int aic32x4_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int aic32x4_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config;
@@ -35,7 +35,10 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
oid = of_match_node(aic32x4_of_id, i2c->dev.of_node);
dev_set_drvdata(&i2c->dev, (void *)oid->data);
- } else if (id) {
+ } else {
+ const struct i2c_device_id *id;
+
+ id = i2c_match_id(aic32x4_i2c_id, i2c);
dev_set_drvdata(&i2c->dev, (void *)id->driver_data);
}
@@ -70,7 +73,7 @@ static struct i2c_driver aic32x4_i2c_driver = {
.name = "tlv320aic32x4",
.of_match_table = aic32x4_of_id,
},
- .probe = aic32x4_i2c_probe,
+ .probe_new = 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 2f272bc3f5da..7bd9ce08bb7b 100644
--- a/sound/soc/codecs/tlv320aic3x-i2c.c
+++ b/sound/soc/codecs/tlv320aic3x-i2c.c
@@ -17,10 +17,21 @@
#include "tlv320aic3x.h"
-static int aic3x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+static const struct i2c_device_id aic3x_i2c_id[] = {
+ { "tlv320aic3x", AIC3X_MODEL_3X },
+ { "tlv320aic33", AIC3X_MODEL_33 },
+ { "tlv320aic3007", AIC3X_MODEL_3007 },
+ { "tlv320aic3104", AIC3X_MODEL_3104 },
+ { "tlv320aic3106", AIC3X_MODEL_3106 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
+
+static int aic3x_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config;
+ const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c);
config = aic3x_regmap;
config.reg_bits = 8;
@@ -37,16 +48,6 @@ static int aic3x_i2c_remove(struct i2c_client *i2c)
return 0;
}
-static const struct i2c_device_id aic3x_i2c_id[] = {
- { "tlv320aic3x", AIC3X_MODEL_3X },
- { "tlv320aic33", AIC3X_MODEL_33 },
- { "tlv320aic3007", AIC3X_MODEL_3007 },
- { "tlv320aic3104", AIC3X_MODEL_3104 },
- { "tlv320aic3106", AIC3X_MODEL_3106 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
-
static const struct of_device_id aic3x_of_id[] = {
{ .compatible = "ti,tlv320aic3x", },
{ .compatible = "ti,tlv320aic33" },
@@ -62,7 +63,7 @@ static struct i2c_driver aic3x_i2c_driver = {
.name = "tlv320aic3x",
.of_match_table = aic3x_of_id,
},
- .probe = aic3x_i2c_probe,
+ .probe_new = 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 48572d66cb3b..66f1d1cd6cf0 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1463,8 +1463,7 @@ static struct snd_soc_dai_driver dac33_dai = {
.ops = &dac33_dai_ops,
};
-static int dac33_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int dac33_i2c_probe(struct i2c_client *client)
{
struct tlv320dac33_platform_data *pdata;
struct tlv320dac33_priv *dac33;
@@ -1566,7 +1565,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = {
.driver = {
.name = "tlv320dac33-codec",
},
- .probe = dac33_i2c_probe,
+ .probe_new = 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 e2d7ae615c52..8c00db32996b 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -209,13 +209,20 @@ static const struct regmap_config tpa6130a2_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int tpa6130a2_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct i2c_device_id tpa6130a2_id[] = {
+ { "tpa6130a2", TPA6130A2 },
+ { "tpa6140a2", TPA6140A2 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+static int tpa6130a2_probe(struct i2c_client *client)
{
struct device *dev;
struct tpa6130a2_data *data;
struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
struct device_node *np = client->dev.of_node;
+ const struct i2c_device_id *id;
const char *regulator;
unsigned int version;
int ret;
@@ -244,6 +251,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
+ id = i2c_match_id(tpa6130a2_id, client);
data->id = id->driver_data;
if (data->power_gpio >= 0) {
@@ -297,13 +305,6 @@ static int tpa6130a2_probe(struct i2c_client *client,
&tpa6130a2_component_driver, NULL, 0);
}
-static const struct i2c_device_id tpa6130a2_id[] = {
- { "tpa6130a2", TPA6130A2 },
- { "tpa6140a2", TPA6140A2 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
-
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id tpa6130a2_of_match[] = {
{ .compatible = "ti,tpa6130a2", },
@@ -318,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
.name = "tpa6130a2",
.of_match_table = of_match_ptr(tpa6130a2_of_match),
},
- .probe = tpa6130a2_probe,
+ .probe_new = tpa6130a2_probe,
.id_table = tpa6130a2_id,
};
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 962f5d48378a..d8ab0810fceb 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -282,8 +282,7 @@ static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e,
return 0;
}
-static int ts3a227e_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ts3a227e_i2c_probe(struct i2c_client *i2c)
{
struct ts3a227e *ts3a227e;
struct device *dev = &i2c->dev;
@@ -389,7 +388,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 = ts3a227e_i2c_probe,
+ .probe_new = 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 5b63e017a43b..4fb0bb01bcdc 100644
--- a/sound/soc/codecs/tscs42xx.c
+++ b/sound/soc/codecs/tscs42xx.c
@@ -1409,8 +1409,7 @@ static const struct reg_sequence tscs42xx_patch[] = {
static char const * const src_names[TSCS42XX_PLL_SRC_CNT] = {
"xtal", "mclk1", "mclk2"};
-static int tscs42xx_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tscs42xx_i2c_probe(struct i2c_client *i2c)
{
struct tscs42xx *tscs42xx;
int src;
@@ -1505,7 +1504,7 @@ static struct i2c_driver tscs42xx_i2c_driver = {
.name = "tscs42xx",
.of_match_table = tscs42xx_of_match,
},
- .probe = tscs42xx_i2c_probe,
+ .probe_new = tscs42xx_i2c_probe,
.id_table = tscs42xx_i2c_id,
};
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
index 7e1826d6f06f..38622bc247fc 100644
--- a/sound/soc/codecs/tscs454.c
+++ b/sound/soc/codecs/tscs454.c
@@ -3120,18 +3120,17 @@ static int set_aif_sample_format(struct snd_soc_component *component,
unsigned int width;
int ret;
- switch (format) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (snd_pcm_format_width(format)) {
+ case 16:
width = FV_WL_16;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
+ case 20:
width = FV_WL_20;
break;
- case SNDRV_PCM_FORMAT_S24_3LE:
+ case 24:
width = FV_WL_24;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- case SNDRV_PCM_FORMAT_S32_LE:
+ case 32:
width = FV_WL_32;
break;
default:
@@ -3326,6 +3325,7 @@ static const struct snd_soc_component_driver soc_component_dev_tscs454 = {
.num_dapm_routes = ARRAY_SIZE(tscs454_intercon),
.controls = tscs454_snd_controls,
.num_controls = ARRAY_SIZE(tscs454_snd_controls),
+ .endianness = 1,
};
#define TSCS454_RATES SNDRV_PCM_RATE_8000_96000
@@ -3400,8 +3400,7 @@ static struct snd_soc_dai_driver tscs454_dais[] = {
static char const * const src_names[] = {
"xtal", "mclk1", "mclk2", "bclk"};
-static int tscs454_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int tscs454_i2c_probe(struct i2c_client *i2c)
{
struct tscs454 *tscs454;
int src;
@@ -3474,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = {
.name = "tscs454",
.of_match_table = tscs454_of_match,
},
- .probe = tscs454_i2c_probe,
+ .probe_new = tscs454_i2c_probe,
.id_table = tscs454_i2c_id,
};
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 13060a9a2388..b5004842520b 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -739,8 +739,7 @@ static const struct snd_soc_component_driver soc_component_dev_uda1380 = {
.non_legacy_dai_naming = 1,
};
-static int uda1380_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int uda1380_i2c_probe(struct i2c_client *i2c)
{
struct uda1380_platform_data *pdata = i2c->dev.platform_data;
struct uda1380_priv *uda1380;
@@ -800,7 +799,7 @@ static struct i2c_driver uda1380_i2c_driver = {
.name = "uda1380-codec",
.of_match_table = uda1380_of_match,
},
- .probe = uda1380_i2c_probe,
+ .probe_new = uda1380_i2c_probe,
.id_table = uda1380_i2c_id,
};
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 1e60db4056ad..617a36a89dfe 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -4924,6 +4924,7 @@ static const struct snd_soc_component_driver wcd9335_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(wcd9335_dapm_widgets),
.dapm_routes = wcd9335_audio_map,
.num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map),
+ .endianness = 1,
};
static int wcd9335_probe(struct wcd9335_codec *wcd)
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 6298ebe96e94..f56907d0942d 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -5847,6 +5847,7 @@ static const struct snd_soc_component_driver wcd934x_component_drv = {
.dapm_routes = wcd934x_audio_map,
.num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map),
.set_jack = wcd934x_codec_set_jack,
+ .endianness = 1,
};
static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index 898b2887fa63..c1b61b997f69 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -4168,6 +4168,7 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd938x = {
.dapm_routes = wcd938x_audio_map,
.num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map),
.set_jack = wcd938x_codec_set_jack,
+ .endianness = 1,
};
static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd)
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index d6ffe99550fe..b6366dea15a6 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -192,8 +192,7 @@ static void wm1250_ev1_free(struct i2c_client *i2c)
gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
}
-static int wm1250_ev1_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static int wm1250_ev1_probe(struct i2c_client *i2c)
{
int id, board, rev, ret;
@@ -247,7 +246,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = {
.driver = {
.name = "wm1250-ev1",
},
- .probe = wm1250_ev1_probe,
+ .probe_new = 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 72e165cc6443..ede5f2a982a6 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -536,7 +536,7 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
{
struct i2c_client *i2c = wm2000->i2c;
int i, j;
- int ret;
+ int ret = 0;
if (wm2000->anc_mode == mode)
return 0;
@@ -566,13 +566,13 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
ret = anc_transitions[i].step[j](i2c,
anc_transitions[i].analogue);
if (ret != 0)
- return ret;
+ break;
}
if (anc_transitions[i].dest == ANC_OFF)
clk_disable_unprepare(wm2000->mclk);
- return 0;
+ return ret;
}
static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
@@ -803,12 +803,10 @@ static const struct snd_soc_component_driver soc_component_dev_wm2000 = {
.num_dapm_routes = ARRAY_SIZE(wm2000_audio_map),
.idle_bias_on = 1,
.use_pmdown_time = 1,
- .endianness = 1,
.non_legacy_dai_naming = 1,
};
-static int wm2000_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+static int wm2000_i2c_probe(struct i2c_client *i2c)
{
struct wm2000_priv *wm2000;
struct wm2000_platform_data *pdata;
@@ -941,7 +939,7 @@ static struct i2c_driver wm2000_i2c_driver = {
.driver = {
.name = "wm2000",
},
- .probe = wm2000_i2c_probe,
+ .probe_new = wm2000_i2c_probe,
.id_table = wm2000_i2c_id,
};
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 8863b533f9c4..1cd544580c83 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2176,8 +2176,7 @@ static const unsigned int wm2200_mic_ctrl_reg[] = {
WM2200_IN3L_CONTROL,
};
-static int wm2200_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm2200_i2c_probe(struct i2c_client *i2c)
{
struct wm2200_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm2200_priv *wm2200;
@@ -2489,7 +2488,7 @@ static struct i2c_driver wm2200_i2c_driver = {
.name = "wm2200",
.pm = &wm2200_pm,
},
- .probe = wm2200_i2c_probe,
+ .probe_new = 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 9cab01ee4ee9..a89870918174 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2411,8 +2411,7 @@ static const unsigned int wm5100_mic_ctrl_reg[] = {
WM5100_IN4L_CONTROL,
};
-static int wm5100_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm5100_i2c_probe(struct i2c_client *i2c)
{
struct wm5100_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm5100_priv *wm5100;
@@ -2713,7 +2712,7 @@ static struct i2c_driver wm5100_i2c_driver = {
.name = "wm5100",
.pm = &wm5100_pm,
},
- .probe = wm5100_i2c_probe,
+ .probe_new = 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 a18e2290b8c8..c6439d25006b 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -646,8 +646,7 @@ static struct spi_driver wm8510_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8510_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8510_i2c_probe(struct i2c_client *i2c)
{
struct wm8510_priv *wm8510;
int ret;
@@ -680,7 +679,7 @@ static struct i2c_driver wm8510_i2c_driver = {
.name = "wm8510",
.of_match_table = wm8510_of_match,
},
- .probe = wm8510_i2c_probe,
+ .probe_new = wm8510_i2c_probe,
.id_table = wm8510_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index c8b50aac6c18..ba35a0221dc8 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -443,8 +443,7 @@ static const struct regmap_config wm8523_regmap = {
.volatile_reg = wm8523_volatile_register,
};
-static int wm8523_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8523_i2c_probe(struct i2c_client *i2c)
{
struct wm8523_priv *wm8523;
unsigned int val;
@@ -529,7 +528,7 @@ static struct i2c_driver wm8523_i2c_driver = {
.name = "wm8523",
.of_match_table = wm8523_of_match,
},
- .probe = wm8523_i2c_probe,
+ .probe_new = wm8523_i2c_probe,
.id_table = wm8523_i2c_id,
};
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 85ad2f03cfd0..84020195314d 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -996,8 +996,7 @@ static const struct of_device_id wm8580_of_match[] = {
};
MODULE_DEVICE_TABLE(of, wm8580_of_match);
-static int wm8580_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8580_i2c_probe(struct i2c_client *i2c)
{
const struct of_device_id *of_id;
struct wm8580_priv *wm8580;
@@ -1051,7 +1050,7 @@ static struct i2c_driver wm8580_i2c_driver = {
.name = "wm8580",
.of_match_table = wm8580_of_match,
},
- .probe = wm8580_i2c_probe,
+ .probe_new = wm8580_i2c_probe,
.id_table = wm8580_i2c_id,
};
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index bc4d161c59e5..b68a1ebcd061 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -432,8 +432,7 @@ static struct spi_driver wm8711_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8711_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int wm8711_i2c_probe(struct i2c_client *client)
{
struct wm8711_priv *wm8711;
int ret;
@@ -466,7 +465,7 @@ static struct i2c_driver wm8711_i2c_driver = {
.name = "wm8711",
.of_match_table = wm8711_of_match,
},
- .probe = wm8711_i2c_probe,
+ .probe_new = wm8711_i2c_probe,
.id_table = wm8711_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 2cd58d133899..119ff0a1bb35 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -273,8 +273,7 @@ static struct spi_driver wm8728_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8728_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8728_i2c_probe(struct i2c_client *i2c)
{
struct wm8728_priv *wm8728;
int ret;
@@ -307,7 +306,7 @@ static struct i2c_driver wm8728_i2c_driver = {
.name = "wm8728",
.of_match_table = wm8728_of_match,
},
- .probe = wm8728_i2c_probe,
+ .probe_new = 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
new file mode 100644
index 000000000000..fdf03bf91606
--- /dev/null
+++ b/sound/soc/codecs/wm8731-i2c.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8731-i2c.c -- WM8731 ALSA SoC Audio driver I2C code
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include "wm8731.h"
+
+
+static const struct of_device_id wm8731_of_match[] = {
+ { .compatible = "wlf,wm8731", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static int wm8731_i2c_probe(struct i2c_client *i2c)
+{
+ struct wm8731_priv *wm8731;
+ int ret;
+
+ wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
+ GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8731);
+
+ wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
+ if (IS_ERR(wm8731->regmap)) {
+ ret = PTR_ERR(wm8731->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return wm8731_init(&i2c->dev, wm8731);
+}
+
+static const struct i2c_device_id wm8731_i2c_id[] = {
+ { "wm8731", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
+
+static struct i2c_driver wm8731_i2c_driver = {
+ .driver = {
+ .name = "wm8731",
+ .of_match_table = wm8731_of_match,
+ },
+ .probe_new = wm8731_i2c_probe,
+ .id_table = wm8731_i2c_id,
+};
+
+module_i2c_driver(wm8731_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver - I2C");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c
new file mode 100644
index 000000000000..542ed097d89a
--- /dev/null
+++ b/sound/soc/codecs/wm8731-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * wm8731.c -- WM8731 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include "wm8731.h"
+
+static const struct of_device_id wm8731_of_match[] = {
+ { .compatible = "wlf,wm8731", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static int wm8731_spi_probe(struct spi_device *spi)
+{
+ struct wm8731_priv *wm8731;
+ int ret;
+
+ wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, wm8731);
+
+ wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
+ if (IS_ERR(wm8731->regmap)) {
+ ret = PTR_ERR(wm8731->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return wm8731_init(&spi->dev, wm8731);
+}
+
+static struct spi_driver wm8731_spi_driver = {
+ .driver = {
+ .name = "wm8731",
+ .of_match_table = wm8731_of_match,
+ },
+ .probe = wm8731_spi_probe,
+};
+
+module_spi_driver(wm8731_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver - SPI");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index b14c6d104e6d..2408c4a591d5 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -15,13 +15,9 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
-#include <linux/spi/spi.h>
-#include <linux/of_device.h>
-#include <linux/mutex.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -32,7 +28,6 @@
#include "wm8731.h"
-#define WM8731_NUM_SUPPLIES 4
static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
"AVDD",
"HPVDD",
@@ -40,21 +35,6 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
"DBVDD",
};
-/* codec private data */
-struct wm8731_priv {
- struct regmap *regmap;
- struct clk *mclk;
- struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
- const struct snd_pcm_hw_constraint_list *constraints;
- unsigned int sysclk;
- int sysclk_type;
- int playback_fs;
- bool deemph;
-
- struct mutex lock;
-};
-
-
/*
* wm8731 register cache
*/
@@ -429,12 +409,11 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_component *component = codec_dai->component;
u16 iface = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -570,59 +549,6 @@ static struct snd_soc_dai_driver wm8731_dai = {
.symmetric_rate = 1,
};
-static int wm8731_request_supplies(struct device *dev,
- struct wm8731_priv *wm8731)
-{
- int ret = 0, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
- wm8731->supplies[i].supply = wm8731_supply_names[i];
-
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies),
- wm8731->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
-
- ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
- wm8731->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to enable supplies: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731)
-{
- int ret = 0;
-
- ret = wm8731_reset(wm8731->regmap);
- if (ret < 0) {
- dev_err(dev, "Failed to issue reset: %d\n", ret);
- goto err;
- }
-
- /* Clear POWEROFF, keep everything else disabled */
- regmap_write(wm8731->regmap, WM8731_PWR, 0x7f);
-
- /* Latch the update bits */
- regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0);
- regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0);
- regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0);
- regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0);
-
- /* Disable bypass path by default */
- regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0);
-
- regcache_mark_dirty(wm8731->regmap);
-
-err:
- return ret;
-}
-
static const struct snd_soc_component_driver soc_component_dev_wm8731 = {
.set_bias_level = wm8731_set_bias_level,
.controls = wm8731_snd_controls,
@@ -638,136 +564,65 @@ static const struct snd_soc_component_driver soc_component_dev_wm8731 = {
.non_legacy_dai_naming = 1,
};
-static const struct of_device_id wm8731_of_match[] = {
- { .compatible = "wlf,wm8731", },
- { }
-};
-
-MODULE_DEVICE_TABLE(of, wm8731_of_match);
-
-static const struct regmap_config wm8731_regmap = {
- .reg_bits = 7,
- .val_bits = 9,
-
- .max_register = WM8731_RESET,
- .volatile_reg = wm8731_volatile,
-
- .cache_type = REGCACHE_RBTREE,
- .reg_defaults = wm8731_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
-};
-
-#if defined(CONFIG_SPI_MASTER)
-static int wm8731_spi_probe(struct spi_device *spi)
+int wm8731_init(struct device *dev, struct wm8731_priv *wm8731)
{
- struct wm8731_priv *wm8731;
- int ret;
-
- wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
- if (wm8731 == NULL)
- return -ENOMEM;
+ int ret = 0, i;
- wm8731->mclk = devm_clk_get(&spi->dev, "mclk");
+ wm8731->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(wm8731->mclk)) {
ret = PTR_ERR(wm8731->mclk);
if (ret == -ENOENT) {
wm8731->mclk = NULL;
- dev_warn(&spi->dev, "Assuming static MCLK\n");
+ dev_warn(dev, "Assuming static MCLK\n");
} else {
- dev_err(&spi->dev, "Failed to get MCLK: %d\n",
- ret);
+ dev_err(dev, "Failed to get MCLK: %d\n", ret);
return ret;
}
}
mutex_init(&wm8731->lock);
- spi_set_drvdata(spi, wm8731);
-
- ret = wm8731_request_supplies(&spi->dev, wm8731);
- if (ret != 0)
- return ret;
+ for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+ wm8731->supplies[i].supply = wm8731_supply_names[i];
- wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
- if (IS_ERR(wm8731->regmap)) {
- ret = PTR_ERR(wm8731->regmap);
- dev_err(&spi->dev, "Failed to allocate register map: %d\n",
- ret);
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
return ret;
}
- ret = wm8731_hw_init(&spi->dev, wm8731);
- if (ret != 0)
- return ret;
-
- ret = devm_snd_soc_register_component(&spi->dev,
- &soc_component_dev_wm8731, &wm8731_dai, 1);
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
if (ret != 0) {
- dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- return 0;
-}
-
-static struct spi_driver wm8731_spi_driver = {
- .driver = {
- .name = "wm8731",
- .of_match_table = wm8731_of_match,
- },
- .probe = wm8731_spi_probe,
-};
-#endif /* CONFIG_SPI_MASTER */
-
-#if IS_ENABLED(CONFIG_I2C)
-static int wm8731_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct wm8731_priv *wm8731;
- int ret;
-
- wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
- GFP_KERNEL);
- if (wm8731 == NULL)
- return -ENOMEM;
-
- wm8731->mclk = devm_clk_get(&i2c->dev, "mclk");
- if (IS_ERR(wm8731->mclk)) {
- ret = PTR_ERR(wm8731->mclk);
- if (ret == -ENOENT) {
- wm8731->mclk = NULL;
- dev_warn(&i2c->dev, "Assuming static MCLK\n");
- } else {
- dev_err(&i2c->dev, "Failed to get MCLK: %d\n",
- ret);
- return ret;
- }
+ ret = wm8731_reset(wm8731->regmap);
+ if (ret < 0) {
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
+ goto err_regulator_enable;
}
- mutex_init(&wm8731->lock);
-
- i2c_set_clientdata(i2c, wm8731);
+ /* Clear POWEROFF, keep everything else disabled */
+ regmap_write(wm8731->regmap, WM8731_PWR, 0x7f);
- ret = wm8731_request_supplies(&i2c->dev, wm8731);
- if (ret != 0)
- return ret;
+ /* Latch the update bits */
+ regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0);
+ regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0);
- wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
- if (IS_ERR(wm8731->regmap)) {
- ret = PTR_ERR(wm8731->regmap);
- dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- ret);
- goto err_regulator_enable;
- }
+ /* Disable bypass path by default */
+ regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0);
- ret = wm8731_hw_init(&i2c->dev, wm8731);
- if (ret != 0)
- goto err_regulator_enable;
+ regcache_mark_dirty(wm8731->regmap);
- ret = devm_snd_soc_register_component(&i2c->dev,
+ ret = devm_snd_soc_register_component(dev,
&soc_component_dev_wm8731, &wm8731_dai, 1);
if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
goto err_regulator_enable;
}
@@ -779,54 +634,20 @@ err_regulator_enable:
return ret;
}
+EXPORT_SYMBOL_GPL(wm8731_init);
-static const struct i2c_device_id wm8731_i2c_id[] = {
- { "wm8731", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
-
-static struct i2c_driver wm8731_i2c_driver = {
- .driver = {
- .name = "wm8731",
- .of_match_table = wm8731_of_match,
- },
- .probe = wm8731_i2c_probe,
- .id_table = wm8731_i2c_id,
-};
-#endif
+const struct regmap_config wm8731_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
-static int __init wm8731_modinit(void)
-{
- int ret = 0;
-#if IS_ENABLED(CONFIG_I2C)
- ret = i2c_add_driver(&wm8731_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n",
- ret);
- }
-#endif
-#if defined(CONFIG_SPI_MASTER)
- ret = spi_register_driver(&wm8731_spi_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n",
- ret);
- }
-#endif
- return ret;
-}
-module_init(wm8731_modinit);
+ .max_register = WM8731_RESET,
+ .volatile_reg = wm8731_volatile,
-static void __exit wm8731_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
- i2c_del_driver(&wm8731_i2c_driver);
-#endif
-#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&wm8731_spi_driver);
-#endif
-}
-module_exit(wm8731_exit);
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8731_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(wm8731_regmap);
MODULE_DESCRIPTION("ASoC WM8731 driver");
MODULE_AUTHOR("Richard Purdie");
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 4fcf1226d7c2..8d5a7a9ca6b2 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -12,6 +12,13 @@
#ifndef _WM8731_H
#define _WM8731_H
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+struct clk;
+struct snd_pcm_hw_constraint_list;
+
/* WM8731 register space */
#define WM8731_LINVOL 0x00
@@ -33,4 +40,24 @@
#define WM8731_DAI 0
+#define WM8731_NUM_SUPPLIES 4
+
+/* codec private data */
+struct wm8731_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+ const struct snd_pcm_hw_constraint_list *constraints;
+ unsigned int sysclk;
+ int sysclk_type;
+ int playback_fs;
+ bool deemph;
+
+ struct mutex lock;
+};
+
+extern const struct regmap_config wm8731_regmap;
+
+int wm8731_init(struct device *dev, struct wm8731_priv *wm8731);
+
#endif
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 7a3f9fbe8d53..5778091d1c09 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -606,8 +606,7 @@ static const struct regmap_config wm8737_regmap = {
};
#if IS_ENABLED(CONFIG_I2C)
-static int wm8737_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8737_i2c_probe(struct i2c_client *i2c)
{
struct wm8737_priv *wm8737;
int ret, i;
@@ -651,7 +650,7 @@ static struct i2c_driver wm8737_i2c_driver = {
.name = "wm8737",
.of_match_table = wm8737_of_match,
},
- .probe = wm8737_i2c_probe,
+ .probe_new = wm8737_i2c_probe,
.id_table = wm8737_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 0e3994326936..871e2c5421b8 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -565,8 +565,7 @@ static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
}
#if IS_ENABLED(CONFIG_I2C)
-static int wm8741_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8741_i2c_probe(struct i2c_client *i2c)
{
struct wm8741_priv *wm8741;
int ret, i;
@@ -618,7 +617,7 @@ static struct i2c_driver wm8741_i2c_driver = {
.name = "wm8741",
.of_match_table = wm8741_of_match,
},
- .probe = wm8741_i2c_probe,
+ .probe_new = wm8741_i2c_probe,
.id_table = wm8741_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 9491817020d8..1426fc1f7c5a 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -780,8 +780,7 @@ static struct spi_driver wm8750_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8750_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8750_i2c_probe(struct i2c_client *i2c)
{
struct wm8750_priv *wm8750;
struct regmap *regmap;
@@ -815,7 +814,7 @@ static struct i2c_driver wm8750_i2c_driver = {
.name = "wm8750",
.of_match_table = wm8750_of_match,
},
- .probe = wm8750_i2c_probe,
+ .probe_new = wm8750_i2c_probe,
.id_table = wm8750_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index deaa54be6268..931134d334ec 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1552,8 +1552,7 @@ static struct spi_driver wm8753_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8753_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8753_i2c_probe(struct i2c_client *i2c)
{
struct wm8753_priv *wm8753;
int ret;
@@ -1592,7 +1591,7 @@ static struct i2c_driver wm8753_i2c_driver = {
.name = "wm8753",
.of_match_table = wm8753_of_match,
},
- .probe = wm8753_i2c_probe,
+ .probe_new = wm8753_i2c_probe,
.id_table = wm8753_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 554acf56130c..f164cb6744c4 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -490,8 +490,7 @@ static struct spi_driver wm8776_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8776_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8776_i2c_probe(struct i2c_client *i2c)
{
struct wm8776_priv *wm8776;
int ret;
@@ -525,7 +524,7 @@ static struct i2c_driver wm8776_i2c_driver = {
.name = "wm8776",
.of_match_table = wm8776_of_match,
},
- .probe = wm8776_i2c_probe,
+ .probe_new = 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 f97a75e64166..04dc9fb5afb4 100644
--- a/sound/soc/codecs/wm8804-i2c.c
+++ b/sound/soc/codecs/wm8804-i2c.c
@@ -14,8 +14,7 @@
#include "wm8804.h"
-static int wm8804_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8804_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
@@ -62,7 +61,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 = wm8804_i2c_probe,
+ .probe_new = 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 bf3a4415a85f..84a3daf0c11e 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1261,8 +1261,7 @@ static struct spi_driver wm8900_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8900_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8900_i2c_probe(struct i2c_client *i2c)
{
struct wm8900_priv *wm8900;
int ret;
@@ -1299,7 +1298,7 @@ static struct i2c_driver wm8900_i2c_driver = {
.driver = {
.name = "wm8900",
},
- .probe = wm8900_i2c_probe,
+ .probe_new = 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 75f30154c809..3c95c2aea515 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1981,8 +1981,7 @@ static int wm8903_set_pdata_from_of(struct i2c_client *i2c,
return 0;
}
-static int wm8903_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8903_i2c_probe(struct i2c_client *i2c)
{
struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8903_priv *wm8903;
@@ -2132,7 +2131,7 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
if (ret != 0) {
dev_err(wm8903->dev, "Failed to request IRQ: %d\n",
ret);
- return ret;
+ goto err;
}
/* Enable write sequencer interrupts */
@@ -2214,7 +2213,7 @@ static struct i2c_driver wm8903_i2c_driver = {
.name = "wm8903",
.of_match_table = wm8903_of_match,
},
- .probe = wm8903_i2c_probe,
+ .probe_new = 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 a02a77fef360..04bb8e392497 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2162,8 +2162,9 @@ static const struct of_device_id wm8904_of_match[] = {
MODULE_DEVICE_TABLE(of, wm8904_of_match);
#endif
-static int wm8904_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id wm8904_i2c_id[];
+
+static int wm8904_i2c_probe(struct i2c_client *i2c)
{
struct wm8904_priv *wm8904;
unsigned int val;
@@ -2197,6 +2198,8 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
return -EINVAL;
wm8904->devtype = (enum wm8904_type)match->data;
} else {
+ const struct i2c_device_id *id =
+ i2c_match_id(wm8904_i2c_id, i2c);
wm8904->devtype = id->driver_data;
}
@@ -2328,7 +2331,7 @@ static struct i2c_driver wm8904_i2c_driver = {
.name = "wm8904",
.of_match_table = of_match_ptr(wm8904_of_match),
},
- .probe = wm8904_i2c_probe,
+ .probe_new = wm8904_i2c_probe,
.id_table = wm8904_i2c_id,
};
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 440d048ef0c0..589394d420ce 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -750,8 +750,7 @@ static const struct regmap_config wm8940_regmap = {
.volatile_reg = wm8940_volatile_register,
};
-static int wm8940_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8940_i2c_probe(struct i2c_client *i2c)
{
struct wm8940_priv *wm8940;
int ret;
@@ -779,11 +778,18 @@ static const struct i2c_device_id wm8940_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
+static const struct of_device_id wm8940_of_match[] = {
+ { .compatible = "wlf,wm8940", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8940_of_match);
+
static struct i2c_driver wm8940_i2c_driver = {
.driver = {
.name = "wm8940",
+ .of_match_table = wm8940_of_match,
},
- .probe = wm8940_i2c_probe,
+ .probe_new = wm8940_i2c_probe,
.id_table = wm8940_i2c_id,
};
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 513df47bd87d..80e3cbd704ee 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -968,8 +968,7 @@ static const struct regmap_config wm8955_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults),
};
-static int wm8955_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8955_i2c_probe(struct i2c_client *i2c)
{
struct wm8955_priv *wm8955;
int ret;
@@ -1005,7 +1004,7 @@ static struct i2c_driver wm8955_i2c_driver = {
.driver = {
.name = "wm8955",
},
- .probe = wm8955_i2c_probe,
+ .probe_new = wm8955_i2c_probe,
.id_table = wm8955_i2c_id,
};
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index ca7660f4bb05..8c8f32b23083 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -14,6 +14,7 @@
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -1410,8 +1411,7 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
ARRAY_SIZE(pdata->hp_cfg));
}
-static int wm8960_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8960_i2c_probe(struct i2c_client *i2c)
{
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8960_priv *wm8960;
@@ -1498,18 +1498,30 @@ static const struct i2c_device_id wm8960_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
+#if defined(CONFIG_OF)
static const struct of_device_id wm8960_of_match[] = {
{ .compatible = "wlf,wm8960", },
{ }
};
MODULE_DEVICE_TABLE(of, wm8960_of_match);
+#endif
+
+#if defined(CONFIG_ACPI)
+static const struct acpi_device_id wm8960_acpi_match[] = {
+ { "1AEC8960", 0 }, /* Wolfson PCI ID + part ID */
+ { "10138960", 0 }, /* Cirrus Logic PCI ID + part ID */
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, wm8960_acpi_match);
+#endif
static struct i2c_driver wm8960_i2c_driver = {
.driver = {
.name = "wm8960",
- .of_match_table = wm8960_of_match,
+ .of_match_table = of_match_ptr(wm8960_of_match),
+ .acpi_match_table = ACPI_PTR(wm8960_acpi_match),
},
- .probe = wm8960_i2c_probe,
+ .probe_new = 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 ef80d9fc1eec..69eb731dbf4b 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -911,8 +911,7 @@ static const struct regmap_config wm8961_regmap = {
.readable_reg = wm8961_readable,
};
-static int wm8961_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8961_i2c_probe(struct i2c_client *i2c)
{
struct wm8961_priv *wm8961;
unsigned int val;
@@ -977,7 +976,7 @@ static struct i2c_driver wm8961_i2c_driver = {
.driver = {
.name = "wm8961",
},
- .probe = wm8961_i2c_probe,
+ .probe_new = wm8961_i2c_probe,
.id_table = wm8961_i2c_id,
};
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 2c41d31956aa..34cd5a2a997c 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2896,9 +2896,8 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
reinit_completion(&wm8962->fll_lock);
- ret = pm_runtime_get_sync(component->dev);
+ ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
- pm_runtime_put_noidle(component->dev);
dev_err(component->dev, "Failed to resume device: %d\n", ret);
return ret;
}
@@ -3030,9 +3029,8 @@ static irqreturn_t wm8962_irq(int irq, void *data)
unsigned int active;
int reg, ret;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
- pm_runtime_put_noidle(dev);
dev_err(dev, "Failed to resume: %d\n", ret);
return IRQ_NONE;
}
@@ -3555,8 +3553,7 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
return PTR_ERR_OR_ZERO(pdata->mclk);
}
-static int wm8962_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8962_i2c_probe(struct i2c_client *i2c)
{
struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm8962_priv *wm8962;
@@ -3892,7 +3889,7 @@ static struct i2c_driver wm8962_i2c_driver = {
.of_match_table = wm8962_of_match,
.pm = &wm8962_pm,
},
- .probe = wm8962_i2c_probe,
+ .probe_new = 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 ddf0e2f5e66a..8a289b048e66 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -672,8 +672,7 @@ static const struct regmap_config wm8971_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int wm8971_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8971_i2c_probe(struct i2c_client *i2c)
{
struct wm8971_priv *wm8971;
@@ -702,7 +701,7 @@ static struct i2c_driver wm8971_i2c_driver = {
.driver = {
.name = "wm8971",
},
- .probe = wm8971_i2c_probe,
+ .probe_new = wm8971_i2c_probe,
.id_table = wm8971_i2c_id,
};
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index fdc68ab49742..a8d7809f3f64 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -685,8 +685,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8974 = {
.non_legacy_dai_naming = 1,
};
-static int wm8974_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8974_i2c_probe(struct i2c_client *i2c)
{
struct wm8974_priv *priv;
struct regmap *regmap;
@@ -725,7 +724,7 @@ static struct i2c_driver wm8974_i2c_driver = {
.name = "wm8974",
.of_match_table = wm8974_of_match,
},
- .probe = wm8974_i2c_probe,
+ .probe_new = wm8974_i2c_probe,
.id_table = wm8974_i2c_id,
};
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 7091e1a9d516..141f50bfec68 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1020,8 +1020,7 @@ static const struct regmap_config wm8978_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults),
};
-static int wm8978_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8978_i2c_probe(struct i2c_client *i2c)
{
struct wm8978_priv *wm8978;
int ret;
@@ -1074,7 +1073,7 @@ static struct i2c_driver wm8978_i2c_driver = {
.name = "wm8978",
.of_match_table = wm8978_of_match,
},
- .probe = wm8978_i2c_probe,
+ .probe_new = wm8978_i2c_probe,
.id_table = wm8978_i2c_id,
};
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index d8ed22a9caac..ae89554d47bc 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -1035,8 +1035,7 @@ static struct spi_driver wm8983_spi_driver = {
#endif
#if IS_ENABLED(CONFIG_I2C)
-static int wm8983_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8983_i2c_probe(struct i2c_client *i2c)
{
struct wm8983_priv *wm8983;
int ret;
@@ -1070,7 +1069,7 @@ static struct i2c_driver wm8983_i2c_driver = {
.driver = {
.name = "wm8983",
},
- .probe = wm8983_i2c_probe,
+ .probe_new = wm8983_i2c_probe,
.id_table = wm8983_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index a7e01106fbc0..cf2c32eac773 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1167,10 +1167,12 @@ static struct spi_driver wm8985_spi_driver = {
#endif
#if IS_ENABLED(CONFIG_I2C)
-static int wm8985_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static const struct i2c_device_id wm8985_i2c_id[];
+
+static int wm8985_i2c_probe(struct i2c_client *i2c)
{
struct wm8985_priv *wm8985;
+ const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c);
int ret;
wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL);
@@ -1205,7 +1207,7 @@ static struct i2c_driver wm8985_i2c_driver = {
.driver = {
.name = "wm8985",
},
- .probe = wm8985_i2c_probe,
+ .probe_new = wm8985_i2c_probe,
.id_table = wm8985_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 1d2f881bbcae..27538d6598cf 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -872,8 +872,7 @@ static struct spi_driver wm8988_spi_driver = {
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
-static int wm8988_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8988_i2c_probe(struct i2c_client *i2c)
{
struct wm8988_priv *wm8988;
int ret;
@@ -907,7 +906,7 @@ static struct i2c_driver wm8988_i2c_driver = {
.driver = {
.name = "wm8988",
},
- .probe = wm8988_i2c_probe,
+ .probe_new = wm8988_i2c_probe,
.id_table = wm8988_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 938940777e5d..c9448a59c872 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1220,8 +1220,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8990 = {
.non_legacy_dai_naming = 1,
};
-static int wm8990_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8990_i2c_probe(struct i2c_client *i2c)
{
struct wm8990_priv *wm8990;
int ret;
@@ -1249,7 +1248,7 @@ static struct i2c_driver wm8990_i2c_driver = {
.driver = {
.name = "wm8990",
},
- .probe = wm8990_i2c_probe,
+ .probe_new = wm8990_i2c_probe,
.id_table = wm8990_i2c_id,
};
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 16bc8609d0d2..998bc89bb7e1 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1257,8 +1257,7 @@ static const struct regmap_config wm8991_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int wm8991_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8991_i2c_probe(struct i2c_client *i2c)
{
struct wm8991_priv *wm8991;
unsigned int val;
@@ -1325,7 +1324,7 @@ static struct i2c_driver wm8991_i2c_driver = {
.driver = {
.name = "wm8991",
},
- .probe = wm8991_i2c_probe,
+ .probe_new = wm8991_i2c_probe,
.id_table = wm8991_i2c_id,
};
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index c4f41692b806..f4da77ec9d6c 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1624,8 +1624,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8993 = {
.non_legacy_dai_naming = 1,
};
-static int wm8993_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8993_i2c_probe(struct i2c_client *i2c)
{
struct wm8993_priv *wm8993;
unsigned int reg;
@@ -1745,7 +1744,7 @@ static struct i2c_driver wm8993_i2c_driver = {
.driver = {
.name = "wm8993",
},
- .probe = wm8993_i2c_probe,
+ .probe_new = 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 b896d9c5bea0..ea9727446707 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2231,8 +2231,7 @@ static struct spi_driver wm8995_spi_driver = {
#endif
#if IS_ENABLED(CONFIG_I2C)
-static int wm8995_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8995_i2c_probe(struct i2c_client *i2c)
{
struct wm8995_priv *wm8995;
int ret;
@@ -2270,7 +2269,7 @@ static struct i2c_driver wm8995_i2c_driver = {
.driver = {
.name = "wm8995",
},
- .probe = wm8995_i2c_probe,
+ .probe_new = wm8995_i2c_probe,
.id_table = wm8995_i2c_id
};
#endif
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 197ae7d84a49..f7bb27d1c76d 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2755,8 +2755,7 @@ static struct snd_soc_dai_driver wm8996_dai[] = {
},
};
-static int wm8996_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8996_i2c_probe(struct i2c_client *i2c)
{
struct wm8996_priv *wm8996;
int ret, i;
@@ -3091,7 +3090,7 @@ static struct i2c_driver wm8996_i2c_driver = {
.driver = {
.name = "wm8996",
},
- .probe = wm8996_i2c_probe,
+ .probe_new = 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 4a667ee82fe2..87b58448cea7 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1299,8 +1299,7 @@ static const struct regmap_config wm9081_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int wm9081_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm9081_i2c_probe(struct i2c_client *i2c)
{
struct wm9081_priv *wm9081;
unsigned int reg;
@@ -1373,7 +1372,7 @@ static struct i2c_driver wm9081_i2c_driver = {
.driver = {
.name = "wm9081",
},
- .probe = wm9081_i2c_probe,
+ .probe_new = 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 e0231a54609c..f7d80f1e37a8 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -543,7 +543,6 @@ static const struct snd_soc_component_driver soc_component_dev_wm9090 = {
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
- .endianness = 1,
.non_legacy_dai_naming = 1,
};
@@ -561,8 +560,7 @@ static const struct regmap_config wm9090_regmap = {
};
-static int wm9090_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm9090_i2c_probe(struct i2c_client *i2c)
{
struct wm9090_priv *wm9090;
unsigned int reg;
@@ -619,7 +617,7 @@ static struct i2c_driver wm9090_i2c_driver = {
.driver = {
.name = "wm9090",
},
- .probe = wm9090_i2c_probe,
+ .probe_new = wm9090_i2c_probe,
.id_table = wm9090_id,
};
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index e32c8ded181d..7973a75cac05 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -97,13 +97,13 @@ struct wm_adsp_system_config_xm_hdr {
__be32 wdma[8];
__be32 build_job_name[3];
__be32 build_job_number;
-};
+} __packed;
struct wm_halo_system_config_xm_hdr {
__be32 halo_heartbeat;
__be32 build_job_name[3];
__be32 build_job_number;
-};
+} __packed;
struct wm_adsp_alg_xm_struct {
__be32 magic;
@@ -114,13 +114,13 @@ struct wm_adsp_alg_xm_struct {
__be32 high_water_mark;
__be32 low_water_mark;
__be64 smoothed_power;
-};
+} __packed;
struct wm_adsp_host_buf_coeff_v1 {
__be32 host_buf_ptr; /* Host buffer pointer */
__be32 versions; /* Version numbers */
__be32 name[4]; /* The buffer name */
-};
+} __packed;
struct wm_adsp_buffer {
__be32 buf1_base; /* Base addr of first buffer area */
@@ -141,7 +141,7 @@ struct wm_adsp_buffer {
__be32 min_free; /* min free space since stream start */
__be32 blocks_written[2]; /* total blocks written (64 bit) */
__be32 words_written[2]; /* total words written (64 bit) */
-};
+} __packed;
struct wm_adsp_compr;
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 616b26c70c3b..f3a56f3ce487 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1066,6 +1066,7 @@ static const struct snd_soc_component_driver wsa881x_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
.dapm_routes = wsa881x_audio_map,
.num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+ .endianness = 1,
};
static int wsa881x_update_status(struct sdw_slave *slave,
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index d7d1536a4f37..20a9f8e924b3 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -11,7 +11,7 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_platform.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <linux/pm_runtime.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -1211,11 +1211,9 @@ static int fsl_asrc_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
goto err_pm_get_sync;
- }
ret = fsl_asrc_init(asrc);
if (ret) {
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index cd9b36ec0ecb..5038faf035cb 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -8,7 +8,7 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h
index 30620d56252c..86d5c360d4f5 100644
--- a/sound/soc/fsl/fsl_easrc.h
+++ b/sound/soc/fsl/fsl_easrc.h
@@ -7,7 +7,7 @@
#define _FSL_EASRC_H
#include <sound/asound.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include "fsl_asrc_common.h"
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index ed444e8f1d6b..1a2bdf8e76f0 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -1050,11 +1050,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
goto err_pm_get_sync;
- }
ret = fsl_esai_hw_init(esai_priv);
if (ret)
diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c
index 9f90989ac59a..7b88d52f27de 100644
--- a/sound/soc/fsl/fsl_micfil.c
+++ b/sound/soc/fsl/fsl_micfil.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright 2018 NXP
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interrupt.h>
@@ -15,6 +16,7 @@
#include <linux/regmap.h>
#include <linux/sysfs.h>
#include <linux/types.h>
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -22,10 +24,17 @@
#include <sound/core.h>
#include "fsl_micfil.h"
-#include "imx-pcm.h"
-#define FSL_MICFIL_RATES SNDRV_PCM_RATE_8000_48000
-#define FSL_MICFIL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+#define MICFIL_OSR_DEFAULT 16
+
+enum quality {
+ QUALITY_HIGH,
+ QUALITY_MEDIUM,
+ QUALITY_LOW,
+ QUALITY_VLOW0,
+ QUALITY_VLOW1,
+ QUALITY_VLOW2,
+};
struct fsl_micfil {
struct platform_device *pdev;
@@ -34,13 +43,11 @@ struct fsl_micfil {
struct clk *busclk;
struct clk *mclk;
struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct sdma_peripheral_config sdmacfg;
unsigned int dataline;
char name[32];
int irq[MICFIL_IRQ_LINES];
- unsigned int mclk_streams;
- int quality; /*QUALITY 2-0 bits */
- bool slave_mode;
- int channel_gain[8];
+ enum quality quality;
};
struct fsl_micfil_soc_data {
@@ -48,6 +55,7 @@ struct fsl_micfil_soc_data {
unsigned int fifo_depth;
unsigned int dataline;
bool imx;
+ u64 formats;
};
static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
@@ -55,37 +63,91 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
.fifos = 8,
.fifo_depth = 8,
.dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx8mp = {
+ .imx = true,
+ .fifos = 8,
+ .fifo_depth = 32,
+ .dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
};
static const struct of_device_id fsl_micfil_dt_ids[] = {
{ .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm },
+ { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp },
{}
};
MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids);
-/* Table 5. Quality Modes
- * Medium 0 0 0
- * High 0 0 1
- * Very Low 2 1 0 0
- * Very Low 1 1 0 1
- * Very Low 0 1 1 0
- * Low 1 1 1
- */
static const char * const micfil_quality_select_texts[] = {
- "Medium", "High",
- "N/A", "N/A",
- "VLow2", "VLow1",
- "VLow0", "Low",
+ [QUALITY_HIGH] = "High",
+ [QUALITY_MEDIUM] = "Medium",
+ [QUALITY_LOW] = "Low",
+ [QUALITY_VLOW0] = "VLow0",
+ [QUALITY_VLOW1] = "Vlow1",
+ [QUALITY_VLOW2] = "Vlow2",
};
static const struct soc_enum fsl_micfil_quality_enum =
- SOC_ENUM_SINGLE(REG_MICFIL_CTRL2,
- MICFIL_CTRL2_QSEL_SHIFT,
- ARRAY_SIZE(micfil_quality_select_texts),
- micfil_quality_select_texts);
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts),
+ micfil_quality_select_texts);
static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0);
+static int micfil_set_quality(struct fsl_micfil *micfil)
+{
+ u32 qsel;
+
+ switch (micfil->quality) {
+ case QUALITY_HIGH:
+ qsel = MICFIL_QSEL_HIGH_QUALITY;
+ break;
+ case QUALITY_MEDIUM:
+ qsel = MICFIL_QSEL_MEDIUM_QUALITY;
+ break;
+ case QUALITY_LOW:
+ qsel = MICFIL_QSEL_LOW_QUALITY;
+ break;
+ case QUALITY_VLOW0:
+ qsel = MICFIL_QSEL_VLOW0_QUALITY;
+ break;
+ case QUALITY_VLOW1:
+ qsel = MICFIL_QSEL_VLOW1_QUALITY;
+ break;
+ case QUALITY_VLOW2:
+ qsel = MICFIL_QSEL_VLOW2_QUALITY;
+ break;
+ }
+
+ return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_QSEL,
+ FIELD_PREP(MICFIL_CTRL2_QSEL, qsel));
+}
+
+static int micfil_quality_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = micfil->quality;
+
+ return 0;
+}
+
+static int micfil_quality_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
+
+ micfil->quality = ucontrol->value.integer.value[0];
+
+ return micfil_set_quality(micfil);
+}
+
static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv),
@@ -105,64 +167,9 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv),
SOC_ENUM_EXT("MICFIL Quality Select",
fsl_micfil_quality_enum,
- snd_soc_get_enum_double, snd_soc_put_enum_double),
+ micfil_quality_get, micfil_quality_set),
};
-static inline int get_pdm_clk(struct fsl_micfil *micfil,
- unsigned int rate)
-{
- u32 ctrl2_reg;
- int qsel, osr;
- int bclk;
-
- regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
- osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK)
- >> MICFIL_CTRL2_CICOSR_SHIFT);
-
- regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
- qsel = ctrl2_reg & MICFIL_CTRL2_QSEL_MASK;
-
- switch (qsel) {
- case MICFIL_HIGH_QUALITY:
- bclk = rate * 8 * osr / 2; /* kfactor = 0.5 */
- break;
- case MICFIL_MEDIUM_QUALITY:
- case MICFIL_VLOW0_QUALITY:
- bclk = rate * 4 * osr * 1; /* kfactor = 1 */
- break;
- case MICFIL_LOW_QUALITY:
- case MICFIL_VLOW1_QUALITY:
- bclk = rate * 2 * osr * 2; /* kfactor = 2 */
- break;
- case MICFIL_VLOW2_QUALITY:
- bclk = rate * osr * 4; /* kfactor = 4 */
- break;
- default:
- dev_err(&micfil->pdev->dev,
- "Please make sure you select a valid quality.\n");
- bclk = -1;
- break;
- }
-
- return bclk;
-}
-
-static inline int get_clk_div(struct fsl_micfil *micfil,
- unsigned int rate)
-{
- u32 ctrl2_reg;
- long mclk_rate;
- int clk_div;
-
- regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
-
- mclk_rate = clk_get_rate(micfil->mclk);
-
- clk_div = mclk_rate / (get_pdm_clk(micfil, rate) * 2);
-
- return clk_div;
-}
-
/* The SRES is a self-negated bit which provides the CPU with the
* capability to initialize the PDM Interface module through the
* slave-bus interface. This bit always reads as zero, and this
@@ -173,45 +180,19 @@ static int fsl_micfil_reset(struct device *dev)
struct fsl_micfil *micfil = dev_get_drvdata(dev);
int ret;
- ret = regmap_update_bits(micfil->regmap,
- REG_MICFIL_CTRL1,
- MICFIL_CTRL1_MDIS_MASK,
- 0);
- if (ret) {
- dev_err(dev, "failed to clear MDIS bit %d\n", ret);
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_MDIS);
+ if (ret)
return ret;
- }
- ret = regmap_update_bits(micfil->regmap,
- REG_MICFIL_CTRL1,
- MICFIL_CTRL1_SRES_MASK,
- MICFIL_CTRL1_SRES);
- if (ret) {
- dev_err(dev, "failed to reset MICFIL: %d\n", ret);
+ ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_SRES);
+ if (ret)
return ret;
- }
return 0;
}
-static int fsl_micfil_set_mclk_rate(struct fsl_micfil *micfil,
- unsigned int freq)
-{
- struct device *dev = &micfil->pdev->dev;
- int ret;
-
- clk_disable_unprepare(micfil->mclk);
-
- ret = clk_set_rate(micfil->mclk, freq * 1024);
- if (ret)
- dev_warn(dev, "failed to set rate (%u): %d\n",
- freq * 1024, ret);
-
- clk_prepare_enable(micfil->mclk);
-
- return ret;
-}
-
static int fsl_micfil_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -249,42 +230,32 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
* 11 - reserved
*/
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_DISEL_MASK,
- (1 << MICFIL_CTRL1_DISEL_SHIFT));
- if (ret) {
- dev_err(dev, "failed to update DISEL bits\n");
+ MICFIL_CTRL1_DISEL,
+ FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA));
+ if (ret)
return ret;
- }
/* Enable the module */
- ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_PDMIEN_MASK,
- MICFIL_CTRL1_PDMIEN);
- if (ret) {
- dev_err(dev, "failed to enable the module\n");
+ ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_PDMIEN);
+ if (ret)
return ret;
- }
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* Disable the module */
- ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_PDMIEN_MASK,
- 0);
- if (ret) {
- dev_err(dev, "failed to enable the module\n");
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_PDMIEN);
+ if (ret)
return ret;
- }
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_DISEL_MASK,
- (0 << MICFIL_CTRL1_DISEL_SHIFT));
- if (ret) {
- dev_err(dev, "failed to update DISEL bits\n");
+ MICFIL_CTRL1_DISEL,
+ FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE));
+ if (ret)
return ret;
- }
break;
default:
return -EINVAL;
@@ -292,39 +263,6 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static int fsl_set_clock_params(struct device *dev, unsigned int rate)
-{
- struct fsl_micfil *micfil = dev_get_drvdata(dev);
- int clk_div;
- int ret;
-
- ret = fsl_micfil_set_mclk_rate(micfil, rate);
- if (ret < 0)
- dev_err(dev, "failed to set mclk[%lu] to rate %u\n",
- clk_get_rate(micfil->mclk), rate);
-
- /* set CICOSR */
- ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
- MICFIL_CTRL2_CICOSR_MASK,
- MICFIL_CTRL2_OSR_DEFAULT);
- if (ret)
- dev_err(dev, "failed to set CICOSR in reg 0x%X\n",
- REG_MICFIL_CTRL2);
-
- /* set CLK_DIV */
- clk_div = get_clk_div(micfil, rate);
- if (clk_div < 0)
- ret = -EINVAL;
-
- ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
- MICFIL_CTRL2_CLKDIV_MASK, clk_div);
- if (ret)
- dev_err(dev, "failed to set CLKDIV in reg 0x%X\n",
- REG_MICFIL_CTRL2);
-
- return ret;
-}
-
static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -332,97 +270,69 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
- struct device *dev = &micfil->pdev->dev;
+ int clk_div = 8;
+ int osr = MICFIL_OSR_DEFAULT;
int ret;
/* 1. Disable the module */
- ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_PDMIEN_MASK, 0);
- if (ret) {
- dev_err(dev, "failed to disable the module\n");
+ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
+ MICFIL_CTRL1_PDMIEN);
+ if (ret)
return ret;
- }
/* enable channels */
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
0xFF, ((1 << channels) - 1));
- if (ret) {
- dev_err(dev, "failed to enable channels %d, reg 0x%X\n", ret,
- REG_MICFIL_CTRL1);
+ if (ret)
return ret;
- }
- ret = fsl_set_clock_params(dev, rate);
- if (ret < 0) {
- dev_err(dev, "Failed to set clock parameters [%d]\n", ret);
+ ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8);
+ if (ret)
+ return ret;
+
+ ret = micfil_set_quality(micfil);
+ if (ret)
return ret;
- }
+ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR,
+ FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) |
+ FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr));
+
+ micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg;
+ micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg);
+ micfil->sdmacfg.n_fifos_src = channels;
+ micfil->sdmacfg.sw_done = true;
micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX;
return 0;
}
-static int fsl_micfil_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
- struct device *dev = &micfil->pdev->dev;
-
- int ret;
-
- if (!freq)
- return 0;
-
- ret = fsl_micfil_set_mclk_rate(micfil, freq);
- if (ret < 0)
- dev_err(dev, "failed to set mclk[%lu] to rate %u\n",
- clk_get_rate(micfil->mclk), freq);
-
- return ret;
-}
-
static const struct snd_soc_dai_ops fsl_micfil_dai_ops = {
.startup = fsl_micfil_startup,
.trigger = fsl_micfil_trigger,
.hw_params = fsl_micfil_hw_params,
- .set_sysclk = fsl_micfil_set_dai_sysclk,
};
static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev);
- struct device *dev = cpu_dai->dev;
- unsigned int val;
int ret;
- int i;
- /* set qsel to medium */
- ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
- MICFIL_CTRL2_QSEL_MASK, MICFIL_MEDIUM_QUALITY);
- if (ret) {
- dev_err(dev, "failed to set quality mode bits, reg 0x%X\n",
- REG_MICFIL_CTRL2);
- return ret;
- }
+ micfil->quality = QUALITY_MEDIUM;
/* set default gain to max_gain */
regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x77777777);
- for (i = 0; i < 8; i++)
- micfil->channel_gain[i] = 0xF;
snd_soc_dai_init_dma_data(cpu_dai, NULL,
&micfil->dma_params_rx);
/* FIFO Watermark Control - FIFOWMK*/
- val = MICFIL_FIFO_CTRL_FIFOWMK(micfil->soc->fifo_depth) - 1;
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL,
- MICFIL_FIFO_CTRL_FIFOWMK_MASK,
- val);
- if (ret) {
- dev_err(dev, "failed to set FIFOWMK\n");
+ MICFIL_FIFO_CTRL_FIFOWMK,
+ FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1));
+ if (ret)
return ret;
- }
return 0;
}
@@ -433,8 +343,8 @@ static struct snd_soc_dai_driver fsl_micfil_dai = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 8,
- .rates = FSL_MICFIL_RATES,
- .formats = FSL_MICFIL_FORMATS,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &fsl_micfil_dai_ops,
};
@@ -578,11 +488,11 @@ static irqreturn_t micfil_isr(int irq, void *devid)
regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg);
regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg);
- dma_enabled = MICFIL_DMA_ENABLED(ctrl1_reg);
+ dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA;
/* Channel 0-7 Output Data Flags */
for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) {
- if (stat_reg & MICFIL_STAT_CHXF_MASK(i))
+ if (stat_reg & MICFIL_STAT_CHXF(i))
dev_dbg(&pdev->dev,
"Data available in Data Channel %d\n", i);
/* if DMA is not enabled, field must be written with 1
@@ -591,17 +501,17 @@ static irqreturn_t micfil_isr(int irq, void *devid)
if (!dma_enabled)
regmap_write_bits(micfil->regmap,
REG_MICFIL_STAT,
- MICFIL_STAT_CHXF_MASK(i),
+ MICFIL_STAT_CHXF(i),
1);
}
for (i = 0; i < MICFIL_FIFO_NUM; i++) {
- if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER_MASK(i))
+ if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i))
dev_dbg(&pdev->dev,
"FIFO Overflow Exception flag for channel %d\n",
i);
- if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(i))
+ if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i))
dev_dbg(&pdev->dev,
"FIFO Underflow Exception flag for channel %d\n",
i);
@@ -618,16 +528,16 @@ static irqreturn_t micfil_err_isr(int irq, void *devid)
regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
- if (stat_reg & MICFIL_STAT_BSY_FIL_MASK)
+ if (stat_reg & MICFIL_STAT_BSY_FIL)
dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n");
- if (stat_reg & MICFIL_STAT_FIR_RDY_MASK)
+ if (stat_reg & MICFIL_STAT_FIR_RDY)
dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n");
- if (stat_reg & MICFIL_STAT_LOWFREQF_MASK) {
+ if (stat_reg & MICFIL_STAT_LOWFREQF) {
dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n");
regmap_write_bits(micfil->regmap, REG_MICFIL_STAT,
- MICFIL_STAT_LOWFREQF_MASK, 1);
+ MICFIL_STAT_LOWFREQF, 1);
}
return IRQ_HANDLED;
@@ -640,7 +550,6 @@ static int fsl_micfil_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *regs;
int ret, i;
- unsigned long irqflag = 0;
micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL);
if (!micfil)
@@ -699,17 +608,13 @@ static int fsl_micfil_probe(struct platform_device *pdev)
/* get IRQs */
for (i = 0; i < MICFIL_IRQ_LINES; i++) {
micfil->irq[i] = platform_get_irq(pdev, i);
- dev_err(&pdev->dev, "GET IRQ: %d\n", micfil->irq[i]);
if (micfil->irq[i] < 0)
return micfil->irq[i];
}
- if (of_property_read_bool(np, "fsl,shared-interrupt"))
- irqflag = IRQF_SHARED;
-
/* Digital Microphone interface interrupt */
ret = devm_request_irq(&pdev->dev, micfil->irq[0],
- micfil_isr, irqflag,
+ micfil_isr, IRQF_SHARED,
micfil->name, micfil);
if (ret) {
dev_err(&pdev->dev, "failed to claim mic interface irq %u\n",
@@ -719,7 +624,7 @@ static int fsl_micfil_probe(struct platform_device *pdev)
/* Digital Microphone interface error interrupt */
ret = devm_request_irq(&pdev->dev, micfil->irq[1],
- micfil_err_isr, irqflag,
+ micfil_err_isr, IRQF_SHARED,
micfil->name, micfil);
if (ret) {
dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n",
@@ -731,7 +636,6 @@ static int fsl_micfil_probe(struct platform_device *pdev)
micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0;
micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX;
-
platform_set_drvdata(pdev, micfil);
pm_runtime_enable(&pdev->dev);
@@ -747,6 +651,8 @@ static int fsl_micfil_probe(struct platform_device *pdev)
return ret;
}
+ fsl_micfil_dai.capture.formats = micfil->soc->formats;
+
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component,
&fsl_micfil_dai, 1);
if (ret) {
diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h
index bac825c3135a..053caba3caf3 100644
--- a/sound/soc/fsl/fsl_micfil.h
+++ b/sound/soc/fsl/fsl_micfil.h
@@ -33,240 +33,94 @@
#define REG_MICFIL_VAD0_ZCD 0xA8
/* MICFIL Control Register 1 -- REG_MICFILL_CTRL1 0x00 */
-#define MICFIL_CTRL1_MDIS_SHIFT 31
-#define MICFIL_CTRL1_MDIS_MASK BIT(MICFIL_CTRL1_MDIS_SHIFT)
-#define MICFIL_CTRL1_MDIS BIT(MICFIL_CTRL1_MDIS_SHIFT)
-#define MICFIL_CTRL1_DOZEN_SHIFT 30
-#define MICFIL_CTRL1_DOZEN_MASK BIT(MICFIL_CTRL1_DOZEN_SHIFT)
-#define MICFIL_CTRL1_DOZEN BIT(MICFIL_CTRL1_DOZEN_SHIFT)
-#define MICFIL_CTRL1_PDMIEN_SHIFT 29
-#define MICFIL_CTRL1_PDMIEN_MASK BIT(MICFIL_CTRL1_PDMIEN_SHIFT)
-#define MICFIL_CTRL1_PDMIEN BIT(MICFIL_CTRL1_PDMIEN_SHIFT)
-#define MICFIL_CTRL1_DBG_SHIFT 28
-#define MICFIL_CTRL1_DBG_MASK BIT(MICFIL_CTRL1_DBG_SHIFT)
-#define MICFIL_CTRL1_DBG BIT(MICFIL_CTRL1_DBG_SHIFT)
-#define MICFIL_CTRL1_SRES_SHIFT 27
-#define MICFIL_CTRL1_SRES_MASK BIT(MICFIL_CTRL1_SRES_SHIFT)
-#define MICFIL_CTRL1_SRES BIT(MICFIL_CTRL1_SRES_SHIFT)
-#define MICFIL_CTRL1_DBGE_SHIFT 26
-#define MICFIL_CTRL1_DBGE_MASK BIT(MICFIL_CTRL1_DBGE_SHIFT)
-#define MICFIL_CTRL1_DBGE BIT(MICFIL_CTRL1_DBGE_SHIFT)
-#define MICFIL_CTRL1_DISEL_SHIFT 24
-#define MICFIL_CTRL1_DISEL_WIDTH 2
-#define MICFIL_CTRL1_DISEL_MASK ((BIT(MICFIL_CTRL1_DISEL_WIDTH) - 1) \
- << MICFIL_CTRL1_DISEL_SHIFT)
-#define MICFIL_CTRL1_DISEL(v) (((v) << MICFIL_CTRL1_DISEL_SHIFT) \
- & MICFIL_CTRL1_DISEL_MASK)
-#define MICFIL_CTRL1_ERREN_SHIFT 23
-#define MICFIL_CTRL1_ERREN_MASK BIT(MICFIL_CTRL1_ERREN_SHIFT)
-#define MICFIL_CTRL1_ERREN BIT(MICFIL_CTRL1_ERREN_SHIFT)
-#define MICFIL_CTRL1_CHEN_SHIFT 0
-#define MICFIL_CTRL1_CHEN_WIDTH 8
-#define MICFIL_CTRL1_CHEN_MASK(x) (BIT(x) << MICFIL_CTRL1_CHEN_SHIFT)
-#define MICFIL_CTRL1_CHEN(x) (MICFIL_CTRL1_CHEN_MASK(x))
+#define MICFIL_CTRL1_MDIS BIT(31)
+#define MICFIL_CTRL1_DOZEN BIT(30)
+#define MICFIL_CTRL1_PDMIEN BIT(29)
+#define MICFIL_CTRL1_DBG BIT(28)
+#define MICFIL_CTRL1_SRES BIT(27)
+#define MICFIL_CTRL1_DBGE BIT(26)
+
+#define MICFIL_CTRL1_DISEL_DISABLE 0
+#define MICFIL_CTRL1_DISEL_DMA 1
+#define MICFIL_CTRL1_DISEL_IRQ 2
+#define MICFIL_CTRL1_DISEL GENMASK(25, 24)
+#define MICFIL_CTRL1_ERREN BIT(23)
+#define MICFIL_CTRL1_CHEN(ch) BIT(ch)
/* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */
#define MICFIL_CTRL2_QSEL_SHIFT 25
-#define MICFIL_CTRL2_QSEL_WIDTH 3
-#define MICFIL_CTRL2_QSEL_MASK ((BIT(MICFIL_CTRL2_QSEL_WIDTH) - 1) \
- << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_HIGH_QUALITY BIT(MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_MEDIUM_QUALITY (0 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_LOW_QUALITY (7 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_VLOW0_QUALITY (6 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_VLOW1_QUALITY (5 << MICFIL_CTRL2_QSEL_SHIFT)
-#define MICFIL_VLOW2_QUALITY (4 << MICFIL_CTRL2_QSEL_SHIFT)
+#define MICFIL_CTRL2_QSEL GENMASK(27, 25)
+#define MICFIL_QSEL_MEDIUM_QUALITY 0
+#define MICFIL_QSEL_HIGH_QUALITY 1
+#define MICFIL_QSEL_LOW_QUALITY 7
+#define MICFIL_QSEL_VLOW0_QUALITY 6
+#define MICFIL_QSEL_VLOW1_QUALITY 5
+#define MICFIL_QSEL_VLOW2_QUALITY 4
-#define MICFIL_CTRL2_CICOSR_SHIFT 16
-#define MICFIL_CTRL2_CICOSR_WIDTH 4
-#define MICFIL_CTRL2_CICOSR_MASK ((BIT(MICFIL_CTRL2_CICOSR_WIDTH) - 1) \
- << MICFIL_CTRL2_CICOSR_SHIFT)
-#define MICFIL_CTRL2_CICOSR(v) (((v) << MICFIL_CTRL2_CICOSR_SHIFT) \
- & MICFIL_CTRL2_CICOSR_MASK)
-#define MICFIL_CTRL2_CLKDIV_SHIFT 0
-#define MICFIL_CTRL2_CLKDIV_WIDTH 8
-#define MICFIL_CTRL2_CLKDIV_MASK ((BIT(MICFIL_CTRL2_CLKDIV_WIDTH) - 1) \
- << MICFIL_CTRL2_CLKDIV_SHIFT)
-#define MICFIL_CTRL2_CLKDIV(v) (((v) << MICFIL_CTRL2_CLKDIV_SHIFT) \
- & MICFIL_CTRL2_CLKDIV_MASK)
+#define MICFIL_CTRL2_CICOSR GENMASK(19, 16)
+#define MICFIL_CTRL2_CLKDIV GENMASK(7, 0)
/* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */
-#define MICFIL_STAT_BSY_FIL_SHIFT 31
-#define MICFIL_STAT_BSY_FIL_MASK BIT(MICFIL_STAT_BSY_FIL_SHIFT)
-#define MICFIL_STAT_BSY_FIL BIT(MICFIL_STAT_BSY_FIL_SHIFT)
-#define MICFIL_STAT_FIR_RDY_SHIFT 30
-#define MICFIL_STAT_FIR_RDY_MASK BIT(MICFIL_STAT_FIR_RDY_SHIFT)
-#define MICFIL_STAT_FIR_RDY BIT(MICFIL_STAT_FIR_RDY_SHIFT)
-#define MICFIL_STAT_LOWFREQF_SHIFT 29
-#define MICFIL_STAT_LOWFREQF_MASK BIT(MICFIL_STAT_LOWFREQF_SHIFT)
-#define MICFIL_STAT_LOWFREQF BIT(MICFIL_STAT_LOWFREQF_SHIFT)
-#define MICFIL_STAT_CHXF_SHIFT(v) (v)
-#define MICFIL_STAT_CHXF_MASK(v) BIT(MICFIL_STAT_CHXF_SHIFT(v))
-#define MICFIL_STAT_CHXF(v) BIT(MICFIL_STAT_CHXF_SHIFT(v))
+#define MICFIL_STAT_BSY_FIL BIT(31)
+#define MICFIL_STAT_FIR_RDY BIT(30)
+#define MICFIL_STAT_LOWFREQF BIT(29)
+#define MICFIL_STAT_CHXF(ch) BIT(ch)
/* MICFIL FIFO Control Register -- REG_MICFIL_FIFO_CTRL 0x10 */
-#define MICFIL_FIFO_CTRL_FIFOWMK_SHIFT 0
-#define MICFIL_FIFO_CTRL_FIFOWMK_WIDTH 3
-#define MICFIL_FIFO_CTRL_FIFOWMK_MASK ((BIT(MICFIL_FIFO_CTRL_FIFOWMK_WIDTH) - 1) \
- << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT)
-#define MICFIL_FIFO_CTRL_FIFOWMK(v) (((v) << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) \
- & MICFIL_FIFO_CTRL_FIFOWMK_MASK)
+#define MICFIL_FIFO_CTRL_FIFOWMK GENMASK(2, 0)
/* MICFIL FIFO Status Register -- REG_MICFIL_FIFO_STAT 0x14 */
-#define MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v) (v)
-#define MICFIL_FIFO_STAT_FIFOX_OVER_MASK(v) BIT(MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v))
-#define MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v) ((v) + 8)
-#define MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(v) BIT(MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v))
+#define MICFIL_FIFO_STAT_FIFOX_OVER(ch) BIT(ch)
+#define MICFIL_FIFO_STAT_FIFOX_UNDER(ch) BIT((ch) + 8)
/* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/
-#define MICFIL_VAD0_CTRL1_CHSEL_SHIFT 24
-#define MICFIL_VAD0_CTRL1_CHSEL_WIDTH 3
-#define MICFIL_VAD0_CTRL1_CHSEL_MASK ((BIT(MICFIL_VAD0_CTRL1_CHSEL_WIDTH) - 1) \
- << MICFIL_VAD0_CTRL1_CHSEL_SHIFT)
-#define MICFIL_VAD0_CTRL1_CHSEL(v) (((v) << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) \
- & MICFIL_VAD0_CTRL1_CHSEL_MASK)
-#define MICFIL_VAD0_CTRL1_CICOSR_SHIFT 16
-#define MICFIL_VAD0_CTRL1_CICOSR_WIDTH 4
-#define MICFIL_VAD0_CTRL1_CICOSR_MASK ((BIT(MICFIL_VAD0_CTRL1_CICOSR_WIDTH) - 1) \
- << MICFIL_VAD0_CTRL1_CICOSR_SHIFT)
-#define MICFIL_VAD0_CTRL1_CICOSR(v) (((v) << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) \
- & MICFIL_VAD0_CTRL1_CICOSR_MASK)
-#define MICFIL_VAD0_CTRL1_INITT_SHIFT 8
-#define MICFIL_VAD0_CTRL1_INITT_WIDTH 5
-#define MICFIL_VAD0_CTRL1_INITT_MASK ((BIT(MICFIL_VAD0_CTRL1_INITT_WIDTH) - 1) \
- << MICFIL_VAD0_CTRL1_INITT_SHIFT)
-#define MICFIL_VAD0_CTRL1_INITT(v) (((v) << MICFIL_VAD0_CTRL1_INITT_SHIFT) \
- & MICFIL_VAD0_CTRL1_INITT_MASK)
-#define MICFIL_VAD0_CTRL1_ST10_SHIFT 4
-#define MICFIL_VAD0_CTRL1_ST10_MASK BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT)
-#define MICFIL_VAD0_CTRL1_ST10 BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT)
-#define MICFIL_VAD0_CTRL1_ERIE_SHIFT 3
-#define MICFIL_VAD0_CTRL1_ERIE_MASK BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT)
-#define MICFIL_VAD0_CTRL1_ERIE BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT)
-#define MICFIL_VAD0_CTRL1_IE_SHIFT 2
-#define MICFIL_VAD0_CTRL1_IE_MASK BIT(MICFIL_VAD0_CTRL1_IE_SHIFT)
-#define MICFIL_VAD0_CTRL1_IE BIT(MICFIL_VAD0_CTRL1_IE_SHIFT)
-#define MICFIL_VAD0_CTRL1_RST_SHIFT 1
-#define MICFIL_VAD0_CTRL1_RST_MASK BIT(MICFIL_VAD0_CTRL1_RST_SHIFT)
-#define MICFIL_VAD0_CTRL1_RST BIT(MICFIL_VAD0_CTRL1_RST_SHIFT)
-#define MICFIL_VAD0_CTRL1_EN_SHIFT 0
-#define MICFIL_VAD0_CTRL1_EN_MASK BIT(MICFIL_VAD0_CTRL1_EN_SHIFT)
-#define MICFIL_VAD0_CTRL1_EN BIT(MICFIL_VAD0_CTRL1_EN_SHIFT)
+#define MICFIL_VAD0_CTRL1_CHSEL GENMASK(26, 24)
+#define MICFIL_VAD0_CTRL1_CICOSR GENMASK(19, 16)
+#define MICFIL_VAD0_CTRL1_INITT GENMASK(12, 8)
+#define MICFIL_VAD0_CTRL1_ST10 BIT(4)
+#define MICFIL_VAD0_CTRL1_ERIE BIT(3)
+#define MICFIL_VAD0_CTRL1_IE BIT(2)
+#define MICFIL_VAD0_CTRL1_RST BIT(1)
+#define MICFIL_VAD0_CTRL1_EN BIT(0)
/* MICFIL HWVAD0 Control 2 Register -- REG_MICFIL_VAD0_CTRL2*/
-#define MICFIL_VAD0_CTRL2_FRENDIS_SHIFT 31
-#define MICFIL_VAD0_CTRL2_FRENDIS_MASK BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_FRENDIS BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_PREFEN_SHIFT 30
-#define MICFIL_VAD0_CTRL2_PREFEN_MASK BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT)
-#define MICFIL_VAD0_CTRL2_PREFEN BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT)
-#define MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT 28
-#define MICFIL_VAD0_CTRL2_FOUTDIS_MASK BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT)
-#define MICFIL_VAD0_CTRL2_FRAMET_SHIFT 16
-#define MICFIL_VAD0_CTRL2_FRAMET_WIDTH 6
-#define MICFIL_VAD0_CTRL2_FRAMET_MASK ((BIT(MICFIL_VAD0_CTRL2_FRAMET_WIDTH) - 1) \
- << MICFIL_VAD0_CTRL2_FRAMET_SHIFT)
-#define MICFIL_VAD0_CTRL2_FRAMET(v) (((v) << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) \
- & MICFIL_VAD0_CTRL2_FRAMET_MASK)
-#define MICFIL_VAD0_CTRL2_INPGAIN_SHIFT 8
-#define MICFIL_VAD0_CTRL2_INPGAIN_WIDTH 4
-#define MICFIL_VAD0_CTRL2_INPGAIN_MASK ((BIT(MICFIL_VAD0_CTRL2_INPGAIN_WIDTH) - 1) \
- << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT)
-#define MICFIL_VAD0_CTRL2_INPGAIN(v) (((v) << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) \
- & MICFIL_VAD0_CTRL2_INPGAIN_MASK)
-#define MICFIL_VAD0_CTRL2_HPF_SHIFT 0
-#define MICFIL_VAD0_CTRL2_HPF_WIDTH 2
-#define MICFIL_VAD0_CTRL2_HPF_MASK ((BIT(MICFIL_VAD0_CTRL2_HPF_WIDTH) - 1) \
- << MICFIL_VAD0_CTRL2_HPF_SHIFT)
-#define MICFIL_VAD0_CTRL2_HPF(v) (((v) << MICFIL_VAD0_CTRL2_HPF_SHIFT) \
- & MICFIL_VAD0_CTRL2_HPF_MASK)
+#define MICFIL_VAD0_CTRL2_FRENDIS BIT(31)
+#define MICFIL_VAD0_CTRL2_PREFEN BIT(30)
+#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(28)
+#define MICFIL_VAD0_CTRL2_FRAMET GENMASK(21, 16)
+#define MICFIL_VAD0_CTRL2_INPGAIN GENMASK(11, 8)
+#define MICFIL_VAD0_CTRL2_HPF GENMASK(1, 0)
/* MICFIL HWVAD0 Signal CONFIG Register -- REG_MICFIL_VAD0_SCONFIG */
-#define MICFIL_VAD0_SCONFIG_SFILEN_SHIFT 31
-#define MICFIL_VAD0_SCONFIG_SFILEN_MASK BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SFILEN BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT 30
-#define MICFIL_VAD0_SCONFIG_SMAXEN_MASK BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SGAIN_SHIFT 0
-#define MICFIL_VAD0_SCONFIG_SGAIN_WIDTH 4
-#define MICFIL_VAD0_SCONFIG_SGAIN_MASK ((BIT(MICFIL_VAD0_SCONFIG_SGAIN_WIDTH) - 1) \
- << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT)
-#define MICFIL_VAD0_SCONFIG_SGAIN(v) (((v) << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) \
- & MICFIL_VAD0_SCONFIG_SGAIN_MASK)
+#define MICFIL_VAD0_SCONFIG_SFILEN BIT(31)
+#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(30)
+#define MICFIL_VAD0_SCONFIG_SGAIN GENMASK(3, 0)
/* MICFIL HWVAD0 Noise CONFIG Register -- REG_MICFIL_VAD0_NCONFIG */
-#define MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT 31
-#define MICFIL_VAD0_NCONFIG_NFILAUT_MASK BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NMINEN_SHIFT 30
-#define MICFIL_VAD0_NCONFIG_NMINEN_MASK BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NMINEN BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NDECEN_SHIFT 29
-#define MICFIL_VAD0_NCONFIG_NDECEN_MASK BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NDECEN BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NOREN_SHIFT 28
-#define MICFIL_VAD0_NCONFIG_NOREN BIT(MICFIL_VAD0_NCONFIG_NOREN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT 8
-#define MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH 5
-#define MICFIL_VAD0_NCONFIG_NFILADJ_MASK ((BIT(MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH) - 1) \
- << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NFILADJ(v) (((v) << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) \
- & MICFIL_VAD0_NCONFIG_NFILADJ_MASK)
-#define MICFIL_VAD0_NCONFIG_NGAIN_SHIFT 0
-#define MICFIL_VAD0_NCONFIG_NGAIN_WIDTH 4
-#define MICFIL_VAD0_NCONFIG_NGAIN_MASK ((BIT(MICFIL_VAD0_NCONFIG_NGAIN_WIDTH) - 1) \
- << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT)
-#define MICFIL_VAD0_NCONFIG_NGAIN(v) (((v) << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) \
- & MICFIL_VAD0_NCONFIG_NGAIN_MASK)
+#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(31)
+#define MICFIL_VAD0_NCONFIG_NMINEN BIT(30)
+#define MICFIL_VAD0_NCONFIG_NDECEN BIT(29)
+#define MICFIL_VAD0_NCONFIG_NOREN BIT(28)
+#define MICFIL_VAD0_NCONFIG_NFILADJ GENMASK(12, 8)
+#define MICFIL_VAD0_NCONFIG_NGAIN GENMASK(3, 0)
/* MICFIL HWVAD0 Zero-Crossing Detector - REG_MICFIL_VAD0_ZCD */
-#define MICFIL_VAD0_ZCD_ZCDTH_SHIFT 16
-#define MICFIL_VAD0_ZCD_ZCDTH_WIDTH 10
-#define MICFIL_VAD0_ZCD_ZCDTH_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDTH_WIDTH) - 1) \
- << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDTH(v) (((v) << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)\
- & MICFIL_VAD0_ZCD_ZCDTH_MASK)
-#define MICFIL_VAD0_ZCD_ZCDADJ_SHIFT 8
-#define MICFIL_VAD0_ZCD_ZCDADJ_WIDTH 4
-#define MICFIL_VAD0_ZCD_ZCDADJ_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDADJ_WIDTH) - 1)\
- << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDADJ(v) (((v) << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)\
- & MICFIL_VAD0_ZCD_ZCDADJ_MASK)
-#define MICFIL_VAD0_ZCD_ZCDAND_SHIFT 4
-#define MICFIL_VAD0_ZCD_ZCDAND_MASK BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDAND BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDAUT_SHIFT 2
-#define MICFIL_VAD0_ZCD_ZCDAUT_MASK BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDAUT BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDEN_SHIFT 0
-#define MICFIL_VAD0_ZCD_ZCDEN_MASK BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT)
-#define MICFIL_VAD0_ZCD_ZCDEN BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT)
+#define MICFIL_VAD0_ZCD_ZCDTH GENMASK(25, 16)
+#define MICFIL_VAD0_ZCD_ZCDADJ GENMASK(11, 8)
+#define MICFIL_VAD0_ZCD_ZCDAND BIT(4)
+#define MICFIL_VAD0_ZCD_ZCDAUT BIT(2)
+#define MICFIL_VAD0_ZCD_ZCDEN BIT(0)
/* MICFIL HWVAD0 Status Register - REG_MICFIL_VAD0_STAT */
-#define MICFIL_VAD0_STAT_INITF_SHIFT 31
-#define MICFIL_VAD0_STAT_INITF_MASK BIT(MICFIL_VAD0_STAT_INITF_SHIFT)
-#define MICFIL_VAD0_STAT_INITF BIT(MICFIL_VAD0_STAT_INITF_SHIFT)
-#define MICFIL_VAD0_STAT_INSATF_SHIFT 16
-#define MICFIL_VAD0_STAT_INSATF_MASK BIT(MICFIL_VAD0_STAT_INSATF_SHIFT)
-#define MICFIL_VAD0_STAT_INSATF BIT(MICFIL_VAD0_STAT_INSATF_SHIFT)
-#define MICFIL_VAD0_STAT_EF_SHIFT 15
-#define MICFIL_VAD0_STAT_EF_MASK BIT(MICFIL_VAD0_STAT_EF_SHIFT)
-#define MICFIL_VAD0_STAT_EF BIT(MICFIL_VAD0_STAT_EF_SHIFT)
-#define MICFIL_VAD0_STAT_IF_SHIFT 0
-#define MICFIL_VAD0_STAT_IF_MASK BIT(MICFIL_VAD0_STAT_IF_SHIFT)
-#define MICFIL_VAD0_STAT_IF BIT(MICFIL_VAD0_STAT_IF_SHIFT)
+#define MICFIL_VAD0_STAT_INITF BIT(31)
+#define MICFIL_VAD0_STAT_INSATF BIT(16)
+#define MICFIL_VAD0_STAT_EF BIT(15)
+#define MICFIL_VAD0_STAT_IF BIT(0)
/* MICFIL Output Control Register */
#define MICFIL_OUTGAIN_CHX_SHIFT(v) (4 * (v))
/* Constants */
-#define MICFIL_DMA_IRQ_DISABLED(v) ((v) & MICFIL_CTRL1_DISEL_MASK)
-#define MICFIL_DMA_ENABLED(v) ((0x1 << MICFIL_CTRL1_DISEL_SHIFT) \
- == ((v) & MICFIL_CTRL1_DISEL_MASK))
-#define MICFIL_IRQ_ENABLED(v) ((0x2 << MICFIL_CTRL1_DISEL_SHIFT) \
- == ((v) & MICFIL_CTRL1_DISEL_MASK))
#define MICFIL_OUTPUT_CHANNELS 8
#define MICFIL_FIFO_NUM 8
@@ -278,6 +132,5 @@
#define MICFIL_SLEEP_MIN 90000 /* in us */
#define MICFIL_SLEEP_MAX 100000 /* in us */
#define MICFIL_DMA_MAXBURST_RX 6
-#define MICFIL_CTRL2_OSR_DEFAULT (0 << MICFIL_CTRL2_CICOSR_SHIFT)
#endif /* _FSL_MICFIL_H */
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index ffc24afb5a7a..fa950dde5310 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1147,7 +1147,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
/* Select MCLK direction */
if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
- sai->verid.version >= 0x0301) {
+ 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);
}
@@ -1203,6 +1203,7 @@ static const struct fsl_sai_soc_data fsl_sai_vf610_data = {
.reg_offset = 0,
.mclk0_is_mclk1 = false,
.flags = 0,
+ .max_register = FSL_SAI_RMR,
};
static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
@@ -1213,6 +1214,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
.reg_offset = 0,
.mclk0_is_mclk1 = true,
.flags = 0,
+ .max_register = FSL_SAI_RMR,
};
static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
@@ -1223,6 +1225,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
.reg_offset = 8,
.mclk0_is_mclk1 = false,
.flags = PMQOS_CPU_LATENCY,
+ .max_register = FSL_SAI_RMR,
};
static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
@@ -1233,6 +1236,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
.reg_offset = 8,
.mclk0_is_mclk1 = false,
.flags = 0,
+ .max_register = FSL_SAI_RMR,
};
static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
@@ -1243,6 +1247,40 @@ static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
.reg_offset = 0,
.mclk0_is_mclk1 = false,
.flags = 0,
+ .max_register = FSL_SAI_RMR,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mm_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_MCTL,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mp_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_imx8ulp_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 16,
+ .reg_offset = 8,
+ .mclk0_is_mclk1 = false,
+ .pins = 4,
+ .flags = PMQOS_CPU_LATENCY,
+ .max_register = FSL_SAI_RTCAP,
};
static const struct of_device_id fsl_sai_ids[] = {
@@ -1252,6 +1290,9 @@ static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data },
{ .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data },
{ .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data },
+ { .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 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 7310fd02cc3c..e4965efe6102 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -223,6 +223,7 @@ struct fsl_sai_soc_data {
unsigned int pins;
unsigned int reg_offset;
unsigned int flags;
+ unsigned int max_register;
};
/**
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index ca30a4ede076..84cb36d9dfea 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -40,6 +40,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/dma/imx-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -214,6 +215,7 @@ struct fsl_ssi_soc_data {
* @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
* @use_dma: DMA is used or FIQ with stream filter
* @use_dual_fifo: DMA with support for dual FIFO mode
+ * @use_dyna_fifo: DMA with support for multi FIFO script
* @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
* @fifo_depth: Depth of the SSI FIFOs
* @slot_width: Width of each DAI slot
@@ -243,6 +245,7 @@ struct fsl_ssi_soc_data {
* @dma_maxburst: Max number of words to transfer in one go. So far,
* this is always the same as fifo_watermark.
* @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
+ * @audio_config: configure for dma multi fifo script
*/
struct fsl_ssi {
struct regmap *regs;
@@ -255,6 +258,7 @@ struct fsl_ssi {
bool synchronous;
bool use_dma;
bool use_dual_fifo;
+ bool use_dyna_fifo;
bool has_ipg_clk_name;
unsigned int fifo_depth;
unsigned int slot_width;
@@ -287,6 +291,7 @@ struct fsl_ssi {
u32 dma_maxburst;
struct mutex ac97_reg_lock;
+ struct sdma_peripheral_config audio_config[2];
};
/*
@@ -643,7 +648,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* task from fifo0, fifo1 would be neglected at the end of each
* period. But SSI would still access fifo1 with an invalid data.
*/
- if (ssi->use_dual_fifo)
+ if (ssi->use_dual_fifo || ssi->use_dyna_fifo)
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
@@ -802,6 +807,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
{
bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
+ struct fsl_ssi_regvals *vals = ssi->regvals;
struct regmap *regs = ssi->regs;
unsigned int channels = params_channels(hw_params);
unsigned int sample_size = params_width(hw_params);
@@ -856,6 +862,28 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
tx2 = tx || ssi->synchronous;
regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
+ if (ssi->use_dyna_fifo) {
+ if (channels == 1) {
+ ssi->audio_config[0].n_fifos_dst = 1;
+ ssi->audio_config[1].n_fifos_src = 1;
+ vals[RX].srcr &= ~SSI_SRCR_RFEN1;
+ vals[TX].stcr &= ~SSI_STCR_TFEN1;
+ vals[RX].scr &= ~SSI_SCR_TCH_EN;
+ vals[TX].scr &= ~SSI_SCR_TCH_EN;
+ } else {
+ ssi->audio_config[0].n_fifos_dst = 2;
+ ssi->audio_config[1].n_fifos_src = 2;
+ vals[RX].srcr |= SSI_SRCR_RFEN1;
+ vals[TX].stcr |= SSI_STCR_TFEN1;
+ vals[RX].scr |= SSI_SCR_TCH_EN;
+ vals[TX].scr |= SSI_SCR_TCH_EN;
+ }
+ ssi->dma_params_tx.peripheral_config = &ssi->audio_config[0];
+ ssi->dma_params_tx.peripheral_size = sizeof(ssi->audio_config[0]);
+ ssi->dma_params_rx.peripheral_config = &ssi->audio_config[1];
+ ssi->dma_params_rx.peripheral_size = sizeof(ssi->audio_config[1]);
+ }
+
return 0;
}
@@ -1353,7 +1381,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
/* Use even numbers to avoid channel swap due to SDMA script design */
- if (ssi->use_dual_fifo) {
+ if (ssi->use_dual_fifo || ssi->use_dyna_fifo) {
ssi->dma_params_tx.maxburst &= ~0x1;
ssi->dma_params_rx.maxburst &= ~0x1;
}
@@ -1446,6 +1474,8 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
ssi->use_dual_fifo = true;
+ if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
+ ssi->use_dyna_fifo = true;
/*
* Backward compatible for older bindings by manually triggering the
* machine driver's probe(). Use /compatible property, including the
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
index 168973035e35..b80c57362fb8 100644
--- a/sound/soc/fsl/imx-es8328.c
+++ b/sound/soc/fsl/imx-es8328.c
@@ -48,7 +48,7 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
if (gpio_is_valid(data->jack_gpio)) {
ret = snd_soc_card_jack_new(rtd->card, "Headphone",
SND_JACK_HEADPHONE | SND_JACK_BTN_0,
- &headset_jack, NULL, 0);
+ &headset_jack);
if (ret)
return ret;
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index 929f69b758af..a780cf5a65ff 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -78,8 +78,9 @@ static int imx_hdmi_init(struct snd_soc_pcm_runtime *rtd)
data->hdmi_jack_pin.pin = "HDMI Jack";
data->hdmi_jack_pin.mask = SND_JACK_LINEOUT;
/* enable jack detection */
- ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
- &data->hdmi_jack, &data->hdmi_jack_pin, 1);
+ ret = snd_soc_card_jack_new_pins(card, "HDMI Jack", SND_JACK_LINEOUT,
+ &data->hdmi_jack,
+ &data->hdmi_jack_pin, 1);
if (ret) {
dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
return ret;
@@ -126,6 +127,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
+ put_device(&cpu_pdev->dev);
goto fail;
}
@@ -205,8 +207,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
}
fail:
- if (cpu_np)
- of_node_put(cpu_np);
+ of_node_put(cpu_np);
return ret;
}
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index 5c6cf1ca8c8a..06b25f4b26b6 100644
--- a/sound/soc/fsl/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -9,7 +9,7 @@
#ifndef _IMX_PCM_H
#define _IMX_PCM_H
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
/*
* Do not change this as the FIQ handler depends on this size
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 8daced42d55e..580a0d963f0e 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -120,19 +120,19 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
- goto fail;
+ goto put_device;
}
comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
if (!comp) {
ret = -ENOMEM;
- goto fail;
+ goto put_device;
}
data->codec_clk = clk_get(&codec_dev->dev, NULL);
if (IS_ERR(data->codec_clk)) {
ret = PTR_ERR(data->codec_clk);
- goto fail;
+ goto put_device;
}
data->clk_frequency = clk_get_rate(data->codec_clk);
@@ -158,10 +158,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
data->card.dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret)
- goto fail;
+ goto put_device;
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret)
- goto fail;
+ goto put_device;
data->card.num_links = 1;
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
@@ -174,7 +174,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
- goto fail;
+ goto put_device;
}
of_node_put(ssi_np);
@@ -182,6 +182,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
return 0;
+put_device:
+ put_device(&codec_dev->dev);
fail:
if (data && !IS_ERR(data->codec_clk))
clk_put(data->codec_clk);
diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
index 19cd0937e740..2d30d822451a 100644
--- a/sound/soc/fsl/imx-ssi.h
+++ b/sound/soc/fsl/imx-ssi.h
@@ -182,7 +182,7 @@
#define DRV_NAME "imx-ssi"
#include <linux/dmaengine.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include "imx-pcm.h"
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index c0f3907a01fd..77ac4051b827 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -711,7 +711,7 @@ static void graph_link_init(struct asoc_simple_priv *priv,
*/
daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame);
if (is_cpu_node)
- daiclk = snd_soc_daifmt_clock_provider_fliped(daiclk);
+ daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk);
dai_link->dai_fmt = daifmt | daiclk;
dai_link->init = asoc_simple_dai_init;
@@ -1178,8 +1178,6 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev,
struct link_info *li;
int ret;
- dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n");
-
li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
if (!li)
return -ENOMEM;
@@ -1245,6 +1243,9 @@ err:
if (ret < 0)
dev_err_probe(dev, ret, "parse error\n");
+ if (ret == 0)
+ dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n");
+
return ret;
}
EXPORT_SYMBOL_GPL(audio_graph2_parse_of);
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index da0c27828ce6..539d7f081bd7 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -721,9 +721,8 @@ int asoc_simple_init_jack(struct snd_soc_card *card,
sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
sjack->gpio.debounce_time = 150;
- snd_soc_card_jack_new(card, pin_name, mask,
- &sjack->jack,
- &sjack->pin, 1);
+ snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
+ &sjack->pin, 1);
snd_soc_jack_add_gpios(&sjack->jack, 1,
&sjack->gpio);
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c
index f1f36f15a503..09d23b11621c 100644
--- a/sound/soc/img/img-i2s-in.c
+++ b/sound/soc/img/img-i2s-in.c
@@ -342,11 +342,9 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
- ret = pm_runtime_get_sync(i2s->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(i2s->dev);
+ ret = pm_runtime_resume_and_get(i2s->dev);
+ if (ret < 0)
return ret;
- }
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_disable(i2s, i);
diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c
index 800f247283cd..cd6a6a825741 100644
--- a/sound/soc/img/img-parallel-out.c
+++ b/sound/soc/img/img-parallel-out.c
@@ -162,11 +162,9 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- ret = pm_runtime_get_sync(prl->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(prl->dev);
+ ret = pm_runtime_resume_and_get(prl->dev);
+ if (ret < 0)
return ret;
- }
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c
index 95914d0612fe..a79d1ccaeec0 100644
--- a/sound/soc/img/img-spdif-in.c
+++ b/sound/soc/img/img-spdif-in.c
@@ -749,11 +749,9 @@ static int img_spdif_in_probe(struct platform_device *pdev)
if (ret)
goto err_pm_disable;
}
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
goto err_suspend;
- }
rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
if (IS_ERR(rst)) {
diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c
index c3189d9ff72f..f7062eba2611 100644
--- a/sound/soc/img/img-spdif-out.c
+++ b/sound/soc/img/img-spdif-out.c
@@ -362,11 +362,9 @@ static int img_spdif_out_probe(struct platform_device *pdev)
if (ret)
goto err_pm_disable;
}
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
goto err_suspend;
- }
img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
IMG_SPDIF_OUT_CTL);
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index d025ca0c77fa..7c85d1bb9c12 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -211,11 +211,16 @@ config SND_SOC_INTEL_KEEMBAY
config SND_SOC_INTEL_AVS
tristate "Intel AVS driver"
- depends on PCI && ACPI
+ depends on X86 || COMPILE_TEST
+ depends on PCI
depends on COMMON_CLK
- select SND_SOC_ACPI
+ select SND_SOC_ACPI if ACPI
+ select SND_SOC_TOPOLOGY
+ select SND_HDA
select SND_HDA_EXT_CORE
select SND_HDA_DSP_LOADER
+ select SND_INTEL_DSP_CONFIG
+ select WANT_DEV_COREDUMP
help
Enable support for Intel(R) cAVS 1.5 platforms with DSP
capabilities. This includes Skylake, Kabylake, Amberlake and
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index e21e11dac000..3a42d68c0247 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -360,7 +360,6 @@ void sst_context_cleanup(struct intel_sst_drv *ctx)
sst_unregister(ctx->dev);
sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
- flush_scheduled_work();
destroy_workqueue(ctx->post_msg_wq);
cpu_latency_qos_remove_request(ctx->qos);
kfree(ctx->fw_sg_list.src);
diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c
index 0af618dd8073..dc31c2c8f54c 100644
--- a/sound/soc/intel/atom/sst/sst_drv_interface.c
+++ b/sound/soc/intel/atom/sst/sst_drv_interface.c
@@ -136,11 +136,10 @@ static int sst_power_control(struct device *dev, bool state)
int usage_count = 0;
if (state) {
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
usage_count = GET_USAGE_COUNT(dev);
dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
if (ret < 0) {
- pm_runtime_put_sync(dev);
dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
return ret;
}
@@ -193,11 +192,9 @@ static int sst_cdev_open(struct device *dev,
struct stream_info *stream;
struct intel_sst_drv *ctx = dev_get_drvdata(dev);
- retval = pm_runtime_get_sync(ctx->dev);
- if (retval < 0) {
- pm_runtime_put_sync(ctx->dev);
+ retval = pm_runtime_resume_and_get(ctx->dev);
+ if (retval < 0)
return retval;
- }
str_id = sst_get_stream(ctx, str_params);
if (str_id > 0) {
@@ -645,11 +642,9 @@ static int sst_send_byte_stream(struct device *dev,
if (NULL == bytes)
return -EINVAL;
- ret_val = pm_runtime_get_sync(ctx->dev);
- if (ret_val < 0) {
- pm_runtime_put_sync(ctx->dev);
+ ret_val = pm_runtime_resume_and_get(ctx->dev);
+ if (ret_val < 0)
return ret_val;
- }
ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
sst_pm_runtime_put(ctx);
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index f842bfc5e97e..b6b93ae80304 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,6 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o
+snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
+ topology.o path.o pcm.o board_selection.o
snd-soc-avs-objs += cldma.o
+snd-soc-avs-objs += skl.o apl.o
+
+snd-soc-avs-objs += trace.o
+# tell define_trace.h where to find the trace header
+CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
new file mode 100644
index 000000000000..b8e2b23c9f64
--- /dev/null
+++ b/sound/soc/intel/avs/apl.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+#include "path.h"
+#include "topology.h"
+
+static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct apl_log_state_info *info;
+ u32 size, num_cores = adev->hw_cfg.dsp_cores;
+ int ret, i;
+
+ if (fls_long(resource_mask) > num_cores)
+ return -EINVAL;
+ size = struct_size(info, logs_core, num_cores);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->aging_timer_period = aging_period;
+ info->fifo_full_timer_period = fifo_full_period;
+ info->core_mask = resource_mask;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, num_cores) {
+ info->logs_core[i].enable = enable;
+ info->logs_core[i].min_priority = *priorities++;
+ }
+ else
+ for_each_set_bit(i, &resource_mask, num_cores)
+ info->logs_core[i].enable = enable;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
+static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct apl_log_buffer_layout layout;
+ unsigned long flags;
+ void __iomem *addr, *buf;
+
+ addr = avs_log_buffer_addr(adev, msg->log.core);
+ if (!addr)
+ return -ENXIO;
+
+ memcpy_fromio(&layout, addr, sizeof(layout));
+
+ spin_lock_irqsave(&adev->dbg.trace_lock, flags);
+ if (!kfifo_initialized(&adev->dbg.trace_fifo))
+ /* consume the logs regardless of consumer presence */
+ goto update_read_ptr;
+
+ buf = apl_log_payload_addr(addr);
+
+ if (layout.read_ptr > layout.write_ptr) {
+ __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
+ apl_log_payload_size(adev) - layout.read_ptr,
+ &adev->dbg.fifo_lock);
+ layout.read_ptr = 0;
+ }
+ __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
+ layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock);
+
+ wake_up(&adev->dbg.trace_waitq);
+
+update_read_ptr:
+ spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+ writel(layout.write_ptr, addr);
+ return 0;
+}
+
+static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout)
+{
+ unsigned long timeout;
+ void __iomem *addr;
+
+ addr = avs_log_buffer_addr(adev, core);
+ if (!addr)
+ return -ENXIO;
+
+ timeout = jiffies + msecs_to_jiffies(10);
+
+ do {
+ memcpy_fromio(layout, addr, sizeof(*layout));
+ if (layout->read_ptr != layout->write_ptr)
+ return 0;
+ usleep_range(500, 1000);
+ } while (!time_after(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+/* reads log header and tests its type */
+#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
+
+static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct apl_log_buffer_layout layout;
+ void __iomem *addr, *buf;
+ size_t dump_size;
+ u16 offset = 0;
+ u8 *dump, *pos;
+
+ dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size;
+ dump = vzalloc(dump_size);
+ if (!dump)
+ return -ENOMEM;
+
+ memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+
+ if (!msg->ext.coredump.stack_dump_size)
+ goto exit;
+
+ /* Dump the registers even if an external error prevents gathering the stack. */
+ addr = avs_log_buffer_addr(adev, msg->ext.coredump.core_id);
+ if (!addr)
+ goto exit;
+
+ buf = apl_log_payload_addr(addr);
+ memcpy_fromio(&layout, addr, sizeof(layout));
+ if (!apl_is_entry_stackdump(buf + layout.read_ptr)) {
+ /*
+ * DSP awaits the remaining logs to be
+ * gathered before dumping stack
+ */
+ msg->log.core = msg->ext.coredump.core_id;
+ avs_dsp_op(adev, log_buffer_status, msg);
+ }
+
+ pos = dump + AVS_FW_REGS_SIZE;
+ /* gather the stack */
+ do {
+ u32 count;
+
+ if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
+ break;
+
+ if (layout.read_ptr > layout.write_ptr) {
+ count = apl_log_payload_size(adev) - layout.read_ptr;
+ memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+ layout.read_ptr = 0;
+ offset += count;
+ }
+ count = layout.write_ptr - layout.read_ptr;
+ memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+ offset += count;
+
+ /* update read pointer */
+ writel(layout.write_ptr, addr);
+ } while (offset < msg->ext.coredump.stack_dump_size);
+
+exit:
+ dev_coredumpv(adev->dev, dump, dump_size, GFP_KERNEL);
+
+ return 0;
+}
+
+static bool apl_lp_streaming(struct avs_dev *adev)
+{
+ struct avs_path *path;
+
+ /* Any gateway without buffer allocated in LP area disqualifies D0IX. */
+ list_for_each_entry(path, &adev->path_list, node) {
+ struct avs_path_pipeline *ppl;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node) {
+ struct avs_tplg_modcfg_ext *cfg;
+
+ cfg = mod->template->cfg_ext;
+
+ /* only copiers have gateway attributes */
+ if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID))
+ continue;
+ /* non-gateway copiers do not prevent PG */
+ if (cfg->copier.dma_type == INVALID_OBJECT_ID)
+ continue;
+
+ if (!mod->gtw_attrs.lp_buffer_alloc)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+ /* wake in all cases */
+ if (wake)
+ return true;
+
+ /*
+ * If no pipelines are running, allow for d0ix schedule.
+ * If all gateways have lp=1, allow for d0ix schedule.
+ * If any gateway with lp=0 is allocated, abort scheduling d0ix.
+ *
+ * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition,
+ * not the power-gating mechanism known from cAVS 2.0.
+ */
+ return apl_lp_streaming(adev);
+}
+
+static int apl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ bool streaming = false;
+ int ret;
+
+ if (enable)
+ /* Either idle or all gateways with lp=1. */
+ streaming = !list_empty(&adev->path_list);
+
+ ret = avs_ipc_set_d0ix(adev, enable, streaming);
+ return AVS_IPC_RET(ret);
+}
+
+const struct avs_dsp_ops apl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .irq_handler = avs_dsp_irq_handler,
+ .irq_thread = avs_dsp_irq_thread,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .enable_logs = apl_enable_logs,
+ .log_buffer_offset = skl_log_buffer_offset,
+ .log_buffer_status = apl_log_buffer_status,
+ .coredump = apl_coredump,
+ .d0ix_toggle = apl_d0ix_toggle,
+ .set_d0ix = apl_set_d0ix,
+};
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index b48a342fd184..92e37722d280 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -11,12 +11,18 @@
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/kfifo.h>
#include <sound/hda_codec.h>
#include <sound/hda_register.h>
+#include <sound/soc-component.h>
#include "messages.h"
#include "registers.h"
struct avs_dev;
+struct avs_tplg;
+struct avs_tplg_library;
+struct avs_soc_component;
+struct avs_ipc_msg;
/*
* struct avs_dsp_ops - Platform-specific DSP operations
@@ -38,11 +44,21 @@ struct avs_dsp_ops {
int (* const load_basefw)(struct avs_dev *, struct firmware *);
int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32);
+ int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long,
+ u32 *);
+ int (* const log_buffer_offset)(struct avs_dev *, u32);
+ int (* const log_buffer_status)(struct avs_dev *, union avs_notify_msg *);
+ int (* const coredump)(struct avs_dev *, union avs_notify_msg *);
+ bool (* const d0ix_toggle)(struct avs_dev *, struct avs_ipc_msg *, bool);
+ int (* const set_d0ix)(struct avs_dev *, bool);
};
#define avs_dsp_op(adev, op, ...) \
((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
+extern const struct avs_dsp_ops skl_dsp_ops;
+extern const struct avs_dsp_ops apl_dsp_ops;
+
#define AVS_PLATATTR_CLDMA BIT_ULL(0)
#define AVS_PLATATTR_IMR BIT_ULL(1)
@@ -70,6 +86,16 @@ struct avs_fw_entry {
struct list_head node;
};
+struct avs_debug {
+ struct kfifo trace_fifo;
+ spinlock_t fifo_lock; /* serialize I/O for trace_fifo */
+ spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
+ wait_queue_head_t trace_waitq;
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 logged_resources; /* context dependent: core or library */
+};
+
/*
* struct avs_dev - Intel HD-Audio driver data
*
@@ -103,6 +129,16 @@ struct avs_dev {
char **lib_names;
struct completion fw_ready;
+ struct work_struct probe_work;
+
+ struct nhlt_acpi_table *nhlt;
+ struct list_head comp_list;
+ struct mutex comp_list_mutex;
+ struct list_head path_list;
+ spinlock_t path_list_lock;
+ struct mutex path_mutex;
+
+ struct avs_debug dbg;
};
/* from hda_bus to avs_dev */
@@ -153,12 +189,18 @@ struct avs_ipc {
struct avs_ipc_msg rx;
u32 default_timeout_ms;
bool ready;
+ atomic_t recovering;
bool rx_completed;
spinlock_t rx_lock;
struct mutex msg_mutex;
struct completion done_completion;
struct completion busy_completion;
+
+ struct work_struct recovery_work;
+ struct delayed_work d0ix_work;
+ atomic_t d0ix_disable_depth;
+ bool in_d0ix;
};
#define AVS_EIPC EREMOTEIO
@@ -195,6 +237,11 @@ int avs_dsp_send_msg_timeout(struct avs_dev *adev,
struct avs_ipc_msg *reply, int timeout);
int avs_dsp_send_msg(struct avs_dev *adev,
struct avs_ipc_msg *request, struct avs_ipc_msg *reply);
+/* Two variants below are for messages that control DSP power states. */
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0);
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, bool wake_d0i0);
int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev,
struct avs_ipc_msg *request, int timeout);
int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request);
@@ -202,6 +249,11 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable);
int avs_ipc_init(struct avs_ipc *ipc, struct device *dev);
void avs_ipc_block(struct avs_ipc *ipc);
+int avs_dsp_disable_d0ix(struct avs_dev *adev);
+int avs_dsp_enable_d0ix(struct avs_dev *adev);
+
+int skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+
/* Firmware resources management */
int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
@@ -232,6 +284,7 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable);
void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable);
void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs);
int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
int avs_dsp_first_boot_firmware(struct avs_dev *adev);
@@ -244,4 +297,53 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
struct avs_module_entry *mods, u32 num_mods);
+/* Soc component members */
+
+struct avs_soc_component {
+ struct snd_soc_component base;
+ struct avs_tplg *tplg;
+
+ struct list_head node;
+};
+
+#define to_avs_soc_component(comp) \
+ container_of(comp, struct avs_soc_component, base)
+
+extern const struct snd_soc_dai_ops avs_dai_fe_ops;
+
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
+int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms);
+int avs_hda_platform_register(struct avs_dev *adev, const char *name);
+
+int avs_register_all_boards(struct avs_dev *adev);
+void avs_unregister_all_boards(struct avs_dev *adev);
+
+/* Firmware tracing helpers */
+
+unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
+ spinlock_t *lock);
+
+#define avs_log_buffer_size(adev) \
+ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({ \
+ s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
+ (__offset < 0) ? NULL : \
+ (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
+})
+
+struct apl_log_buffer_layout {
+ u32 read_ptr;
+ u32 write_ptr;
+ u8 buffer[];
+} __packed;
+
+#define apl_log_payload_size(adev) \
+ (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout))
+
+#define apl_log_payload_addr(addr) \
+ (addr + sizeof(struct apl_log_buffer_layout))
+
#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
new file mode 100644
index 000000000000..80cb0164678a
--- /dev/null
+++ b/sound/soc/intel/avs/board_selection.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+
+static bool i2s_test;
+module_param(i2s_test, bool, 0444);
+MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards");
+
+static const struct dmi_system_id kbl_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
+ },
+ },
+ {}
+};
+
+static const struct dmi_system_id kblr_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
+ },
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+ const struct dmi_system_id *dmi_id;
+ struct dmi_system_id *dmi_table;
+
+ if (mach->quirk_data == NULL)
+ return mach;
+
+ dmi_table = (struct dmi_system_id *)mach->quirk_data;
+
+ dmi_id = dmi_first_match(dmi_table);
+ if (!dmi_id)
+ return NULL;
+
+ return mach;
+}
+
+#define AVS_SSP(x) (BIT(x))
+#define AVS_SSP_RANGE(a, b) (GENMASK(b, a))
+
+/* supported I2S board codec configurations */
+static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "avs_nau8825",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "nau8825-tplg.bin",
+ },
+ {
+ .id = "INT343B",
+ .drv_name = "avs_ssm4567",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "ssm4567-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kbl_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kblr_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "MX98373",
+ .drv_name = "avs_max98373",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98373-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "INT34C3",
+ .drv_name = "avs_tdf8532",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP_RANGE(0, 5),
+ },
+ .pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
+ .tplg_filename = "tdf8532-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(3),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(4),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
+};
+
+struct avs_acpi_boards {
+ int id;
+ struct snd_soc_acpi_mach *machs;
+};
+
+#define AVS_MACH_ENTRY(_id, _mach) \
+ { .id = (_id), .machs = (_mach), }
+
+/* supported I2S boards per platform */
+static const struct avs_acpi_boards i2s_boards[] = {
+ AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */
+ AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */
+ AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */
+ AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */
+ {},
+};
+
+static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+{
+ int id, i;
+
+ id = adev->base.pci->device;
+ for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
+ if (i2s_boards[i].id == id)
+ return &i2s_boards[i];
+ return NULL;
+}
+
+/* platform devices owned by AVS audio are removed with this hook */
+static void board_pdev_unregister(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int avs_register_dmic_board(struct avs_dev *adev)
+{
+ struct platform_device *codec, *board;
+ struct snd_soc_acpi_mach mach = {{0}};
+ int ret;
+
+ if (!adev->nhlt ||
+ !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
+ dev_dbg(adev->dev, "no DMIC endpoints present\n");
+ return 0;
+ }
+
+ codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(codec)) {
+ dev_err(adev->dev, "dmic codec register failed\n");
+ return PTR_ERR(codec);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
+ if (ret < 0) {
+ platform_device_unregister(codec);
+ return ret;
+ }
+
+ ret = avs_dmic_platform_register(adev, "dmic-platform");
+ if (ret < 0)
+ return ret;
+
+ mach.tplg_filename = "dmic-tplg.bin";
+ mach.mach_params.platform = "dmic-platform";
+
+ board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
+ (const void *)&mach, sizeof(mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "dmic board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+ struct platform_device *board;
+ int num_ssps;
+ char *name;
+ int ret;
+
+ num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+ dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+ num_ssps, mach->drv_name, __fls(mach->mach_params.i2s_link_mask));
+ return -ENODEV;
+ }
+
+ name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
+ mach->mach_params.i2s_link_mask);
+ if (!name)
+ return -ENOMEM;
+
+ ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
+ if (ret < 0)
+ return ret;
+
+ mach->mach_params.platform = name;
+
+ board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
+ (const void *)mach, sizeof(*mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "ssp board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_i2s_boards(struct avs_dev *adev)
+{
+ const struct avs_acpi_boards *boards;
+ struct snd_soc_acpi_mach *mach;
+ int ret;
+
+ if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
+ dev_dbg(adev->dev, "no I2S endpoints present\n");
+ return 0;
+ }
+
+ if (i2s_test) {
+ int i, num_ssps;
+
+ num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ /* constrain just in case FW says there can be more SSPs than possible */
+ num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
+
+ mach = avs_test_i2s_machines;
+
+ for (i = 0; i < num_ssps; i++) {
+ ret = avs_register_i2s_board(adev, &mach[i]);
+ if (ret < 0)
+ dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
+ ret);
+ }
+ return 0;
+ }
+
+ boards = avs_get_i2s_boards(adev);
+ if (!boards) {
+ dev_dbg(adev->dev, "no I2S endpoints supported\n");
+ return 0;
+ }
+
+ for (mach = boards->machs; mach->id[0]; mach++) {
+ if (!acpi_dev_present(mach->id, NULL, -1))
+ continue;
+
+ if (mach->machine_quirk)
+ if (!mach->machine_quirk(mach))
+ continue;
+
+ ret = avs_register_i2s_board(adev, mach);
+ if (ret < 0)
+ dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
+ }
+
+ return 0;
+}
+
+static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
+{
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *board;
+ struct hdac_device *hdev = &codec->core;
+ char *pname;
+ int ret, id;
+
+ pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
+ if (!pname)
+ return -ENOMEM;
+
+ ret = avs_hda_platform_register(adev, pname);
+ if (ret < 0)
+ return ret;
+
+ mach.pdata = codec;
+ mach.mach_params.platform = pname;
+ mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
+ hdev->vendor_id);
+ if (!mach.tplg_filename)
+ return -ENOMEM;
+
+ id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
+ board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
+ sizeof(mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "hda board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_hda_boards(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_device *hdev;
+ int ret;
+
+ if (!bus->num_codecs) {
+ dev_dbg(adev->dev, "no HDA endpoints present\n");
+ return 0;
+ }
+
+ list_for_each_entry(hdev, &bus->codec_list, list) {
+ struct hda_codec *codec;
+
+ codec = dev_to_hda_codec(&hdev->dev);
+
+ ret = avs_register_hda_board(adev, codec);
+ if (ret < 0)
+ dev_warn(adev->dev, "register hda-%08x failed: %d\n",
+ codec->core.vendor_id, ret);
+ }
+
+ return 0;
+}
+
+int avs_register_all_boards(struct avs_dev *adev)
+{
+ int ret;
+
+ ret = avs_register_dmic_board(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
+ ret);
+
+ ret = avs_register_i2s_boards(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
+ ret);
+
+ ret = avs_register_hda_boards(adev);
+ if (ret < 0)
+ dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
+ ret);
+
+ return 0;
+}
+
+void avs_unregister_all_boards(struct avs_dev *adev)
+{
+ snd_soc_unregister_component(adev->dev);
+}
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index a4d063d12fec..3a0997c3af2b 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -14,9 +14,17 @@
// foundation of this driver
//
+#include <linux/module.h>
#include <linux/pci.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
#include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/intel-nhlt.h>
#include "avs.h"
+#include "cldma.h"
static void
avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
@@ -59,3 +67,626 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
value = enable ? AZX_VS_EM2_L1SEN : 0;
snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
}
+
+static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
+{
+ unsigned int cp_streams, pb_streams;
+ unsigned int gcap;
+
+ gcap = snd_hdac_chip_readw(bus, GCAP);
+ cp_streams = (gcap >> 8) & 0x0F;
+ pb_streams = (gcap >> 12) & 0x0F;
+ bus->num_streams = cp_streams + pb_streams;
+
+ snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
+ snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
+
+ return snd_hdac_bus_alloc_stream_pages(bus);
+}
+
+static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+ struct hdac_ext_link *hlink;
+ bool ret;
+
+ avs_hdac_clock_gating_enable(bus, false);
+ ret = snd_hdac_bus_init_chip(bus, full_reset);
+
+ /* Reset stream-to-link mapping */
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
+
+ avs_hdac_clock_gating_enable(bus, true);
+
+ /* Set DUM bit to address incorrect position reporting for capture
+ * streams. In order to do so, CTRL needs to be out of reset state
+ */
+ snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM);
+
+ return ret;
+}
+
+static int probe_codec(struct hdac_bus *bus, int addr)
+{
+ struct hda_codec *codec;
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res = -1;
+ int ret;
+
+ mutex_lock(&bus->cmd_mutex);
+ snd_hdac_bus_send_cmd(bus, cmd);
+ snd_hdac_bus_get_response(bus, addr, &res);
+ mutex_unlock(&bus->cmd_mutex);
+ if (res == -1)
+ return -EIO;
+
+ dev_dbg(bus->dev, "codec #%d probed OK: 0x%x\n", addr, res);
+
+ codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "hdaudioB%dD%d", bus->idx, addr);
+ if (IS_ERR(codec)) {
+ dev_err(bus->dev, "init codec failed: %ld\n", PTR_ERR(codec));
+ return PTR_ERR(codec);
+ }
+ /*
+ * Allow avs_core suspend by forcing suspended state on all
+ * of its codec child devices. Component interested in
+ * dealing with hda codecs directly takes pm responsibilities
+ */
+ pm_runtime_set_suspended(hda_codec_dev(codec));
+
+ /* configure effectively creates new ASoC component */
+ ret = snd_hda_codec_configure(codec);
+ if (ret < 0) {
+ dev_err(bus->dev, "failed to config codec %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus)
+{
+ int c;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < HDA_MAX_CODECS; c++) {
+ if (!(bus->codec_mask & BIT(c)))
+ continue;
+
+ if (!probe_codec(bus, c))
+ /* success, continue probing */
+ continue;
+
+ /*
+ * Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(bus->dev, "Codec #%d probe error; disabling it...\n", c);
+ bus->codec_mask &= ~BIT(c);
+ /*
+ * More badly, accessing to a non-existing
+ * codec often screws up the controller bus,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller bus to get
+ * back to the sanity state.
+ */
+ snd_hdac_bus_stop_chip(bus);
+ avs_hdac_bus_init_chip(bus, true);
+ }
+}
+
+static void avs_hda_probe_work(struct work_struct *work)
+{
+ struct avs_dev *adev = container_of(work, struct avs_dev, probe_work);
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ pm_runtime_set_active(bus->dev); /* clear runtime_error flag */
+
+ ret = snd_hdac_i915_init(bus);
+ if (ret < 0)
+ dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret);
+
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+ avs_hdac_bus_init_chip(bus, true);
+ avs_hdac_bus_probe_codecs(bus);
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ /* with all codecs probed, links can be powered down */
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+
+ snd_hdac_ext_bus_ppcap_enable(bus, true);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+ ret = avs_dsp_first_boot_firmware(adev);
+ if (ret < 0)
+ return;
+
+ adev->nhlt = intel_nhlt_init(adev->dev);
+ if (!adev->nhlt)
+ dev_info(bus->dev, "platform has no NHLT\n");
+
+ avs_register_all_boards(adev);
+
+ /* configure PM */
+ pm_runtime_set_autosuspend_delay(bus->dev, 2000);
+ pm_runtime_use_autosuspend(bus->dev);
+ pm_runtime_mark_last_busy(bus->dev);
+ pm_runtime_put_autosuspend(bus->dev);
+ pm_runtime_allow(bus->dev);
+}
+
+static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size)
+{
+ u64 prev_pos, pos, num_bytes;
+
+ div64_u64_rem(stream->curr_pos, buffer_size, &prev_pos);
+ pos = snd_hdac_stream_get_pos_posbuf(stream);
+
+ if (pos < prev_pos)
+ num_bytes = (buffer_size - prev_pos) + pos;
+ else
+ num_bytes = pos - prev_pos;
+
+ stream->curr_pos += num_bytes;
+}
+
+/* called from IRQ */
+static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream)
+{
+ if (stream->substream) {
+ snd_pcm_period_elapsed(stream->substream);
+ } else if (stream->cstream) {
+ u64 buffer_size = stream->cstream->runtime->buffer_size;
+
+ hdac_stream_update_pos(stream, buffer_size);
+ snd_compr_fragment_elapsed(stream->cstream);
+ }
+}
+
+static irqreturn_t hdac_bus_irq_handler(int irq, void *context)
+{
+ struct hdac_bus *bus = context;
+ u32 mask, int_enable;
+ u32 status;
+ int ret = IRQ_NONE;
+
+ if (!pm_runtime_active(bus->dev))
+ return ret;
+
+ spin_lock(&bus->reg_lock);
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ if (status == 0 || status == UINT_MAX) {
+ spin_unlock(&bus->reg_lock);
+ return ret;
+ }
+
+ /* clear rirb int */
+ status = snd_hdac_chip_readb(bus, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ if (status & RIRB_INT_RESPONSE)
+ snd_hdac_bus_update_rirb(bus);
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+ }
+
+ mask = (0x1 << bus->num_streams) - 1;
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ status &= mask;
+ if (status) {
+ /* Disable stream interrupts; Re-enable in bottom half */
+ int_enable = snd_hdac_chip_readl(bus, INTCTL);
+ snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask)));
+ ret = IRQ_WAKE_THREAD;
+ } else {
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock(&bus->reg_lock);
+ return ret;
+}
+
+static irqreturn_t hdac_bus_irq_thread(int irq, void *context)
+{
+ struct hdac_bus *bus = context;
+ u32 status;
+ u32 int_enable;
+ u32 mask;
+ unsigned long flags;
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+
+ snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream);
+
+ /* Re-enable stream interrupts */
+ mask = (0x1 << bus->num_streams) - 1;
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ int_enable = snd_hdac_chip_readl(bus, INTCTL);
+ snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask));
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int avs_hdac_acquire_irq(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ int ret;
+
+ /* request one and check that we only got one interrupt */
+ ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY);
+ if (ret != 1) {
+ dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret);
+ return ret;
+ }
+
+ ret = pci_request_irq(pci, 0, hdac_bus_irq_handler, hdac_bus_irq_thread, bus,
+ KBUILD_MODNAME);
+ if (ret < 0) {
+ dev_err(adev->dev, "Failed to request stream IRQ handler: %d\n", ret);
+ goto free_vector;
+ }
+
+ ret = pci_request_irq(pci, 0, avs_dsp_irq_handler, avs_dsp_irq_thread, adev,
+ KBUILD_MODNAME);
+ if (ret < 0) {
+ dev_err(adev->dev, "Failed to request IPC IRQ handler: %d\n", ret);
+ goto free_stream_irq;
+ }
+
+ return 0;
+
+free_stream_irq:
+ pci_free_irq(pci, 0, bus);
+free_vector:
+ pci_free_irq_vectors(pci);
+ return ret;
+}
+
+static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct hda_bus *bus = &adev->base;
+ struct avs_ipc *ipc;
+ struct device *dev = &pci->dev;
+ int ret;
+
+ ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ bus->core.use_posbuf = 1;
+ bus->core.bdl_pos_adj = 0;
+ bus->core.sync_write = 1;
+ bus->pci = pci;
+ bus->mixer_assigned = -1;
+ mutex_init(&bus->prepare_mutex);
+
+ ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
+ if (!ipc)
+ return -ENOMEM;
+ ret = avs_ipc_init(ipc, dev);
+ if (ret < 0)
+ return ret;
+
+ adev->dev = dev;
+ adev->spec = (const struct avs_spec *)id->driver_data;
+ adev->ipc = ipc;
+ adev->hw_cfg.dsp_cores = hweight_long(AVS_MAIN_CORE_MASK);
+ INIT_WORK(&adev->probe_work, avs_hda_probe_work);
+ INIT_LIST_HEAD(&adev->comp_list);
+ INIT_LIST_HEAD(&adev->path_list);
+ INIT_LIST_HEAD(&adev->fw_list);
+ init_completion(&adev->fw_ready);
+ spin_lock_init(&adev->path_list_lock);
+ mutex_init(&adev->modres_mutex);
+ mutex_init(&adev->comp_list_mutex);
+ mutex_init(&adev->path_mutex);
+
+ return 0;
+}
+
+static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct hdac_bus *bus;
+ struct avs_dev *adev;
+ struct device *dev = &pci->dev;
+ int ret;
+
+ ret = snd_intel_dsp_driver_probe(pci);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_AVS)
+ return -ENODEV;
+
+ ret = pcim_enable_device(pci);
+ if (ret < 0)
+ return ret;
+
+ adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+ ret = avs_bus_init(adev, pci, id);
+ if (ret < 0) {
+ dev_err(dev, "failed to init avs bus: %d\n", ret);
+ return ret;
+ }
+
+ ret = pci_request_regions(pci, "AVS HDAudio");
+ if (ret < 0)
+ return ret;
+
+ bus = &adev->base.core;
+ bus->addr = pci_resource_start(pci, 0);
+ bus->remap_addr = pci_ioremap_bar(pci, 0);
+ if (!bus->remap_addr) {
+ dev_err(bus->dev, "ioremap error\n");
+ ret = -ENXIO;
+ goto err_remap_bar0;
+ }
+
+ adev->dsp_ba = pci_ioremap_bar(pci, 4);
+ if (!adev->dsp_ba) {
+ dev_err(bus->dev, "ioremap error\n");
+ ret = -ENXIO;
+ goto err_remap_bar4;
+ }
+
+ snd_hdac_bus_parse_capabilities(bus);
+ if (bus->mlcap)
+ snd_hdac_ext_bus_get_ml_capabilities(bus);
+
+ if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ }
+
+ ret = avs_hdac_bus_init_streams(bus);
+ if (ret < 0) {
+ dev_err(dev, "failed to init streams: %d\n", ret);
+ goto err_init_streams;
+ }
+
+ ret = avs_hdac_acquire_irq(adev);
+ if (ret < 0) {
+ dev_err(bus->dev, "failed to acquire irq: %d\n", ret);
+ goto err_acquire_irq;
+ }
+
+ pci_set_master(pci);
+ pci_set_drvdata(pci, bus);
+ device_disable_async_suspend(dev);
+
+ schedule_work(&adev->probe_work);
+
+ return 0;
+
+err_acquire_irq:
+ snd_hdac_bus_free_stream_pages(bus);
+ snd_hdac_stream_free_all(bus);
+err_init_streams:
+ iounmap(adev->dsp_ba);
+err_remap_bar4:
+ iounmap(bus->remap_addr);
+err_remap_bar0:
+ pci_release_regions(pci);
+ return ret;
+}
+
+static void avs_pci_remove(struct pci_dev *pci)
+{
+ struct hdac_device *hdev, *save;
+ struct hdac_bus *bus = pci_get_drvdata(pci);
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ cancel_work_sync(&adev->probe_work);
+ avs_ipc_block(adev->ipc);
+
+ avs_unregister_all_boards(adev);
+
+ if (adev->nhlt)
+ intel_nhlt_free(adev->nhlt);
+
+ if (avs_platattr_test(adev, CLDMA))
+ hda_cldma_free(&code_loader);
+
+ snd_hdac_stop_streams_and_chip(bus);
+ avs_dsp_op(adev, int_control, false);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+ /* it is safe to remove all codecs from the system now */
+ list_for_each_entry_safe(hdev, save, &bus->codec_list, list)
+ snd_hda_codec_unregister(hdac_to_hda_codec(hdev));
+
+ snd_hdac_bus_free_stream_pages(bus);
+ snd_hdac_stream_free_all(bus);
+ /* reverse ml_capabilities */
+ snd_hdac_link_free_all(bus);
+ snd_hdac_ext_bus_exit(bus);
+
+ avs_dsp_core_disable(adev, GENMASK(adev->hw_cfg.dsp_cores - 1, 0));
+ snd_hdac_ext_bus_ppcap_enable(bus, false);
+
+ /* snd_hdac_stop_streams_and_chip does that already? */
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+ if (bus->audio_component)
+ snd_hdac_i915_exit(bus);
+
+ avs_module_info_free(adev);
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
+ iounmap(bus->remap_addr);
+ iounmap(adev->dsp_ba);
+ pci_release_regions(pci);
+
+ /* Firmware is not needed anymore */
+ avs_release_firmwares(adev);
+
+ /* pm_runtime_forbid() can rpm_resume() which we do not want */
+ pm_runtime_disable(&pci->dev);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_enable(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+}
+
+static int __maybe_unused avs_suspend_common(struct avs_dev *adev)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ int ret;
+
+ flush_work(&adev->probe_work);
+
+ snd_hdac_ext_bus_link_power_down_all(bus);
+
+ ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false);
+ /*
+ * pm_runtime is blocked on DSP failure but system-wide suspend is not.
+ * Do not block entire system from suspending if that's the case.
+ */
+ if (ret && ret != -EPERM) {
+ dev_err(adev->dev, "set dx failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+
+ avs_dsp_op(adev, int_control, false);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+ ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ if (ret < 0) {
+ dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret);
+ return ret;
+ }
+
+ snd_hdac_ext_bus_ppcap_enable(bus, false);
+ /* disable LP SRAM retention */
+ avs_hda_power_gating_enable(adev, false);
+ snd_hdac_bus_stop_chip(bus);
+ /* disable CG when putting controller to reset */
+ avs_hdac_clock_gating_enable(bus, false);
+ snd_hdac_bus_enter_link_reset(bus);
+ avs_hdac_clock_gating_enable(bus, true);
+
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+ return 0;
+}
+
+static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+ avs_hdac_bus_init_chip(bus, true);
+
+ snd_hdac_ext_bus_ppcap_enable(bus, true);
+ snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+ ret = avs_dsp_boot_firmware(adev, purge);
+ if (ret < 0) {
+ dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+ return ret;
+ }
+
+ /* turn off the links that were off before suspend */
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ if (!hlink->ref_count)
+ snd_hdac_ext_bus_link_power_down(hlink);
+ }
+
+ /* check dma status and clean up CORB/RIRB buffers */
+ if (!bus->cmd_dma_state)
+ snd_hdac_bus_stop_cmd_io(bus);
+
+ return 0;
+}
+
+static int __maybe_unused avs_suspend(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev));
+}
+
+static int __maybe_unused avs_resume(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), true);
+}
+
+static int __maybe_unused avs_runtime_suspend(struct device *dev)
+{
+ return avs_suspend_common(to_avs_dev(dev));
+}
+
+static int __maybe_unused avs_runtime_resume(struct device *dev)
+{
+ return avs_resume_common(to_avs_dev(dev), true);
+}
+
+static const struct dev_pm_ops avs_dev_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(avs_suspend, avs_resume)
+ SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
+};
+
+static const struct avs_spec skl_desc = {
+ .name = "skl",
+ .min_fw_version = {
+ .major = 9,
+ .minor = 21,
+ .hotfix = 0,
+ .build = 4732,
+ },
+ .dsp_ops = &skl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_CLDMA,
+ .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+ .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
+ .rom_status = SKL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_spec apl_desc = {
+ .name = "apl",
+ .min_fw_version = {
+ .major = 9,
+ .minor = 22,
+ .hotfix = 1,
+ .build = 4323,
+ },
+ .dsp_ops = &apl_dsp_ops,
+ .core_init_mask = 3,
+ .attributes = AVS_PLATATTR_IMR,
+ .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET,
+ .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE,
+ .rom_status = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct pci_device_id avs_ids[] = {
+ { PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */
+ { PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */
+ { PCI_VDEVICE(INTEL, 0x5a98), (unsigned long)&apl_desc }, /* APL */
+ { PCI_VDEVICE(INTEL, 0x3198), (unsigned long)&apl_desc }, /* GML */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, avs_ids);
+
+static struct pci_driver avs_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = avs_ids,
+ .probe = avs_pci_probe,
+ .remove = avs_pci_remove,
+ .driver = {
+ .pm = &avs_dev_pm,
+ },
+};
+module_pci_driver(avs_pci_driver);
+
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>");
+MODULE_DESCRIPTION("Intel cAVS sound driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index 3ff17bd22a5a..06d2f7af520f 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -6,10 +6,10 @@
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
-#include <linux/module.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
#include "registers.h"
+#include "trace.h"
#define AVS_ADSPCS_INTERVAL_US 500
#define AVS_ADSPCS_TIMEOUT_US 50000
@@ -19,6 +19,9 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
u32 value, mask, reg;
int ret;
+ value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+ trace_avs_dsp_core_op(value, core_mask, "power", power);
+
mask = AVS_ADSPCS_SPA_MASK(core_mask);
value = power ? mask : 0;
@@ -43,6 +46,9 @@ int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
u32 value, mask, reg;
int ret;
+ value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+ trace_avs_dsp_core_op(value, core_mask, "reset", reset);
+
mask = AVS_ADSPCS_CRST_MASK(core_mask);
value = reset ? mask : 0;
@@ -64,6 +70,9 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
u32 value, mask, reg;
int ret;
+ value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+ trace_avs_dsp_core_op(value, core_mask, "stall", stall);
+
mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
value = stall ? mask : 0;
@@ -152,6 +161,15 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
adev->core_refs[core_id]++;
if (adev->core_refs[core_id] == 1) {
+ /*
+ * No cores other than main-core can be running for DSP
+ * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
+ * simply d0ix power state will no longer be attempted.
+ */
+ ret = avs_dsp_disable_d0ix(adev);
+ if (ret && ret != -AVS_EIPC)
+ goto err_disable_d0ix;
+
ret = avs_dsp_enable(adev, mask);
if (ret)
goto err_enable_dsp;
@@ -160,6 +178,8 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
return 0;
err_enable_dsp:
+ avs_dsp_enable_d0ix(adev);
+err_disable_d0ix:
adev->core_refs[core_id]--;
err:
dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
@@ -185,6 +205,9 @@ static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
ret = avs_dsp_disable(adev, mask);
if (ret)
goto err;
+
+ /* Match disable_d0ix in avs_dsp_get_core(). */
+ avs_dsp_enable_d0ix(adev);
}
return 0;
@@ -298,5 +321,3 @@ int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
ida_free(&adev->ppl_ida, instance_id);
return ret;
}
-
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 68aaf01edbf2..d755ba8b8518 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -6,18 +6,185 @@
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
#include "messages.h"
#include "registers.h"
+#include "trace.h"
#define AVS_IPC_TIMEOUT_MS 300
+#define AVS_D0IX_DELAY_MS 300
+
+static int
+avs_dsp_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ struct avs_ipc *ipc = adev->ipc;
+ int ret;
+
+ /* Is transition required? */
+ if (ipc->in_d0ix == enable)
+ return 0;
+
+ ret = avs_dsp_op(adev, set_d0ix, enable);
+ if (ret) {
+ /* Prevent further d0ix attempts on conscious IPC failure. */
+ if (ret == -AVS_EIPC)
+ atomic_inc(&ipc->d0ix_disable_depth);
+
+ ipc->in_d0ix = false;
+ return ret;
+ }
+
+ ipc->in_d0ix = enable;
+ return 0;
+}
+
+static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+ if (atomic_read(&adev->ipc->d0ix_disable_depth))
+ return;
+
+ mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work,
+ msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+}
+
+static void avs_dsp_d0ix_work(struct work_struct *work)
+{
+ struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work);
+
+ avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true);
+}
+
+static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ if (!atomic_read(&ipc->d0ix_disable_depth)) {
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ return avs_dsp_set_d0ix(adev, false);
+ }
+
+ return 0;
+}
+
+int avs_dsp_disable_d0ix(struct avs_dev *adev)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ /* Prevent PG only on the first disable. */
+ if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) {
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ return avs_dsp_set_d0ix(adev, false);
+ }
+
+ return 0;
+}
+
+int avs_dsp_enable_d0ix(struct avs_dev *adev)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ if (atomic_dec_and_test(&ipc->d0ix_disable_depth))
+ queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work,
+ msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+ return 0;
+}
+
+static void avs_dsp_recovery(struct avs_dev *adev)
+{
+ struct avs_soc_component *acomp;
+ unsigned int core_mask;
+ int ret;
+
+ mutex_lock(&adev->comp_list_mutex);
+ /* disconnect all running streams */
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_card *card;
+
+ card = acomp->base.card;
+ if (!card)
+ continue;
+
+ for_each_card_rtds(card, rtd) {
+ struct snd_pcm *pcm;
+ int dir;
+
+ pcm = rtd->pcm;
+ if (!pcm || rtd->dai_link->no_pcm)
+ continue;
+
+ for_each_pcm_streams(dir) {
+ struct snd_pcm_substream *substream;
+
+ substream = pcm->streams[dir].substream;
+ if (!substream || !substream->runtime)
+ continue;
+
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+ }
+ }
+ }
+ mutex_unlock(&adev->comp_list_mutex);
+
+ /* forcibly shutdown all cores */
+ core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
+ avs_dsp_core_disable(adev, core_mask);
+
+ /* attempt dsp reboot */
+ ret = avs_dsp_boot_firmware(adev, true);
+ if (ret < 0)
+ dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
+
+ pm_runtime_mark_last_busy(adev->dev);
+ pm_runtime_enable(adev->dev);
+ pm_request_autosuspend(adev->dev);
+
+ atomic_set(&adev->ipc->recovering, 0);
+}
+
+static void avs_dsp_recovery_work(struct work_struct *work)
+{
+ struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work);
+
+ avs_dsp_recovery(to_avs_dev(ipc->dev));
+}
+
+static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct avs_ipc *ipc = adev->ipc;
+
+ /* Account for the double-exception case. */
+ ipc->ready = false;
+
+ if (!atomic_add_unless(&ipc->recovering, 1, 1)) {
+ dev_err(adev->dev, "dsp recovery is already in progress\n");
+ return;
+ }
+
+ dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
+
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ ipc->in_d0ix = false;
+ /* Re-enabled on recovery completion. */
+ pm_runtime_disable(adev->dev);
+
+ /* Process received notification. */
+ avs_dsp_op(adev, coredump, msg);
+
+ schedule_work(&ipc->recovery_work);
+}
static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
{
struct avs_ipc *ipc = adev->ipc;
union avs_reply_msg msg = AVS_MSG(header);
+ u64 reg;
+
+ reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+ trace_avs_ipc_reply_msg(header, reg);
ipc->rx.header = header;
/* Abort copying payload if request processing was unsuccessful. */
@@ -28,6 +195,7 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
ipc->rx.size = msg.ext.large_config.data_off_size;
memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size);
+ trace_avs_msg_payload(ipc->rx.data, ipc->rx.size);
}
}
@@ -37,6 +205,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
union avs_notify_msg msg = AVS_MSG(header);
size_t data_size = 0;
void *data = NULL;
+ u64 reg;
+
+ reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+ trace_avs_ipc_notify_msg(header, reg);
/* Ignore spurious notifications until handshake is established. */
if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
@@ -57,6 +229,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
data_size = sizeof(struct avs_notify_res_data);
break;
+ case AVS_NOTIFY_LOG_BUFFER_STATUS:
+ case AVS_NOTIFY_EXCEPTION_CAUGHT:
+ break;
+
case AVS_NOTIFY_MODULE_EVENT:
/* To know the total payload size, header needs to be read first. */
memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
@@ -74,6 +250,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
return;
memcpy_fromio(data, avs_uplink_addr(adev), data_size);
+ trace_avs_msg_payload(data, data_size);
}
/* Perform notification-specific operations. */
@@ -84,6 +261,14 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
complete(&adev->fw_ready);
break;
+ case AVS_NOTIFY_LOG_BUFFER_STATUS:
+ avs_dsp_op(adev, log_buffer_status, &msg);
+ break;
+
+ case AVS_NOTIFY_EXCEPTION_CAUGHT:
+ avs_dsp_exception_caught(adev, &msg);
+ break;
+
default:
break;
}
@@ -249,9 +434,15 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply)
reinit_completion(&ipc->busy_completion);
}
-static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx)
+static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs)
{
+ u64 reg = ULONG_MAX;
+
tx->header |= SKL_ADSP_HIPCI_BUSY;
+ if (read_fwregs)
+ reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+
+ trace_avs_request(tx, reg);
if (tx->size)
memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size);
@@ -272,15 +463,16 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
spin_lock(&ipc->rx_lock);
avs_ipc_msg_init(ipc, reply);
- avs_dsp_send_tx(adev, request);
+ avs_dsp_send_tx(adev, request, true);
spin_unlock(&ipc->rx_lock);
ret = avs_ipc_wait_busy_completion(ipc, timeout);
if (ret) {
if (ret == -ETIMEDOUT) {
- dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n", ret);
+ union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT);
- avs_ipc_block(ipc);
+ /* Same treatment as on exception, just stack_dump=0. */
+ avs_dsp_exception_caught(adev, &msg);
}
goto exit;
}
@@ -297,10 +489,37 @@ exit:
return ret;
}
+static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+ bool schedule_d0ix)
+{
+ int ret;
+
+ trace_avs_d0ix("wake", wake_d0i0, request->header);
+ if (wake_d0i0) {
+ ret = avs_dsp_wake_d0i0(adev, request);
+ if (ret)
+ return ret;
+ }
+
+ ret = avs_dsp_do_send_msg(adev, request, reply, timeout);
+ if (ret)
+ return ret;
+
+ trace_avs_d0ix("schedule", schedule_d0ix, request->header);
+ if (schedule_d0ix)
+ avs_dsp_schedule_d0ix(adev, request);
+
+ return 0;
+}
+
int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
struct avs_ipc_msg *reply, int timeout)
{
- return avs_dsp_do_send_msg(adev, request, reply, timeout);
+ bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true);
+ bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false);
+
+ return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix);
}
int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
@@ -309,6 +528,19 @@ int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms);
}
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0)
+{
+ return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false);
+}
+
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, bool wake_d0i0)
+{
+ return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms,
+ wake_d0i0);
+}
+
static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout)
{
struct avs_ipc *ipc = adev->ipc;
@@ -318,7 +550,11 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req
spin_lock(&ipc->rx_lock);
avs_ipc_msg_init(ipc, NULL);
- avs_dsp_send_tx(adev, request);
+ /*
+ * with hw still stalled, memory windows may not be
+ * configured properly so avoid accessing SRAM
+ */
+ avs_dsp_send_tx(adev, request, false);
spin_unlock(&ipc->rx_lock);
/* ROM messages must be sent before main core is unstalled */
@@ -368,6 +604,8 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
ipc->dev = dev;
ipc->ready = false;
ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
+ INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
+ INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work);
init_completion(&ipc->done_completion);
init_completion(&ipc->busy_completion);
spin_lock_init(&ipc->rx_lock);
@@ -379,4 +617,7 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
void avs_ipc_block(struct avs_ipc *ipc)
{
ipc->ready = false;
+ cancel_work_sync(&ipc->recovery_work);
+ cancel_delayed_work_sync(&ipc->d0ix_work);
+ ipc->in_d0ix = false;
}
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index c47f85161d95..542fd44aa501 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -15,6 +15,7 @@
#include "cldma.h"
#include "messages.h"
#include "registers.h"
+#include "topology.h"
#define AVS_ROM_STS_MASK 0xFF
#define AVS_ROM_INIT_DONE 0x1
@@ -36,6 +37,8 @@
#define AVS_EXT_MANIFEST_MAGIC 0x31454124
#define SKL_MANIFEST_MAGIC 0x00000006
#define SKL_ADSPFW_OFFSET 0x284
+#define APL_MANIFEST_MAGIC 0x44504324
+#define APL_ADSPFW_OFFSET 0x2000
/* Occasionally, engineering (release candidate) firmware is provided for testing. */
static bool debug_ignore_fw_version;
@@ -86,6 +89,8 @@ static int avs_fw_manifest_offset(struct firmware *fw)
switch (magic) {
case SKL_MANIFEST_MAGIC:
return SKL_ADSPFW_OFFSET;
+ case APL_MANIFEST_MAGIC:
+ return APL_ADSPFW_OFFSET;
default:
return -EINVAL;
}
@@ -466,6 +471,71 @@ int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
return 0;
}
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs)
+{
+ int start, id, i = 0;
+ int ret;
+
+ /* Calculate the id to assign for the next lib. */
+ for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
+ if (adev->lib_names[id][0] == '\0')
+ break;
+ if (id + num_libs >= adev->fw_cfg.max_libs_count)
+ return -EINVAL;
+
+ start = id;
+ while (i < num_libs) {
+ struct avs_fw_manifest *man;
+ const struct firmware *fw;
+ struct firmware stripped_fw;
+ char *filename;
+ int j;
+
+ filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name,
+ libs[i].name);
+ if (!filename)
+ return -ENOMEM;
+
+ /*
+ * If any call after this one fails, requested firmware is not released with
+ * avs_release_last_firmware() as failing to load code results in need for reload
+ * of entire driver module. And then avs_release_firmwares() is in place already.
+ */
+ ret = avs_request_firmware(adev, &fw, filename);
+ kfree(filename);
+ if (ret < 0)
+ return ret;
+
+ stripped_fw = *fw;
+ ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
+ if (ret) {
+ dev_err(adev->dev, "invalid library data: %d\n", ret);
+ return ret;
+ }
+
+ ret = avs_fw_manifest_offset(&stripped_fw);
+ if (ret < 0)
+ return ret;
+ man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
+
+ /* Don't load anything that's already in DSP memory. */
+ for (j = 0; j < id; j++)
+ if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE))
+ goto next_lib;
+
+ ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
+ if (ret)
+ return ret;
+
+ strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
+ id++;
+next_lib:
+ i++;
+ }
+
+ return start == id ? 1 : 0;
+}
+
static int avs_dsp_load_basefw(struct avs_dev *adev)
{
const struct avs_fw_version *min_req;
@@ -519,6 +589,7 @@ release_fw:
int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
{
+ struct avs_soc_component *acomp;
int ret, i;
/* Forgo full boot if flash from IMR succeeds. */
@@ -538,7 +609,20 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
avs_hda_l1sen_enable(adev, false);
ret = avs_dsp_load_basefw(adev);
+ if (ret)
+ goto reenable_gating;
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ struct avs_tplg *tplg = acomp->tplg;
+
+ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+ if (ret < 0)
+ break;
+ }
+ mutex_unlock(&adev->comp_list_mutex);
+reenable_gating:
avs_hda_l1sen_enable(adev, true);
avs_hda_clock_gating_enable(adev, true);
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 004da166a943..6404fce8cde4 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -432,7 +432,7 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
request.data = &dx;
request.size = sizeof(dx);
- ret = avs_dsp_send_msg(adev, &request, NULL);
+ ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
if (ret)
avs_ipc_err(adev, &request, "set dx", ret);
@@ -456,7 +456,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
request.header = msg.val;
- ret = avs_dsp_send_msg(adev, &request, NULL);
+ ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
if (ret)
avs_ipc_err(adev, &request, "set d0ix", ret);
@@ -677,6 +677,37 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
return 0;
}
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
+{
+ int ret;
+
+ ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_ENABLE_LOGS, log_info, size);
+ if (ret)
+ dev_err(adev->dev, "enable logs failed: %d\n", ret);
+
+ return ret;
+}
+
+int avs_ipc_set_system_time(struct avs_dev *adev)
+{
+ struct avs_sys_time sys_time;
+ int ret;
+ u64 us;
+
+ /* firmware expects UTC time in micro seconds */
+ us = ktime_to_us(ktime_get());
+ sys_time.val_l = us & UINT_MAX;
+ sys_time.val_u = us >> 32;
+
+ ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
+ if (ret)
+ dev_err(adev->dev, "set system time failed: %d\n", ret);
+
+ return ret;
+}
+
int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
u8 instance_id, u32 sink_id,
const struct avs_audio_format *src_fmt,
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 0395dd7150eb..c0f90dba9af8 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -186,7 +186,9 @@ union avs_reply_msg {
enum avs_notify_msg_type {
AVS_NOTIFY_PHRASE_DETECTED = 4,
AVS_NOTIFY_RESOURCE_EVENT = 5,
+ AVS_NOTIFY_LOG_BUFFER_STATUS = 6,
AVS_NOTIFY_FW_READY = 8,
+ AVS_NOTIFY_EXCEPTION_CAUGHT = 10,
AVS_NOTIFY_MODULE_EVENT = 12,
};
@@ -202,9 +204,17 @@ union avs_notify_msg {
u32 msg_direction:1;
u32 msg_target:1;
};
+ struct {
+ u16 rsvd:12;
+ u16 core:4;
+ } log;
};
union {
u32 val;
+ struct {
+ u32 core_id:2;
+ u32 stack_dump_size:16;
+ } coredump;
} ext;
};
} __packed;
@@ -324,12 +334,46 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
#define AVS_BASEFW_INST_ID 0
enum avs_basefw_runtime_param {
+ AVS_BASEFW_ENABLE_LOGS = 6,
AVS_BASEFW_FIRMWARE_CONFIG = 7,
AVS_BASEFW_HARDWARE_CONFIG = 8,
AVS_BASEFW_MODULES_INFO = 9,
AVS_BASEFW_LIBRARIES_INFO = 16,
+ AVS_BASEFW_SYSTEM_TIME = 20,
+};
+
+enum avs_log_enable {
+ AVS_LOG_DISABLE = 0,
+ AVS_LOG_ENABLE = 1
};
+enum avs_skl_log_priority {
+ AVS_SKL_LOG_CRITICAL = 1,
+ AVS_SKL_LOG_HIGH,
+ AVS_SKL_LOG_MEDIUM,
+ AVS_SKL_LOG_LOW,
+ AVS_SKL_LOG_VERBOSE,
+};
+
+struct skl_log_state {
+ u32 enable;
+ u32 min_priority;
+} __packed;
+
+struct skl_log_state_info {
+ u32 core_mask;
+ struct skl_log_state logs_core[];
+} __packed;
+
+struct apl_log_state_info {
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 core_mask;
+ struct skl_log_state logs_core[];
+} __packed;
+
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
+
struct avs_fw_version {
u16 major;
u16 minor;
@@ -497,6 +541,13 @@ static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry)
int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info);
+struct avs_sys_time {
+ u32 val_l;
+ u32 val_u;
+} __packed;
+
+int avs_ipc_set_system_time(struct avs_dev *adev);
+
/* Module configuration */
#define AVS_MIXIN_MOD_UUID \
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
new file mode 100644
index 000000000000..3d46dd5e5bc4
--- /dev/null
+++ b/sound/soc/intel/avs/path.c
@@ -0,0 +1,1005 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <sound/intel-nhlt.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "avs.h"
+#include "path.h"
+#include "topology.h"
+
+/* Must be called with adev->comp_list_mutex held. */
+static struct avs_tplg *
+avs_path_find_tplg(struct avs_dev *adev, const char *name)
+{
+ struct avs_soc_component *acomp;
+
+ list_for_each_entry(acomp, &adev->comp_list, node)
+ if (!strcmp(acomp->tplg->name, name))
+ return acomp->tplg;
+ return NULL;
+}
+
+static struct avs_path_module *
+avs_path_find_module(struct avs_path_pipeline *ppl, u32 template_id)
+{
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node)
+ if (mod->template->id == template_id)
+ return mod;
+ return NULL;
+}
+
+static struct avs_path_pipeline *
+avs_path_find_pipeline(struct avs_path *path, u32 template_id)
+{
+ struct avs_path_pipeline *ppl;
+
+ list_for_each_entry(ppl, &path->ppl_list, node)
+ if (ppl->template->id == template_id)
+ return ppl;
+ return NULL;
+}
+
+static struct avs_path *
+avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id)
+{
+ struct avs_tplg_path_template *pos, *template = NULL;
+ struct avs_tplg *tplg;
+ struct avs_path *path;
+
+ tplg = avs_path_find_tplg(adev, name);
+ if (!tplg)
+ return NULL;
+
+ list_for_each_entry(pos, &tplg->path_tmpl_list, node) {
+ if (pos->id == template_id) {
+ template = pos;
+ break;
+ }
+ }
+ if (!template)
+ return NULL;
+
+ spin_lock(&adev->path_list_lock);
+ /* Only one variant of given path template may be instantiated at a time. */
+ list_for_each_entry(path, &adev->path_list, node) {
+ if (path->template->owner == template) {
+ spin_unlock(&adev->path_list_lock);
+ return path;
+ }
+ }
+
+ spin_unlock(&adev->path_list_lock);
+ return NULL;
+}
+
+static bool avs_test_hw_params(struct snd_pcm_hw_params *params,
+ struct avs_audio_format *fmt)
+{
+ return (params_rate(params) == fmt->sampling_freq &&
+ params_channels(params) == fmt->num_channels &&
+ params_physical_width(params) == fmt->bit_depth &&
+ params_width(params) == fmt->valid_bit_depth);
+}
+
+static struct avs_tplg_path *
+avs_path_find_variant(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params)
+{
+ struct avs_tplg_path *variant;
+
+ list_for_each_entry(variant, &template->path_list, node) {
+ dev_dbg(adev->dev, "check FE rate %d chn %d vbd %d bd %d\n",
+ variant->fe_fmt->sampling_freq, variant->fe_fmt->num_channels,
+ variant->fe_fmt->valid_bit_depth, variant->fe_fmt->bit_depth);
+ dev_dbg(adev->dev, "check BE rate %d chn %d vbd %d bd %d\n",
+ variant->be_fmt->sampling_freq, variant->be_fmt->num_channels,
+ variant->be_fmt->valid_bit_depth, variant->be_fmt->bit_depth);
+
+ if (variant->fe_fmt && avs_test_hw_params(fe_params, variant->fe_fmt) &&
+ variant->be_fmt && avs_test_hw_params(be_params, variant->be_fmt))
+ return variant;
+ }
+
+ return NULL;
+}
+
+__maybe_unused
+static bool avs_dma_type_is_host(u32 dma_type)
+{
+ return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
+ dma_type == AVS_DMA_HDA_HOST_INPUT;
+}
+
+__maybe_unused
+static bool avs_dma_type_is_link(u32 dma_type)
+{
+ return !avs_dma_type_is_host(dma_type);
+}
+
+__maybe_unused
+static bool avs_dma_type_is_output(u32 dma_type)
+{
+ return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
+ dma_type == AVS_DMA_HDA_LINK_OUTPUT ||
+ dma_type == AVS_DMA_I2S_LINK_OUTPUT;
+}
+
+__maybe_unused
+static bool avs_dma_type_is_input(u32 dma_type)
+{
+ return !avs_dma_type_is_output(dma_type);
+}
+
+static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct nhlt_acpi_table *nhlt = adev->nhlt;
+ struct avs_tplg_module *t = mod->template;
+ struct avs_copier_cfg *cfg;
+ struct nhlt_specific_cfg *ep_blob;
+ union avs_connector_node_id node_id = {0};
+ size_t cfg_size, data_size = 0;
+ void *data = NULL;
+ u32 dma_type;
+ int ret;
+
+ dma_type = t->cfg_ext->copier.dma_type;
+ node_id.dma_type = dma_type;
+
+ switch (dma_type) {
+ struct avs_audio_format *fmt;
+ int direction;
+
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ case AVS_DMA_I2S_LINK_INPUT:
+ if (avs_dma_type_is_input(dma_type))
+ direction = SNDRV_PCM_STREAM_CAPTURE;
+ else
+ direction = SNDRV_PCM_STREAM_PLAYBACK;
+
+ if (t->cfg_ext->copier.blob_fmt)
+ fmt = t->cfg_ext->copier.blob_fmt;
+ else if (direction == SNDRV_PCM_STREAM_CAPTURE)
+ fmt = t->in_fmt;
+ else
+ fmt = t->cfg_ext->copier.out_fmt;
+
+ ep_blob = intel_nhlt_get_endpoint_blob(adev->dev,
+ nhlt, t->cfg_ext->copier.vindex.i2s.instance,
+ NHLT_LINK_SSP, fmt->valid_bit_depth, fmt->bit_depth,
+ fmt->num_channels, fmt->sampling_freq, direction,
+ NHLT_DEVICE_I2S);
+ if (!ep_blob) {
+ dev_err(adev->dev, "no I2S ep_blob found\n");
+ return -ENOENT;
+ }
+
+ data = ep_blob->caps;
+ data_size = ep_blob->size;
+ /* I2S gateway's vindex is statically assigned in topology */
+ node_id.vindex = t->cfg_ext->copier.vindex.val;
+
+ break;
+
+ case AVS_DMA_DMIC_LINK_INPUT:
+ direction = SNDRV_PCM_STREAM_CAPTURE;
+
+ if (t->cfg_ext->copier.blob_fmt)
+ fmt = t->cfg_ext->copier.blob_fmt;
+ else
+ fmt = t->in_fmt;
+
+ ep_blob = intel_nhlt_get_endpoint_blob(adev->dev, nhlt, 0,
+ NHLT_LINK_DMIC, fmt->valid_bit_depth,
+ fmt->bit_depth, fmt->num_channels,
+ fmt->sampling_freq, direction, NHLT_DEVICE_DMIC);
+ if (!ep_blob) {
+ dev_err(adev->dev, "no DMIC ep_blob found\n");
+ return -ENOENT;
+ }
+
+ data = ep_blob->caps;
+ data_size = ep_blob->size;
+ /* DMIC gateway's vindex is statically assigned in topology */
+ node_id.vindex = t->cfg_ext->copier.vindex.val;
+
+ break;
+
+ case AVS_DMA_HDA_HOST_OUTPUT:
+ case AVS_DMA_HDA_HOST_INPUT:
+ /* HOST gateway's vindex is dynamically assigned with DMA id */
+ node_id.vindex = mod->owner->owner->dma_id;
+ break;
+
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ node_id.vindex = t->cfg_ext->copier.vindex.val |
+ mod->owner->owner->dma_id;
+ break;
+
+ case INVALID_OBJECT_ID:
+ default:
+ node_id = INVALID_NODE_ID;
+ break;
+ }
+
+ cfg_size = sizeof(*cfg) + data_size;
+ /* Every config-BLOB contains gateway attributes. */
+ if (data_size)
+ cfg_size -= sizeof(cfg->gtw_cfg.config.attrs);
+
+ cfg = kzalloc(cfg_size, GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->out_fmt = *t->cfg_ext->copier.out_fmt;
+ cfg->feature_mask = t->cfg_ext->copier.feature_mask;
+ cfg->gtw_cfg.node_id = node_id;
+ cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size;
+ /* config_length in DWORDs */
+ cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4);
+ if (data)
+ memcpy(&cfg->gtw_cfg.config, data, data_size);
+
+ mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, cfg, cfg_size,
+ &mod->instance_id);
+ kfree(cfg);
+ return ret;
+}
+
+static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_updown_mixer_cfg cfg;
+ int i;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_channel_config = t->cfg_ext->updown_mix.out_channel_config;
+ cfg.coefficients_select = t->cfg_ext->updown_mix.coefficients_select;
+ for (i = 0; i < AVS_CHANNELS_MAX; i++)
+ cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i];
+ cfg.channel_map = t->cfg_ext->updown_mix.channel_map;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_src_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_src_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_freq = t->cfg_ext->src.out_freq;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_asrc_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_asrc_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_freq = t->cfg_ext->asrc.out_freq;
+ cfg.mode = t->cfg_ext->asrc.mode;
+ cfg.disable_jitter_buffer = t->cfg_ext->asrc.disable_jitter_buffer;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_aec_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_aec_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.ref_fmt = *t->cfg_ext->aec.ref_fmt;
+ cfg.out_fmt = *t->cfg_ext->aec.out_fmt;
+ cfg.cpc_lp_mode = t->cfg_ext->aec.cpc_lp_mode;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_mux_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_mux_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.ref_fmt = *t->cfg_ext->mux.ref_fmt;
+ cfg.out_fmt = *t->cfg_ext->mux.out_fmt;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_wov_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_wov_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.cpc_lp_mode = t->cfg_ext->wov.cpc_lp_mode;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_micsel_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_micsel_cfg cfg;
+
+ cfg.base.cpc = t->cfg_base->cpc;
+ cfg.base.ibs = t->cfg_base->ibs;
+ cfg.base.obs = t->cfg_base->obs;
+ cfg.base.is_pages = t->cfg_base->is_pages;
+ cfg.base.audio_fmt = *t->in_fmt;
+ cfg.out_fmt = *t->cfg_ext->micsel.out_fmt;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_modbase_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_modcfg_base cfg;
+
+ cfg.cpc = t->cfg_base->cpc;
+ cfg.ibs = t->cfg_base->ibs;
+ cfg.obs = t->cfg_base->obs;
+ cfg.is_pages = t->cfg_base->is_pages;
+ cfg.audio_fmt = *t->in_fmt;
+
+ return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, &cfg, sizeof(cfg),
+ &mod->instance_id);
+}
+
+static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *tcfg = t->cfg_ext;
+ struct avs_modcfg_ext *cfg;
+ size_t cfg_size, num_pins;
+ int ret, i;
+
+ num_pins = tcfg->generic.num_input_pins + tcfg->generic.num_output_pins;
+ cfg_size = sizeof(*cfg) + sizeof(*cfg->pin_fmts) * num_pins;
+
+ cfg = kzalloc(cfg_size, GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->num_input_pins = tcfg->generic.num_input_pins;
+ cfg->num_output_pins = tcfg->generic.num_output_pins;
+
+ /* configure pin formats */
+ for (i = 0; i < num_pins; i++) {
+ struct avs_tplg_pin_format *tpin = &tcfg->generic.pin_fmts[i];
+ struct avs_pin_format *pin = &cfg->pin_fmts[i];
+
+ pin->pin_index = tpin->pin_index;
+ pin->iobs = tpin->iobs;
+ pin->audio_fmt = *tpin->fmt;
+ }
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
+ t->core_id, t->domain, cfg, cfg_size,
+ &mod->instance_id);
+ kfree(cfg);
+ return ret;
+}
+
+static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ const guid_t *type = &mod->template->cfg_ext->type;
+
+ if (guid_equal(type, &AVS_MIXIN_MOD_UUID) ||
+ guid_equal(type, &AVS_MIXOUT_MOD_UUID) ||
+ guid_equal(type, &AVS_KPBUFF_MOD_UUID))
+ return avs_modbase_create(adev, mod);
+ if (guid_equal(type, &AVS_COPIER_MOD_UUID))
+ return avs_copier_create(adev, mod);
+ if (guid_equal(type, &AVS_MICSEL_MOD_UUID))
+ return avs_micsel_create(adev, mod);
+ if (guid_equal(type, &AVS_MUX_MOD_UUID))
+ return avs_mux_create(adev, mod);
+ if (guid_equal(type, &AVS_UPDWMIX_MOD_UUID))
+ return avs_updown_mix_create(adev, mod);
+ if (guid_equal(type, &AVS_SRCINTC_MOD_UUID))
+ return avs_src_create(adev, mod);
+ if (guid_equal(type, &AVS_AEC_MOD_UUID))
+ return avs_aec_create(adev, mod);
+ if (guid_equal(type, &AVS_ASRC_MOD_UUID))
+ return avs_asrc_create(adev, mod);
+ if (guid_equal(type, &AVS_INTELWOV_MOD_UUID))
+ return avs_wov_create(adev, mod);
+
+ if (guid_equal(type, &AVS_PROBE_MOD_UUID)) {
+ dev_err(adev->dev, "Probe module can't be instantiated by topology");
+ return -EINVAL;
+ }
+
+ return avs_modext_create(adev, mod);
+}
+
+static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ kfree(mod);
+}
+
+static struct avs_path_module *
+avs_path_module_create(struct avs_dev *adev,
+ struct avs_path_pipeline *owner,
+ struct avs_tplg_module *template)
+{
+ struct avs_path_module *mod;
+ int module_id, ret;
+
+ module_id = avs_get_module_id(adev, &template->cfg_ext->type);
+ if (module_id < 0)
+ return ERR_PTR(module_id);
+
+ mod = kzalloc(sizeof(*mod), GFP_KERNEL);
+ if (!mod)
+ return ERR_PTR(-ENOMEM);
+
+ mod->template = template;
+ mod->module_id = module_id;
+ mod->owner = owner;
+ INIT_LIST_HEAD(&mod->node);
+
+ ret = avs_path_module_type_create(adev, mod);
+ if (ret) {
+ dev_err(adev->dev, "module-type create failed: %d\n", ret);
+ kfree(mod);
+ return ERR_PTR(ret);
+ }
+
+ return mod;
+}
+
+static int avs_path_binding_arm(struct avs_dev *adev, struct avs_path_binding *binding)
+{
+ struct avs_path_module *this_mod, *target_mod;
+ struct avs_path_pipeline *target_ppl;
+ struct avs_path *target_path;
+ struct avs_tplg_binding *t;
+
+ t = binding->template;
+ this_mod = avs_path_find_module(binding->owner,
+ t->mod_id);
+ if (!this_mod) {
+ dev_err(adev->dev, "path mod %d not found\n", t->mod_id);
+ return -EINVAL;
+ }
+
+ /* update with target_tplg_name too */
+ target_path = avs_path_find_path(adev, t->target_tplg_name,
+ t->target_path_tmpl_id);
+ if (!target_path) {
+ dev_err(adev->dev, "target path %s:%d not found\n",
+ t->target_tplg_name, t->target_path_tmpl_id);
+ return -EINVAL;
+ }
+
+ target_ppl = avs_path_find_pipeline(target_path,
+ t->target_ppl_id);
+ if (!target_ppl) {
+ dev_err(adev->dev, "target ppl %d not found\n", t->target_ppl_id);
+ return -EINVAL;
+ }
+
+ target_mod = avs_path_find_module(target_ppl, t->target_mod_id);
+ if (!target_mod) {
+ dev_err(adev->dev, "target mod %d not found\n", t->target_mod_id);
+ return -EINVAL;
+ }
+
+ if (t->is_sink) {
+ binding->sink = this_mod;
+ binding->sink_pin = t->mod_pin;
+ binding->source = target_mod;
+ binding->source_pin = t->target_mod_pin;
+ } else {
+ binding->sink = target_mod;
+ binding->sink_pin = t->target_mod_pin;
+ binding->source = this_mod;
+ binding->source_pin = t->mod_pin;
+ }
+
+ return 0;
+}
+
+static void avs_path_binding_free(struct avs_dev *adev, struct avs_path_binding *binding)
+{
+ kfree(binding);
+}
+
+static struct avs_path_binding *avs_path_binding_create(struct avs_dev *adev,
+ struct avs_path_pipeline *owner,
+ struct avs_tplg_binding *t)
+{
+ struct avs_path_binding *binding;
+
+ binding = kzalloc(sizeof(*binding), GFP_KERNEL);
+ if (!binding)
+ return ERR_PTR(-ENOMEM);
+
+ binding->template = t;
+ binding->owner = owner;
+ INIT_LIST_HEAD(&binding->node);
+
+ return binding;
+}
+
+static int avs_path_pipeline_arm(struct avs_dev *adev,
+ struct avs_path_pipeline *ppl)
+{
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node) {
+ struct avs_path_module *source, *sink;
+ int ret;
+
+ /*
+ * Only one module (so it's implicitly last) or it is the last
+ * one, either way we don't have next module to bind it to.
+ */
+ if (mod == list_last_entry(&ppl->mod_list,
+ struct avs_path_module, node))
+ break;
+
+ /* bind current module to next module on list */
+ source = mod;
+ sink = list_next_entry(mod, node);
+ if (!source || !sink)
+ return -EINVAL;
+
+ ret = avs_ipc_bind(adev, source->module_id, source->instance_id,
+ sink->module_id, sink->instance_id, 0, 0);
+ if (ret)
+ return AVS_IPC_RET(ret);
+ }
+
+ return 0;
+}
+
+static void avs_path_pipeline_free(struct avs_dev *adev,
+ struct avs_path_pipeline *ppl)
+{
+ struct avs_path_binding *binding, *bsave;
+ struct avs_path_module *mod, *save;
+
+ list_for_each_entry_safe(binding, bsave, &ppl->binding_list, node) {
+ list_del(&binding->node);
+ avs_path_binding_free(adev, binding);
+ }
+
+ avs_dsp_delete_pipeline(adev, ppl->instance_id);
+
+ /* Unload resources occupied by owned modules */
+ list_for_each_entry_safe(mod, save, &ppl->mod_list, node) {
+ avs_dsp_delete_module(adev, mod->module_id, mod->instance_id,
+ mod->owner->instance_id,
+ mod->template->core_id);
+ avs_path_module_free(adev, mod);
+ }
+
+ list_del(&ppl->node);
+ kfree(ppl);
+}
+
+static struct avs_path_pipeline *
+avs_path_pipeline_create(struct avs_dev *adev, struct avs_path *owner,
+ struct avs_tplg_pipeline *template)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_tplg_pplcfg *cfg = template->cfg;
+ struct avs_tplg_module *tmod;
+ int ret, i;
+
+ ppl = kzalloc(sizeof(*ppl), GFP_KERNEL);
+ if (!ppl)
+ return ERR_PTR(-ENOMEM);
+
+ ppl->template = template;
+ ppl->owner = owner;
+ INIT_LIST_HEAD(&ppl->binding_list);
+ INIT_LIST_HEAD(&ppl->mod_list);
+ INIT_LIST_HEAD(&ppl->node);
+
+ ret = avs_dsp_create_pipeline(adev, cfg->req_size, cfg->priority,
+ cfg->lp, cfg->attributes,
+ &ppl->instance_id);
+ if (ret) {
+ dev_err(adev->dev, "error creating pipeline %d\n", ret);
+ kfree(ppl);
+ return ERR_PTR(ret);
+ }
+
+ list_for_each_entry(tmod, &template->mod_list, node) {
+ struct avs_path_module *mod;
+
+ mod = avs_path_module_create(adev, ppl, tmod);
+ if (IS_ERR(mod)) {
+ ret = PTR_ERR(mod);
+ dev_err(adev->dev, "error creating module %d\n", ret);
+ goto init_err;
+ }
+
+ list_add_tail(&mod->node, &ppl->mod_list);
+ }
+
+ for (i = 0; i < template->num_bindings; i++) {
+ struct avs_path_binding *binding;
+
+ binding = avs_path_binding_create(adev, ppl, template->bindings[i]);
+ if (IS_ERR(binding)) {
+ ret = PTR_ERR(binding);
+ dev_err(adev->dev, "error creating binding %d\n", ret);
+ goto init_err;
+ }
+
+ list_add_tail(&binding->node, &ppl->binding_list);
+ }
+
+ return ppl;
+
+init_err:
+ avs_path_pipeline_free(adev, ppl);
+ return ERR_PTR(ret);
+}
+
+static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
+ struct avs_tplg_path *template, u32 dma_id)
+{
+ struct avs_tplg_pipeline *tppl;
+
+ path->owner = adev;
+ path->template = template;
+ path->dma_id = dma_id;
+ INIT_LIST_HEAD(&path->ppl_list);
+ INIT_LIST_HEAD(&path->node);
+
+ /* create all the pipelines */
+ list_for_each_entry(tppl, &template->ppl_list, node) {
+ struct avs_path_pipeline *ppl;
+
+ ppl = avs_path_pipeline_create(adev, path, tppl);
+ if (IS_ERR(ppl))
+ return PTR_ERR(ppl);
+
+ list_add_tail(&ppl->node, &path->ppl_list);
+ }
+
+ spin_lock(&adev->path_list_lock);
+ list_add_tail(&path->node, &adev->path_list);
+ spin_unlock(&adev->path_list_lock);
+
+ return 0;
+}
+
+static int avs_path_arm(struct avs_dev *adev, struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_path_binding *binding;
+ int ret;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ /*
+ * Arm all ppl bindings before binding internal modules
+ * as it costs no IPCs which isn't true for the latter.
+ */
+ list_for_each_entry(binding, &ppl->binding_list, node) {
+ ret = avs_path_binding_arm(adev, binding);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = avs_path_pipeline_arm(adev, ppl);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void avs_path_free_unlocked(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl, *save;
+
+ spin_lock(&path->owner->path_list_lock);
+ list_del(&path->node);
+ spin_unlock(&path->owner->path_list_lock);
+
+ list_for_each_entry_safe(ppl, save, &path->ppl_list, node)
+ avs_path_pipeline_free(path->owner, ppl);
+
+ kfree(path);
+}
+
+static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path *template)
+{
+ struct avs_path *path;
+ int ret;
+
+ path = kzalloc(sizeof(*path), GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ ret = avs_path_init(adev, path, template, dma_id);
+ if (ret < 0)
+ goto err;
+
+ ret = avs_path_arm(adev, path);
+ if (ret < 0)
+ goto err;
+
+ path->state = AVS_PPL_STATE_INVALID;
+ return path;
+err:
+ avs_path_free_unlocked(path);
+ return ERR_PTR(ret);
+}
+
+void avs_path_free(struct avs_path *path)
+{
+ struct avs_dev *adev = path->owner;
+
+ mutex_lock(&adev->path_mutex);
+ avs_path_free_unlocked(path);
+ mutex_unlock(&adev->path_mutex);
+}
+
+struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params)
+{
+ struct avs_tplg_path *variant;
+ struct avs_path *path;
+
+ variant = avs_path_find_variant(adev, template, fe_params, be_params);
+ if (!variant) {
+ dev_err(adev->dev, "no matching variant found\n");
+ return ERR_PTR(-ENOENT);
+ }
+
+ /* Serialize path and its components creation. */
+ mutex_lock(&adev->path_mutex);
+ /* Satisfy needs of avs_path_find_tplg(). */
+ mutex_lock(&adev->comp_list_mutex);
+
+ path = avs_path_create_unlocked(adev, dma_id, variant);
+
+ mutex_unlock(&adev->comp_list_mutex);
+ mutex_unlock(&adev->path_mutex);
+
+ return path;
+}
+
+static int avs_path_bind_prepare(struct avs_dev *adev,
+ struct avs_path_binding *binding)
+{
+ const struct avs_audio_format *src_fmt, *sink_fmt;
+ struct avs_tplg_module *tsource = binding->source->template;
+ struct avs_path_module *source = binding->source;
+ int ret;
+
+ /*
+ * only copier modules about to be bound
+ * to output pin other than 0 need preparation
+ */
+ if (!binding->source_pin)
+ return 0;
+ if (!guid_equal(&tsource->cfg_ext->type, &AVS_COPIER_MOD_UUID))
+ return 0;
+
+ src_fmt = tsource->in_fmt;
+ sink_fmt = binding->sink->template->in_fmt;
+
+ ret = avs_ipc_copier_set_sink_format(adev, source->module_id,
+ source->instance_id, binding->source_pin,
+ src_fmt, sink_fmt);
+ if (ret) {
+ dev_err(adev->dev, "config copier failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+
+ return 0;
+}
+
+int avs_path_bind(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_binding *binding;
+
+ list_for_each_entry(binding, &ppl->binding_list, node) {
+ struct avs_path_module *source, *sink;
+
+ source = binding->source;
+ sink = binding->sink;
+
+ ret = avs_path_bind_prepare(adev, binding);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_ipc_bind(adev, source->module_id,
+ source->instance_id, sink->module_id,
+ sink->instance_id, binding->sink_pin,
+ binding->source_pin);
+ if (ret) {
+ dev_err(adev->dev, "bind path failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int avs_path_unbind(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_binding *binding;
+
+ list_for_each_entry(binding, &ppl->binding_list, node) {
+ struct avs_path_module *source, *sink;
+
+ source = binding->source;
+ sink = binding->sink;
+
+ ret = avs_ipc_unbind(adev, source->module_id,
+ source->instance_id, sink->module_id,
+ sink->instance_id, binding->sink_pin,
+ binding->source_pin);
+ if (ret) {
+ dev_err(adev->dev, "unbind path failed: %d\n", ret);
+ return AVS_IPC_RET(ret);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int avs_path_reset(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ if (path->state == AVS_PPL_STATE_RESET)
+ return 0;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+ AVS_PPL_STATE_RESET);
+ if (ret) {
+ dev_err(adev->dev, "reset path failed: %d\n", ret);
+ path->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ path->state = AVS_PPL_STATE_RESET;
+ return 0;
+}
+
+int avs_path_pause(struct avs_path *path)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ if (path->state == AVS_PPL_STATE_PAUSED)
+ return 0;
+
+ list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+ AVS_PPL_STATE_PAUSED);
+ if (ret) {
+ dev_err(adev->dev, "pause path failed: %d\n", ret);
+ path->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ path->state = AVS_PPL_STATE_PAUSED;
+ return 0;
+}
+
+int avs_path_run(struct avs_path *path, int trigger)
+{
+ struct avs_path_pipeline *ppl;
+ struct avs_dev *adev = path->owner;
+ int ret;
+
+ if (path->state == AVS_PPL_STATE_RUNNING && trigger == AVS_TPLG_TRIGGER_AUTO)
+ return 0;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ if (ppl->template->cfg->trigger != trigger)
+ continue;
+
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
+ AVS_PPL_STATE_RUNNING);
+ if (ret) {
+ dev_err(adev->dev, "run path failed: %d\n", ret);
+ path->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ path->state = AVS_PPL_STATE_RUNNING;
+ return 0;
+}
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
new file mode 100644
index 000000000000..197222c5e008
--- /dev/null
+++ b/sound/soc/intel/avs/path.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_PATH_H
+#define __SOUND_SOC_INTEL_AVS_PATH_H
+
+#include <linux/list.h>
+#include "avs.h"
+#include "topology.h"
+
+struct avs_path {
+ u32 dma_id;
+ struct list_head ppl_list;
+ u32 state;
+
+ struct avs_tplg_path *template;
+ struct avs_dev *owner;
+ /* device path management */
+ struct list_head node;
+};
+
+struct avs_path_pipeline {
+ u8 instance_id;
+ struct list_head mod_list;
+ struct list_head binding_list;
+
+ struct avs_tplg_pipeline *template;
+ struct avs_path *owner;
+ /* path pipelines management */
+ struct list_head node;
+};
+
+struct avs_path_module {
+ u16 module_id;
+ u16 instance_id;
+ union avs_gtw_attributes gtw_attrs;
+
+ struct avs_tplg_module *template;
+ struct avs_path_pipeline *owner;
+ /* pipeline modules management */
+ struct list_head node;
+};
+
+struct avs_path_binding {
+ struct avs_path_module *source;
+ u8 source_pin;
+ struct avs_path_module *sink;
+ u8 sink_pin;
+
+ struct avs_tplg_binding *template;
+ struct avs_path_pipeline *owner;
+ /* pipeline bindings management */
+ struct list_head node;
+};
+
+void avs_path_free(struct avs_path *path);
+struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path_template *template,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params);
+int avs_path_bind(struct avs_path *path);
+int avs_path_unbind(struct avs_path *path);
+int avs_path_reset(struct avs_path *path);
+int avs_path_pause(struct avs_path *path);
+int avs_path_run(struct avs_path *path, int trigger);
+
+#endif
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
new file mode 100644
index 000000000000..668f533578a6
--- /dev/null
+++ b/sound/soc/intel/avs/pcm.c
@@ -0,0 +1,1182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+#include "path.h"
+#include "topology.h"
+
+struct avs_dma_data {
+ struct avs_tplg_path_template *template;
+ struct avs_path *path;
+ /*
+ * link stream is stored within substream's runtime
+ * private_data to fulfill the needs of codec BE path
+ *
+ * host stream assigned
+ */
+ struct hdac_ext_stream *host_stream;
+};
+
+static struct avs_tplg_path_template *
+avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
+{
+ struct snd_soc_dapm_widget *dw;
+ struct snd_soc_dapm_path *dp;
+ enum snd_soc_dapm_direction dir;
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ dw = dai->capture_widget;
+ dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN;
+ } else {
+ dw = dai->playback_widget;
+ dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT;
+ }
+
+ dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]);
+ if (!dp)
+ return NULL;
+
+ /* Get the other widget, with actual path template data */
+ dw = (dp->source == dw) ? dp->sink : dp->source;
+
+ return dw->priv;
+}
+
+static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe)
+{
+ struct avs_tplg_path_template *template;
+ struct avs_dma_data *data;
+
+ template = avs_dai_find_path_template(dai, is_fe, substream->stream);
+ if (!template) {
+ dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n",
+ snd_pcm_stream_str(substream), dai->name);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->template = template;
+ snd_soc_dai_set_dma_data(dai, substream, data);
+
+ return 0;
+}
+
+static int avs_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *fe_hw_params,
+ struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+ int dma_id)
+{
+ struct avs_dma_data *data;
+ struct avs_path *path;
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p",
+ __func__, substream, substream->runtime);
+ dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+ params_rate(fe_hw_params), params_channels(fe_hw_params),
+ params_width(fe_hw_params), params_physical_width(fe_hw_params));
+
+ dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p",
+ __func__, substream, substream->runtime);
+ dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+ params_rate(be_hw_params), params_channels(be_hw_params),
+ params_width(be_hw_params), params_physical_width(be_hw_params));
+
+ path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ dev_err(dai->dev, "create path failed: %d\n", ret);
+ return ret;
+ }
+
+ data->path = path;
+ return 0;
+}
+
+static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+ int dma_id)
+{
+ struct snd_pcm_hw_params *fe_hw_params = NULL;
+ struct snd_soc_pcm_runtime *fe, *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = asoc_substream_to_rtd(substream);
+ for_each_dpcm_fe(be, substream->stream, dpcm) {
+ fe = dpcm->fe;
+ fe_hw_params = &fe->dpcm[substream->stream].hw_params;
+ }
+
+ return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id);
+}
+
+static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ ret = avs_path_reset(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "reset path failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause path failed: %d\n", ret);
+ return ret;
+}
+
+static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return avs_dai_startup(substream, dai, false);
+}
+
+static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ kfree(data);
+}
+
+static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ /* Actual port-id comes from topology. */
+ return avs_dai_be_hw_params(substream, hw_params, dai, 0);
+}
+
+static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path) {
+ avs_path_free(data->path);
+ data->path = NULL;
+ }
+
+ return 0;
+}
+
+static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+}
+
+static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ int ret = 0;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret < 0)
+ dev_err(dai->dev, "run BE path failed: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
+ .startup = avs_dai_nonhda_be_startup,
+ .shutdown = avs_dai_nonhda_be_shutdown,
+ .hw_params = avs_dai_nonhda_be_hw_params,
+ .hw_free = avs_dai_nonhda_be_hw_free,
+ .prepare = avs_dai_nonhda_be_prepare,
+ .trigger = avs_dai_nonhda_be_trigger,
+};
+
+static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return avs_dai_startup(substream, dai, false);
+}
+
+static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return avs_dai_nonhda_be_shutdown(substream, dai);
+}
+
+static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *link_stream;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ link_stream = substream->runtime->private_data;
+
+ return avs_dai_be_hw_params(substream, hw_params, dai,
+ hdac_stream(link_stream)->stream_tag - 1);
+}
+
+static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_stream *link_stream;
+ struct hdac_ext_link *link;
+ struct hda_codec *codec;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ link_stream = substream->runtime->private_data;
+ link_stream->link_prepared = false;
+ avs_path_free(data->path);
+ data->path = NULL;
+
+ /* clear link <-> stream mapping */
+ codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+ link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr);
+ if (!link)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag);
+
+ return 0;
+}
+
+static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hdac_ext_stream *link_stream = runtime->private_data;
+ struct hdac_ext_link *link;
+ struct hda_codec *codec;
+ struct hdac_bus *bus;
+ unsigned int format_val;
+ int ret;
+
+ if (link_stream->link_prepared)
+ return 0;
+
+ codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+ bus = &codec->bus->core;
+ format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+ runtime->sample_bits, 0);
+
+ snd_hdac_ext_stream_decouple(bus, link_stream, true);
+ snd_hdac_ext_link_stream_reset(link_stream);
+ snd_hdac_ext_link_stream_setup(link_stream, format_val);
+
+ link = snd_hdac_ext_bus_link_at(bus, codec->core.addr);
+ if (!link)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag);
+
+ ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+ if (ret)
+ return ret;
+
+ link_stream->link_prepared = true;
+ return 0;
+}
+
+static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *link_stream;
+ struct avs_dma_data *data;
+ int ret = 0;
+
+ dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ link_stream = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_link_stream_start(link_stream);
+
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret < 0)
+ dev_err(dai->dev, "run BE path failed: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+ snd_hdac_ext_link_stream_clear(link_stream);
+
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
+ .startup = avs_dai_hda_be_startup,
+ .shutdown = avs_dai_hda_be_shutdown,
+ .hw_params = avs_dai_hda_be_hw_params,
+ .hw_free = avs_dai_hda_be_hw_free,
+ .prepare = avs_dai_hda_be_prepare,
+ .trigger = avs_dai_hda_be_trigger,
+};
+
+static const unsigned int rates[] = {
+ 8000, 11025, 12000, 16000,
+ 22050, 24000, 32000, 44100,
+ 48000, 64000, 88200, 96000,
+ 128000, 176400, 192000,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct avs_dma_data *data;
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ ret = avs_dai_startup(substream, dai, true);
+ if (ret)
+ return ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST);
+ if (!host_stream) {
+ kfree(data);
+ return -EBUSY;
+ }
+
+ data->host_stream = host_stream;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates);
+ snd_pcm_set_sync(substream);
+
+ dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p",
+ __func__, hdac_stream(host_stream)->stream_tag, substream);
+
+ return 0;
+}
+
+static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+ kfree(data);
+}
+
+static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_hw_params *be_hw_params = NULL;
+ struct snd_soc_pcm_runtime *fe, *be;
+ struct snd_soc_dpcm *dpcm;
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ host_stream = data->host_stream;
+
+ hdac_stream(host_stream)->bufsize = 0;
+ hdac_stream(host_stream)->period_bytes = 0;
+ hdac_stream(host_stream)->format_val = 0;
+
+ fe = asoc_substream_to_rtd(substream);
+ for_each_dpcm_be(fe, substream->stream, dpcm) {
+ be = dpcm->be;
+ be_hw_params = &be->dpcm[substream->stream].hw_params;
+ }
+
+ ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai,
+ hdac_stream(host_stream)->stream_tag - 1);
+ if (ret)
+ goto create_err;
+
+ ret = avs_path_bind(data->path);
+ if (ret < 0) {
+ dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret);
+ goto bind_err;
+ }
+
+ return 0;
+
+bind_err:
+ avs_path_free(data->path);
+ data->path = NULL;
+create_err:
+ snd_pcm_lib_free_pages(substream);
+ return ret;
+}
+
+static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ int ret;
+
+ dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p",
+ __func__, substream, substream->runtime);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ host_stream = data->host_stream;
+
+ ret = avs_path_unbind(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret);
+
+ avs_path_free(data->path);
+ data->path = NULL;
+ snd_hdac_stream_cleanup(hdac_stream(host_stream));
+ hdac_stream(host_stream)->prepared = false;
+
+ ret = snd_pcm_lib_free_pages(substream);
+ if (ret < 0)
+ dev_dbg(dai->dev, "Failed to free pages!\n");
+
+ return ret;
+}
+
+static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct avs_dma_data *data;
+ struct avs_dev *adev = to_avs_dev(dai->dev);
+ struct hdac_ext_stream *host_stream;
+ struct hdac_bus *bus;
+ unsigned int format_val;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ host_stream = data->host_stream;
+
+ if (hdac_stream(host_stream)->prepared)
+ return 0;
+
+ bus = hdac_stream(host_stream)->bus;
+ snd_hdac_ext_stream_decouple(bus, data->host_stream, true);
+ snd_hdac_stream_reset(hdac_stream(host_stream));
+
+ format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+ runtime->sample_bits, 0);
+
+ ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_hdac_stream_setup(hdac_stream(host_stream));
+ if (ret < 0)
+ return ret;
+
+ ret = avs_dai_prepare(adev, substream, dai);
+ if (ret)
+ return ret;
+
+ hdac_stream(host_stream)->prepared = true;
+ return 0;
+}
+
+static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ struct hdac_bus *bus;
+ unsigned long flags;
+ int ret = 0;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ host_stream = data->host_stream;
+ bus = hdac_stream(host_stream)->bus;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ snd_hdac_stream_start(hdac_stream(host_stream), true);
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret < 0)
+ dev_err(dai->dev, "run FE path failed: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause FE path failed: %d\n", ret);
+
+ spin_lock_irqsave(&bus->reg_lock, flags);
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset FE path failed: %d\n", ret);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+const struct snd_soc_dai_ops avs_dai_fe_ops = {
+ .startup = avs_dai_fe_startup,
+ .shutdown = avs_dai_fe_shutdown,
+ .hw_params = avs_dai_fe_hw_params,
+ .hw_free = avs_dai_fe_hw_free,
+ .prepare = avs_dai_fe_prepare,
+ .trigger = avs_dai_fe_trigger,
+};
+
+static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct snd_soc_component *component = file->private_data;
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ char buf[64];
+ size_t len;
+
+ len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
+ mach->tplg_filename);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations topology_name_fops = {
+ .open = simple_open,
+ .read = topology_name_read,
+ .llseek = default_llseek,
+};
+
+static int avs_component_load_libraries(struct avs_soc_component *acomp)
+{
+ struct avs_tplg *tplg = acomp->tplg;
+ struct avs_dev *adev = to_avs_dev(acomp->base.dev);
+ int ret;
+
+ if (!tplg->num_libs)
+ return 0;
+
+ /* Parent device may be asleep and library loading involves IPCs. */
+ ret = pm_runtime_resume_and_get(adev->dev);
+ if (ret < 0)
+ return ret;
+
+ avs_hda_clock_gating_enable(adev, false);
+ avs_hda_l1sen_enable(adev, false);
+
+ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+
+ avs_hda_l1sen_enable(adev, true);
+ avs_hda_clock_gating_enable(adev, true);
+
+ if (!ret)
+ ret = avs_module_info_init(adev, false);
+
+ pm_runtime_mark_last_busy(adev->dev);
+ pm_runtime_put_autosuspend(adev->dev);
+
+ return ret;
+}
+
+static int avs_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_soc_component *acomp;
+ struct avs_dev *adev;
+ char *filename;
+ int ret;
+
+ dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
+ mach = dev_get_platdata(card->dev);
+ acomp = to_avs_soc_component(component);
+ adev = to_avs_dev(component->dev);
+
+ acomp->tplg = avs_tplg_new(component);
+ if (!acomp->tplg)
+ return -ENOMEM;
+
+ if (!mach->tplg_filename)
+ goto finalize;
+
+ /* Load specified topology and create debugfs for it. */
+ filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
+ mach->tplg_filename);
+ if (!filename)
+ return -ENOMEM;
+
+ ret = avs_load_topology(component, filename);
+ kfree(filename);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_component_load_libraries(acomp);
+ if (ret < 0) {
+ dev_err(card->dev, "libraries loading failed: %d\n", ret);
+ goto err_load_libs;
+ }
+
+finalize:
+ debugfs_create_file("topology_name", 0444, component->debugfs_root, component,
+ &topology_name_fops);
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_add_tail(&acomp->node, &adev->comp_list);
+ mutex_unlock(&adev->comp_list_mutex);
+
+ return 0;
+
+err_load_libs:
+ avs_remove_topology(component);
+ return ret;
+}
+
+static void avs_component_remove(struct snd_soc_component *component)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(component);
+ struct snd_soc_acpi_mach *mach;
+ struct avs_dev *adev = to_avs_dev(component->dev);
+ int ret;
+
+ mach = dev_get_platdata(component->card->dev);
+
+ mutex_lock(&adev->comp_list_mutex);
+ list_del(&acomp->node);
+ mutex_unlock(&adev->comp_list_mutex);
+
+ if (mach->tplg_filename) {
+ ret = avs_remove_topology(component);
+ if (ret < 0)
+ dev_err(component->dev, "unload topology failed: %d\n", ret);
+ }
+}
+
+static int avs_component_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_pcm_hardware hwparams;
+
+ /* only FE DAI links are handled here */
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ hwparams.info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
+
+ hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE;
+ hwparams.period_bytes_min = 128;
+ hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2;
+ hwparams.periods_min = 2;
+ hwparams.periods_max = AZX_MAX_FRAG;
+ hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE;
+ hwparams.fifo_size = 0;
+
+ return snd_soc_set_runtime_hwparams(substream, &hwparams);
+}
+
+static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream)
+{
+ return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index));
+}
+
+static snd_pcm_uframes_t
+avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct avs_dma_data *data;
+ struct hdac_ext_stream *host_stream;
+ unsigned int pos;
+
+ data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ if (!data->host_stream)
+ return 0;
+
+ host_stream = data->host_stream;
+ pos = avs_hda_stream_dpib_read(host_stream);
+
+ if (pos >= hdac_stream(host_stream)->bufsize)
+ pos = 0;
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int avs_component_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+static int avs_component_construct(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_pcm *pcm = rtd->pcm;
+
+ if (dai->driver->playback.channels_min)
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+ MAX_PREALLOC_SIZE);
+
+ if (dai->driver->capture.channels_min)
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+ MAX_PREALLOC_SIZE);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver avs_component_driver = {
+ .name = "avs-pcm",
+ .probe = avs_component_probe,
+ .remove = avs_component_remove,
+ .open = avs_component_open,
+ .pointer = avs_component_pointer,
+ .mmap = avs_component_mmap,
+ .pcm_construct = avs_component_construct,
+ .module_get_upon_open = 1, /* increment refcount when a pcm is opened */
+ .topology_name_prefix = "intel/avs",
+ .non_legacy_dai_naming = true,
+};
+
+static int avs_soc_component_register(struct device *dev, const char *name,
+ const struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+{
+ struct avs_soc_component *acomp;
+ int ret;
+
+ acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL);
+ if (!acomp)
+ return -ENOMEM;
+
+ ret = snd_soc_component_initialize(&acomp->base, drv, dev);
+ if (ret < 0)
+ return ret;
+
+ /* force name change after ASoC is done with its init */
+ acomp->base.name = name;
+ INIT_LIST_HEAD(&acomp->node);
+
+ return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
+}
+
+static struct snd_soc_dai_driver dmic_cpu_dais[] = {
+{
+ .name = "DMIC Pin",
+ .ops = &avs_dai_nonhda_be_ops,
+ .capture = {
+ .stream_name = "DMIC Rx",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "DMIC WoV Pin",
+ .ops = &avs_dai_nonhda_be_ops,
+ .capture = {
+ .stream_name = "DMIC WoV Rx",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+{
+ return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
+ ARRAY_SIZE(dmic_cpu_dais));
+}
+
+static const struct snd_soc_dai_driver i2s_dai_template = {
+ .ops = &avs_dai_nonhda_be_ops,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms)
+{
+ struct snd_soc_dai_driver *cpus, *dai;
+ size_t ssp_count, cpu_count;
+ int i, j;
+
+ ssp_count = adev->hw_cfg.i2s_caps.ctrl_count;
+ cpu_count = hweight_long(port_mask);
+ if (tdms)
+ for_each_set_bit(i, &port_mask, ssp_count)
+ cpu_count += hweight_long(tdms[i]);
+
+ cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ dai = cpus;
+ for_each_set_bit(i, &port_mask, ssp_count) {
+ memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
+
+ if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+ return -ENOMEM;
+ dai++;
+ }
+
+ if (!tdms)
+ goto plat_register;
+
+ for_each_set_bit(i, &port_mask, ssp_count) {
+ for_each_set_bit(j, &tdms[i], ssp_count) {
+ memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+ dai->name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j);
+ dai->playback.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j);
+ dai->capture.stream_name =
+ devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j);
+
+ if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+ return -ENOMEM;
+ dai++;
+ }
+ }
+
+plat_register:
+ return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+}
+
+/* HD-Audio CPU DAI template */
+static const struct snd_soc_dai_driver hda_cpu_dai = {
+ .ops = &avs_dai_hda_be_ops,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+};
+
+static void avs_component_hda_unregister_dais(struct snd_soc_component *component)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_dai *dai, *save;
+ struct hda_codec *codec;
+ char name[32];
+
+ mach = dev_get_platdata(component->card->dev);
+ codec = mach->pdata;
+ sprintf(name, "%s-cpu", dev_name(&codec->core.dev));
+
+ for_each_component_dais_safe(component, dai, save) {
+ if (!strstr(dai->driver->name, name))
+ continue;
+
+ if (dai->playback_widget)
+ snd_soc_dapm_free_widget(dai->playback_widget);
+ if (dai->capture_widget)
+ snd_soc_dapm_free_widget(dai->capture_widget);
+ snd_soc_unregister_dai(dai);
+ }
+}
+
+static int avs_component_hda_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_dai_driver *dais;
+ struct snd_soc_acpi_mach *mach;
+ struct hda_codec *codec;
+ struct hda_pcm *pcm;
+ const char *cname;
+ int pcm_count = 0, ret, i;
+
+ mach = dev_get_platdata(component->card->dev);
+ if (!mach)
+ return -EINVAL;
+
+ codec = mach->pdata;
+ if (list_empty(&codec->pcm_list_head))
+ return -EINVAL;
+ list_for_each_entry(pcm, &codec->pcm_list_head, list)
+ pcm_count++;
+
+ dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais),
+ GFP_KERNEL);
+ if (!dais)
+ return -ENOMEM;
+
+ cname = dev_name(&codec->core.dev);
+ dapm = snd_soc_component_get_dapm(component);
+ pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+ for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+ struct snd_soc_dai *dai;
+
+ memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais));
+ dais[i].id = i;
+ dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL,
+ "%s-cpu%d", cname, i);
+ if (!dais[i].name) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (pcm->stream[0].substreams) {
+ dais[i].playback.stream_name =
+ devm_kasprintf(component->dev, GFP_KERNEL,
+ "%s-cpu%d Tx", cname, i);
+ if (!dais[i].playback.stream_name) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ }
+
+ if (pcm->stream[1].substreams) {
+ dais[i].capture.stream_name =
+ devm_kasprintf(component->dev, GFP_KERNEL,
+ "%s-cpu%d Rx", cname, i);
+ if (!dais[i].capture.stream_name) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ }
+
+ dai = snd_soc_register_dai(component, &dais[i], false);
+ if (!dai) {
+ dev_err(component->dev, "register dai for %s failed\n",
+ pcm->name);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+ if (ret < 0) {
+ dev_err(component->dev, "create widgets failed: %d\n",
+ ret);
+ goto exit;
+ }
+ }
+
+ ret = avs_component_probe(component);
+exit:
+ if (ret)
+ avs_component_hda_unregister_dais(component);
+
+ return ret;
+}
+
+static void avs_component_hda_remove(struct snd_soc_component *component)
+{
+ avs_component_hda_unregister_dais(component);
+ avs_component_remove(component);
+}
+
+static int avs_component_hda_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_stream *link_stream;
+ struct hda_codec *codec;
+
+ /* only BE DAI links are handled here */
+ if (!rtd->dai_link->no_pcm)
+ return avs_component_open(component, substream);
+
+ codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+ link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
+ HDAC_EXT_STREAM_TYPE_LINK);
+ if (!link_stream)
+ return -EBUSY;
+
+ substream->runtime->private_data = link_stream;
+ return 0;
+}
+
+static int avs_component_hda_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_stream *link_stream;
+
+ /* only BE DAI links are handled here */
+ if (!rtd->dai_link->no_pcm)
+ return 0;
+
+ link_stream = substream->runtime->private_data;
+ snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ substream->runtime->private_data = NULL;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver avs_hda_component_driver = {
+ .name = "avs-hda-pcm",
+ .probe = avs_component_hda_probe,
+ .remove = avs_component_hda_remove,
+ .open = avs_component_hda_open,
+ .close = avs_component_hda_close,
+ .pointer = avs_component_pointer,
+ .mmap = avs_component_mmap,
+ .pcm_construct = avs_component_construct,
+ /*
+ * hda platform component's probe() is dependent on
+ * codec->pcm_list_head, it needs to be initialized after codec
+ * component. remove_order is here for completeness sake
+ */
+ .probe_order = SND_SOC_COMP_ORDER_LATE,
+ .remove_order = SND_SOC_COMP_ORDER_EARLY,
+ .module_get_upon_open = 1,
+ .topology_name_prefix = "intel/avs",
+ .non_legacy_dai_naming = true,
+};
+
+int avs_hda_platform_register(struct avs_dev *adev, const char *name)
+{
+ return avs_soc_component_register(adev->dev, name,
+ &avs_hda_component_driver, NULL, 0);
+}
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index 3fd02389ed2b..95be86148cf3 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -14,6 +14,7 @@
#define AZX_PGCTL_LSRMD_MASK BIT(4)
#define AZX_CGCTL_MISCBDCGE_MASK BIT(6)
#define AZX_VS_EM2_L1SEN BIT(13)
+#define AZX_VS_EM2_DUM BIT(23)
/* Intel HD Audio General DSP Registers */
#define AVS_ADSP_GEN_BASE 0x0
@@ -47,6 +48,12 @@
#define SKL_ADSP_HIPCIE_DONE BIT(30)
#define SKL_ADSP_HIPCT_BUSY BIT(31)
+/* Intel HD Audio SRAM windows base addresses */
+#define SKL_ADSP_SRAM_BASE_OFFSET 0x8000
+#define SKL_ADSP_SRAM_WINDOW_SIZE 0x2000
+#define APL_ADSP_SRAM_BASE_OFFSET 0x80000
+#define APL_ADSP_SRAM_WINDOW_SIZE 0x20000
+
/* Constants used when accessing SRAM, space shared with firmware */
#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram_base_offset)
#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0)
@@ -58,6 +65,7 @@
#define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW
/* HOST -> DSP communication window */
#define AVS_DOWNLINK_WINDOW 1
+#define AVS_DEBUG_WINDOW 2
/* registry I/O helpers */
#define avs_sram_offset(adev, window_idx) \
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
new file mode 100644
index 000000000000..bda5ec7510fe
--- /dev/null
+++ b/sound/soc/intel/avs/skl.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "messages.h"
+
+static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct skl_log_state_info *info;
+ u32 size, num_cores = adev->hw_cfg.dsp_cores;
+ int ret, i;
+
+ if (fls_long(resource_mask) > num_cores)
+ return -EINVAL;
+ size = struct_size(info, logs_core, num_cores);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->core_mask = resource_mask;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) {
+ info->logs_core[i].enable = enable;
+ info->logs_core[i].min_priority = *priorities++;
+ }
+ else
+ for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0))
+ info->logs_core[i].enable = enable;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
+int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+ return core * avs_log_buffer_size(adev);
+}
+
+/* fw DbgLogWp registers */
+#define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core)
+
+static int
+skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ unsigned long flags;
+ void __iomem *buf;
+ u16 size, write, offset;
+
+ spin_lock_irqsave(&adev->dbg.trace_lock, flags);
+ if (!kfifo_initialized(&adev->dbg.trace_fifo)) {
+ spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+ return 0;
+ }
+
+ size = avs_log_buffer_size(adev) / 2;
+ write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
+ /* determine buffer half */
+ offset = (write < size) ? size : 0;
+
+ /* Address is guaranteed to exist in SRAM2. */
+ buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
+ __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock);
+ wake_up(&adev->dbg.trace_waitq);
+ spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+
+ return 0;
+}
+
+static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ u8 *dump;
+
+ dump = vzalloc(AVS_FW_REGS_SIZE);
+ if (!dump)
+ return -ENOMEM;
+
+ memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+ dev_coredumpv(adev->dev, dump, AVS_FW_REGS_SIZE, GFP_KERNEL);
+
+ return 0;
+}
+
+static bool
+skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+ /* unsupported on cAVS 1.5 hw */
+ return false;
+}
+
+static int skl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ /* unsupported on cAVS 1.5 hw */
+ return 0;
+}
+
+const struct avs_dsp_ops skl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .irq_handler = avs_dsp_irq_handler,
+ .irq_thread = avs_dsp_irq_thread,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_cldma_load_basefw,
+ .load_lib = avs_cldma_load_library,
+ .transfer_mods = avs_cldma_transfer_modules,
+ .enable_logs = skl_enable_logs,
+ .log_buffer_offset = skl_log_buffer_offset,
+ .log_buffer_status = skl_log_buffer_status,
+ .coredump = skl_coredump,
+ .d0ix_toggle = skl_d0ix_toggle,
+ .set_d0ix = skl_set_d0ix,
+};
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
new file mode 100644
index 000000000000..0d11cc8aab0b
--- /dev/null
+++ b/sound/soc/intel/avs/topology.c
@@ -0,0 +1,1598 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/uuid.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-topology.h>
+#include <uapi/sound/intel/avs/tokens.h>
+#include "avs.h"
+#include "topology.h"
+
+/* Get pointer to vendor array at the specified offset. */
+#define avs_tplg_vendor_array_at(array, offset) \
+ ((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset))
+
+/* Get pointer to vendor array that is next in line. */
+#define avs_tplg_vendor_array_next(array) \
+ (avs_tplg_vendor_array_at(array, le32_to_cpu((array)->size)))
+
+/*
+ * Scan provided block of tuples for the specified token. If found,
+ * @offset is updated with position at which first matching token is
+ * located.
+ *
+ * Returns 0 on success, -ENOENT if not found and error code otherwise.
+ */
+static int
+avs_tplg_vendor_array_lookup(struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 token, u32 *offset)
+{
+ u32 pos = 0;
+
+ while (block_size > 0) {
+ struct snd_soc_tplg_vendor_value_elem *tuple;
+ u32 tuples_size = le32_to_cpu(tuples->size);
+
+ if (tuples_size > block_size)
+ return -EINVAL;
+
+ tuple = tuples->value;
+ if (le32_to_cpu(tuple->token) == token) {
+ *offset = pos;
+ return 0;
+ }
+
+ block_size -= tuples_size;
+ pos += tuples_size;
+ tuples = avs_tplg_vendor_array_next(tuples);
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * See avs_tplg_vendor_array_lookup() for description.
+ *
+ * Behaves exactly like avs_tplg_vendor_lookup() but starts from the
+ * next vendor array in line. Useful when searching for the finish line
+ * of an arbitrary entry in a list of entries where each is composed of
+ * several vendor tuples and a specific token marks the beginning of
+ * a new entry block.
+ */
+static int
+avs_tplg_vendor_array_lookup_next(struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 token, u32 *offset)
+{
+ u32 tuples_size = le32_to_cpu(tuples->size);
+ int ret;
+
+ if (tuples_size > block_size)
+ return -EINVAL;
+
+ tuples = avs_tplg_vendor_array_next(tuples);
+ block_size -= tuples_size;
+
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size, token, offset);
+ if (!ret)
+ *offset += tuples_size;
+ return ret;
+}
+
+/*
+ * Scan provided block of tuples for the specified token which marks
+ * the border of an entry block. Behavior is similar to
+ * avs_tplg_vendor_array_lookup() except 0 is also returned if no
+ * matching token has been found. In such case, returned @size is
+ * assigned to @block_size as the entire block belongs to the current
+ * entry.
+ *
+ * Returns 0 on success, error code otherwise.
+ */
+static int
+avs_tplg_vendor_entry_size(struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size, u32 entry_id_token, u32 *size)
+{
+ int ret;
+
+ ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, entry_id_token, size);
+ if (ret == -ENOENT) {
+ *size = block_size;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Vendor tuple parsing descriptor.
+ *
+ * @token: vendor specific token that identifies tuple
+ * @type: tuple type, one of SND_SOC_TPLG_TUPLE_TYPE_XXX
+ * @offset: offset of a struct's field to initialize
+ * @parse: parsing function, extracts and assigns value to object's field
+ */
+struct avs_tplg_token_parser {
+ enum avs_tplg_token token;
+ u32 type;
+ u32 offset;
+ int (*parse)(struct snd_soc_component *comp, void *elem, void *object, u32 offset);
+};
+
+static int
+avs_parse_uuid_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ guid_t *val = (guid_t *)((u8 *)object + offset);
+
+ guid_copy((guid_t *)val, (const guid_t *)&tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ bool *val = (bool *)((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ u8 *val = ((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_short_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ u16 *val = (u16 *)((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_word_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+ u32 *val = (u32 *)((u8 *)object + offset);
+
+ *val = le32_to_cpu(tuple->value);
+
+ return 0;
+}
+
+static int
+avs_parse_string_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_string_elem *tuple = elem;
+ char *val = (char *)((u8 *)object + offset);
+
+ snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", tuple->string);
+
+ return 0;
+}
+
+static int avs_parse_uuid_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, int count,
+ struct snd_soc_tplg_vendor_array *tuples)
+{
+ struct snd_soc_tplg_vendor_uuid_elem *tuple;
+ int ret, i, j;
+
+ /* Parse element by element. */
+ for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+ tuple = &tuples->uuid[i];
+
+ for (j = 0; j < count; j++) {
+ /* Ignore non-UUID tokens. */
+ if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID ||
+ parsers[j].token != le32_to_cpu(tuple->token))
+ continue;
+
+ ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_parse_string_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, int count,
+ struct snd_soc_tplg_vendor_array *tuples)
+{
+ struct snd_soc_tplg_vendor_string_elem *tuple;
+ int ret, i, j;
+
+ /* Parse element by element. */
+ for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+ tuple = &tuples->string[i];
+
+ for (j = 0; j < count; j++) {
+ /* Ignore non-string tokens. */
+ if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING ||
+ parsers[j].token != le32_to_cpu(tuple->token))
+ continue;
+
+ ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_parse_word_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, int count,
+ struct snd_soc_tplg_vendor_array *tuples)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple;
+ int ret, i, j;
+
+ /* Parse element by element. */
+ for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
+ tuple = &tuples->value[i];
+
+ for (j = 0; j < count; j++) {
+ /* Ignore non-integer tokens. */
+ if (!(parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
+ parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
+ parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE ||
+ parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL))
+ continue;
+
+ if (parsers[j].token != le32_to_cpu(tuple->token))
+ continue;
+
+ ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int avs_parse_tokens(struct snd_soc_component *comp, void *object,
+ const struct avs_tplg_token_parser *parsers, size_t count,
+ struct snd_soc_tplg_vendor_array *tuples, int priv_size)
+{
+ int array_size, ret;
+
+ while (priv_size > 0) {
+ array_size = le32_to_cpu(tuples->size);
+
+ if (array_size <= 0) {
+ dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
+ return -EINVAL;
+ }
+
+ /* Make sure there is enough data before parsing. */
+ priv_size -= array_size;
+ if (priv_size < 0) {
+ dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
+ return -EINVAL;
+ }
+
+ switch (le32_to_cpu(tuples->type)) {
+ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+ ret = avs_parse_uuid_tokens(comp, object, parsers, count, tuples);
+ break;
+ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+ ret = avs_parse_string_tokens(comp, object, parsers, count, tuples);
+ break;
+ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
+ case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
+ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
+ ret = avs_parse_word_tokens(comp, object, parsers, count, tuples);
+ break;
+ default:
+ dev_err(comp->dev, "unknown token type %d\n", tuples->type);
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(comp->dev, "parsing %zu tokens of %d type failed: %d\n",
+ count, tuples->type, ret);
+ return ret;
+ }
+
+ tuples = avs_tplg_vendor_array_next(tuples);
+ }
+
+ return 0;
+}
+
+#define AVS_DEFINE_PTR_PARSER(name, type, member) \
+static int \
+avs_parse_##name##_ptr(struct snd_soc_component *comp, void *elem, void *object, u32 offset) \
+{ \
+ struct snd_soc_tplg_vendor_value_elem *tuple = elem; \
+ struct avs_soc_component *acomp = to_avs_soc_component(comp); \
+ type **val = (type **)(object + offset); \
+ u32 idx; \
+ \
+ idx = le32_to_cpu(tuple->value); \
+ if (idx >= acomp->tplg->num_##member) \
+ return -EINVAL; \
+ \
+ *val = &acomp->tplg->member[idx]; \
+ \
+ return 0; \
+}
+
+AVS_DEFINE_PTR_PARSER(audio_format, struct avs_audio_format, fmts);
+AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base);
+AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext);
+AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs);
+AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings);
+
+static int
+parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_value_elem *velem = elem;
+ struct avs_audio_format *audio_format = object;
+
+ switch (offset) {
+ case AVS_TKN_AFMT_NUM_CHANNELS_U32:
+ audio_format->num_channels = le32_to_cpu(velem->value);
+ break;
+ case AVS_TKN_AFMT_VALID_BIT_DEPTH_U32:
+ audio_format->valid_bit_depth = le32_to_cpu(velem->value);
+ break;
+ case AVS_TKN_AFMT_SAMPLE_TYPE_U32:
+ audio_format->sample_type = le32_to_cpu(velem->value);
+ break;
+ }
+
+ return 0;
+}
+
+static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem,
+ void *object, u32 offset)
+{
+ struct snd_soc_tplg_vendor_string_elem *tuple = elem;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
+ char *val = (char *)((u8 *)object + offset);
+
+ /*
+ * Dynamic naming - string formats, e.g.: ssp%d - supported only for
+ * topologies describing single device e.g.: an I2S codec on SSP0.
+ */
+ if (hweight_long(mach->mach_params.i2s_link_mask) != 1)
+ return avs_parse_string_token(comp, elem, object, offset);
+
+ snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string,
+ __ffs(mach->mach_params.i2s_link_mask));
+
+ return 0;
+}
+
+static int
+parse_dictionary_header(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ void **dict, u32 *num_entries, size_t entry_size,
+ u32 num_entries_token)
+{
+ struct snd_soc_tplg_vendor_value_elem *tuple;
+
+ /* Dictionary header consists of single tuple - entry count. */
+ tuple = tuples->value;
+ if (le32_to_cpu(tuple->token) != num_entries_token) {
+ dev_err(comp->dev, "invalid dictionary header, expected: %d\n",
+ num_entries_token);
+ return -EINVAL;
+ }
+
+ *num_entries = le32_to_cpu(tuple->value);
+ *dict = devm_kcalloc(comp->card->dev, *num_entries, entry_size, GFP_KERNEL);
+ if (!*dict)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+parse_dictionary_entries(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ void *dict, u32 num_entries, size_t entry_size,
+ u32 entry_id_token,
+ const struct avs_tplg_token_parser *parsers, size_t num_parsers)
+{
+ void *pos = dict;
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ u32 esize;
+ int ret;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ entry_id_token, &esize);
+ if (ret)
+ return ret;
+
+ ret = avs_parse_tokens(comp, pos, parsers, num_parsers, tuples, esize);
+ if (ret < 0) {
+ dev_err(comp->dev, "parse entry: %d of type: %d failed: %d\n",
+ i, entry_id_token, ret);
+ return ret;
+ }
+
+ pos += entry_size;
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static int parse_dictionary(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ void **dict, u32 *num_entries, size_t entry_size,
+ u32 num_entries_token, u32 entry_id_token,
+ const struct avs_tplg_token_parser *parsers, size_t num_parsers)
+{
+ int ret;
+
+ ret = parse_dictionary_header(comp, tuples, dict, num_entries,
+ entry_size, num_entries_token);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ return parse_dictionary_entries(comp, tuples, block_size, *dict,
+ *num_entries, entry_size,
+ entry_id_token, parsers, num_parsers);
+}
+
+static const struct avs_tplg_token_parser library_parsers[] = {
+ {
+ .token = AVS_TKN_LIBRARY_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_library, name),
+ .parse = avs_parse_string_token,
+ },
+};
+
+static int avs_tplg_parse_libraries(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->libs,
+ &tplg->num_libs, sizeof(*tplg->libs),
+ AVS_TKN_MANIFEST_NUM_LIBRARIES_U32,
+ AVS_TKN_LIBRARY_ID_U32,
+ library_parsers, ARRAY_SIZE(library_parsers));
+}
+
+static const struct avs_tplg_token_parser audio_format_parsers[] = {
+ {
+ .token = AVS_TKN_AFMT_SAMPLE_RATE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, sampling_freq),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_BIT_DEPTH_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, bit_depth),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_CHANNEL_MAP_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, channel_map),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_CHANNEL_CFG_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, channel_config),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_INTERLEAVING_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_audio_format, interleaving),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_AFMT_NUM_CHANNELS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = AVS_TKN_AFMT_NUM_CHANNELS_U32,
+ .parse = parse_audio_format_bitfield,
+ },
+ {
+ .token = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32,
+ .parse = parse_audio_format_bitfield,
+ },
+ {
+ .token = AVS_TKN_AFMT_SAMPLE_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = AVS_TKN_AFMT_SAMPLE_TYPE_U32,
+ .parse = parse_audio_format_bitfield,
+ },
+};
+
+static int avs_tplg_parse_audio_formats(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->fmts,
+ &tplg->num_fmts, sizeof(*tplg->fmts),
+ AVS_TKN_MANIFEST_NUM_AFMTS_U32,
+ AVS_TKN_AFMT_ID_U32,
+ audio_format_parsers, ARRAY_SIZE(audio_format_parsers));
+}
+
+static const struct avs_tplg_token_parser modcfg_base_parsers[] = {
+ {
+ .token = AVS_TKN_MODCFG_BASE_CPC_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, cpc),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_BASE_IBS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, ibs),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_BASE_OBS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, obs),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_BASE_PAGES_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_base, is_pages),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_tplg_parse_modcfgs_base(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->modcfgs_base,
+ &tplg->num_modcfgs_base, sizeof(*tplg->modcfgs_base),
+ AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32,
+ AVS_TKN_MODCFG_BASE_ID_U32,
+ modcfg_base_parsers, ARRAY_SIZE(modcfg_base_parsers));
+}
+
+static const struct avs_tplg_token_parser modcfg_ext_parsers[] = {
+ {
+ .token = AVS_TKN_MODCFG_EXT_TYPE_UUID,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_UUID,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, type),
+ .parse = avs_parse_uuid_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.feature_mask),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_VINDEX_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.vindex),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_DMA_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_buffer_size),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, copier.blob_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, micsel.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, wov.cpc_lp_mode),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_SRC_OUT_FREQ_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, src.out_freq),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, mux.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, mux.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, aec.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, aec.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, aec.cpc_lp_mode),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.out_freq),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_ASRC_MODE_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.mode),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.disable_jitter_buffer),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.out_channel_config),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients_select),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[0]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[1]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[2]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[3]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[4]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[5]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[6]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[7]),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.channel_map),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_input_pins),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins),
+ .parse = avs_parse_short_token,
+ },
+};
+
+static const struct avs_tplg_token_parser pin_format_parsers[] = {
+ {
+ .token = AVS_TKN_PIN_FMT_INDEX_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pin_format, pin_index),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PIN_FMT_IOBS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pin_format, iobs),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PIN_FMT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pin_format, fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+};
+
+static int avs_tplg_parse_modcfg_ext(struct snd_soc_component *comp,
+ struct avs_tplg_modcfg_ext *cfg,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ u32 esize;
+ int ret;
+
+ /* See where pin block starts. */
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_PIN_FMT_INDEX_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = avs_parse_tokens(comp, cfg, modcfg_ext_parsers,
+ ARRAY_SIZE(modcfg_ext_parsers), tuples, esize);
+ if (ret)
+ return ret;
+
+ block_size -= esize;
+ /* Parse trailing in/out pin formats if any. */
+ if (block_size) {
+ struct avs_tplg_pin_format *pins;
+ u32 num_pins;
+
+ num_pins = cfg->generic.num_input_pins + cfg->generic.num_output_pins;
+ if (!num_pins)
+ return -EINVAL;
+
+ pins = devm_kcalloc(comp->card->dev, num_pins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ ret = parse_dictionary_entries(comp, tuples, block_size,
+ pins, num_pins, sizeof(*pins),
+ AVS_TKN_PIN_FMT_INDEX_U32,
+ pin_format_parsers,
+ ARRAY_SIZE(pin_format_parsers));
+ if (ret)
+ return ret;
+ cfg->generic.pin_fmts = pins;
+ }
+
+ return 0;
+}
+
+static int avs_tplg_parse_modcfgs_ext(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->modcfgs_ext,
+ &tplg->num_modcfgs_ext,
+ sizeof(*tplg->modcfgs_ext),
+ AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_modcfgs_ext; i++) {
+ struct avs_tplg_modcfg_ext *cfg = &tplg->modcfgs_ext[i];
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_MODCFG_EXT_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = avs_tplg_parse_modcfg_ext(comp, cfg, tuples, esize);
+ if (ret)
+ return ret;
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser pplcfg_parsers[] = {
+ {
+ .token = AVS_TKN_PPLCFG_REQ_SIZE_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_pplcfg, req_size),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_PRIORITY_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_pplcfg, priority),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_LOW_POWER_BOOL,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL,
+ .offset = offsetof(struct avs_tplg_pplcfg, lp),
+ .parse = avs_parse_bool_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_ATTRIBUTES_U16,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT,
+ .offset = offsetof(struct avs_tplg_pplcfg, attributes),
+ .parse = avs_parse_short_token,
+ },
+ {
+ .token = AVS_TKN_PPLCFG_TRIGGER_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pplcfg, trigger),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_tplg_parse_pplcfgs(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->pplcfgs,
+ &tplg->num_pplcfgs, sizeof(*tplg->pplcfgs),
+ AVS_TKN_MANIFEST_NUM_PPLCFGS_U32,
+ AVS_TKN_PPLCFG_ID_U32,
+ pplcfg_parsers, ARRAY_SIZE(pplcfg_parsers));
+}
+
+static const struct avs_tplg_token_parser binding_parsers[] = {
+ {
+ .token = AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_binding, target_tplg_name),
+ .parse = parse_link_formatted_string,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, target_path_tmpl_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_PPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, target_ppl_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_MOD_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, target_mod_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_TARGET_MOD_PIN_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_binding, target_mod_pin),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_MOD_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_binding, mod_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_MOD_PIN_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_binding, mod_pin),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_BINDING_IS_SINK_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_binding, is_sink),
+ .parse = avs_parse_byte_token,
+ },
+};
+
+static int avs_tplg_parse_bindings(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+
+ return parse_dictionary(comp, tuples, block_size, (void **)&tplg->bindings,
+ &tplg->num_bindings, sizeof(*tplg->bindings),
+ AVS_TKN_MANIFEST_NUM_BINDINGS_U32,
+ AVS_TKN_BINDING_ID_U32,
+ binding_parsers, ARRAY_SIZE(binding_parsers));
+}
+
+static const struct avs_tplg_token_parser module_parsers[] = {
+ {
+ .token = AVS_TKN_MOD_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MOD_MODCFG_BASE_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, cfg_base),
+ .parse = avs_parse_modcfg_base_ptr,
+ },
+ {
+ .token = AVS_TKN_MOD_IN_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, in_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MOD_CORE_ID_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_module, core_id),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_PROC_DOMAIN_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_module, domain),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MOD_MODCFG_EXT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, cfg_ext),
+ .parse = avs_parse_modcfg_ext_ptr,
+ },
+};
+
+static struct avs_tplg_module *
+avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_tplg_module *module;
+ int ret;
+
+ module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return ERR_PTR(-ENOMEM);
+
+ ret = avs_parse_tokens(comp, module, module_parsers,
+ ARRAY_SIZE(module_parsers), tuples, block_size);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ module->owner = owner;
+ INIT_LIST_HEAD(&module->node);
+
+ return module;
+}
+
+static const struct avs_tplg_token_parser pipeline_parsers[] = {
+ {
+ .token = AVS_TKN_PPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pipeline, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PPL_PPLCFG_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pipeline, cfg),
+ .parse = avs_parse_pplcfg_ptr,
+ },
+ {
+ .token = AVS_TKN_PPL_NUM_BINDING_IDS_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_pipeline, num_bindings),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static const struct avs_tplg_token_parser bindings_parsers[] = {
+ {
+ .token = AVS_TKN_PPL_BINDING_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = 0, /* to treat pipeline->bindings as dictionary */
+ .parse = avs_parse_binding_ptr,
+ },
+};
+
+static struct avs_tplg_pipeline *
+avs_tplg_pipeline_create(struct snd_soc_component *comp, struct avs_tplg_path *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_tplg_pipeline *pipeline;
+ u32 modblk_size, offset;
+ int ret;
+
+ pipeline = devm_kzalloc(comp->card->dev, sizeof(*pipeline), GFP_KERNEL);
+ if (!pipeline)
+ return ERR_PTR(-ENOMEM);
+
+ pipeline->owner = owner;
+ INIT_LIST_HEAD(&pipeline->mod_list);
+
+ /* Pipeline header MUST be followed by at least one module. */
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+ AVS_TKN_MOD_ID_U32, &offset);
+ if (!ret && !offset)
+ ret = -EINVAL;
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Process header which precedes module sections. */
+ ret = avs_parse_tokens(comp, pipeline, pipeline_parsers,
+ ARRAY_SIZE(pipeline_parsers), tuples, offset);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ block_size -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* Optionally, binding sections follow module ones. */
+ ret = avs_tplg_vendor_array_lookup_next(tuples, block_size,
+ AVS_TKN_PPL_BINDING_ID_U32, &offset);
+ if (ret) {
+ if (ret != -ENOENT)
+ return ERR_PTR(ret);
+
+ /* Does header information match actual block layout? */
+ if (pipeline->num_bindings)
+ return ERR_PTR(-EINVAL);
+
+ modblk_size = block_size;
+ } else {
+ pipeline->bindings = devm_kcalloc(comp->card->dev, pipeline->num_bindings,
+ sizeof(*pipeline->bindings), GFP_KERNEL);
+ if (!pipeline->bindings)
+ return ERR_PTR(-ENOMEM);
+
+ modblk_size = offset;
+ }
+
+ block_size -= modblk_size;
+ do {
+ struct avs_tplg_module *module;
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, modblk_size,
+ AVS_TKN_MOD_ID_U32, &esize);
+ if (ret)
+ return ERR_PTR(ret);
+
+ module = avs_tplg_module_create(comp, pipeline, tuples, esize);
+ if (IS_ERR(module)) {
+ dev_err(comp->dev, "parse module failed: %ld\n",
+ PTR_ERR(module));
+ return ERR_CAST(module);
+ }
+
+ list_add_tail(&module->node, &pipeline->mod_list);
+ modblk_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ } while (modblk_size > 0);
+
+ /* What's left is optional range of bindings. */
+ ret = parse_dictionary_entries(comp, tuples, block_size, pipeline->bindings,
+ pipeline->num_bindings, sizeof(*pipeline->bindings),
+ AVS_TKN_PPL_BINDING_ID_U32,
+ bindings_parsers, ARRAY_SIZE(bindings_parsers));
+ if (ret)
+ return ERR_PTR(ret);
+
+ return pipeline;
+}
+
+static const struct avs_tplg_token_parser path_parsers[] = {
+ {
+ .token = AVS_TKN_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_PATH_FE_FMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, fe_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_PATH_BE_FMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, be_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+};
+
+static struct avs_tplg_path *
+avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ const struct avs_tplg_token_parser *parsers, u32 num_parsers)
+{
+ struct avs_tplg_pipeline *pipeline;
+ struct avs_tplg_path *path;
+ u32 offset;
+ int ret;
+
+ path = devm_kzalloc(comp->card->dev, sizeof(*path), GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ path->owner = owner;
+ INIT_LIST_HEAD(&path->ppl_list);
+ INIT_LIST_HEAD(&path->node);
+
+ /* Path header MAY be followed by one or more pipelines. */
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+ AVS_TKN_PPL_ID_U32, &offset);
+ if (ret == -ENOENT)
+ offset = block_size;
+ else if (ret)
+ return ERR_PTR(ret);
+ else if (!offset)
+ return ERR_PTR(-EINVAL);
+
+ /* Process header which precedes pipeline sections. */
+ ret = avs_parse_tokens(comp, path, parsers, num_parsers, tuples, offset);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ block_size -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+ while (block_size > 0) {
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_PPL_ID_U32, &esize);
+ if (ret)
+ return ERR_PTR(ret);
+
+ pipeline = avs_tplg_pipeline_create(comp, path, tuples, esize);
+ if (IS_ERR(pipeline)) {
+ dev_err(comp->dev, "parse pipeline failed: %ld\n",
+ PTR_ERR(pipeline));
+ return ERR_CAST(pipeline);
+ }
+
+ list_add_tail(&pipeline->node, &path->ppl_list);
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return path;
+}
+
+static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
+ {
+ .token = AVS_TKN_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, id),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int parse_path_template(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
+ struct avs_tplg_path_template *template,
+ const struct avs_tplg_token_parser *tmpl_tokens, u32 num_tmpl_tokens,
+ const struct avs_tplg_token_parser *path_tokens, u32 num_path_tokens)
+{
+ struct avs_tplg_path *path;
+ u32 offset;
+ int ret;
+
+ /* Path template header MUST be followed by at least one path variant. */
+ ret = avs_tplg_vendor_array_lookup(tuples, block_size,
+ AVS_TKN_PATH_ID_U32, &offset);
+ if (ret)
+ return ret;
+
+ /* Process header which precedes path variants sections. */
+ ret = avs_parse_tokens(comp, template, tmpl_tokens, num_tmpl_tokens, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ block_size -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+ do {
+ u32 esize;
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_PATH_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ path = avs_tplg_path_create(comp, template, tuples, esize, path_tokens,
+ num_path_tokens);
+ if (IS_ERR(path)) {
+ dev_err(comp->dev, "parse path failed: %ld\n", PTR_ERR(path));
+ return PTR_ERR(path);
+ }
+
+ list_add_tail(&path->node, &template->path_list);
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ } while (block_size > 0);
+
+ return 0;
+}
+
+static struct avs_tplg_path_template *
+avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *owner,
+ struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
+{
+ struct avs_tplg_path_template *template;
+ int ret;
+
+ template = devm_kzalloc(comp->card->dev, sizeof(*template), GFP_KERNEL);
+ if (!template)
+ return ERR_PTR(-ENOMEM);
+
+ template->owner = owner; /* Used to access component tplg is assigned to. */
+ INIT_LIST_HEAD(&template->path_list);
+ INIT_LIST_HEAD(&template->node);
+
+ ret = parse_path_template(comp, tuples, block_size, template, path_tmpl_parsers,
+ ARRAY_SIZE(path_tmpl_parsers), path_parsers,
+ ARRAY_SIZE(path_parsers));
+ if (ret)
+ return ERR_PTR(ret);
+
+ return template;
+}
+
+static int avs_route_load(struct snd_soc_component *comp, int index,
+ struct snd_soc_dapm_route *route)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
+ size_t len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
+ char buf[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ u32 port;
+
+ /* See parse_link_formatted_string() for dynamic naming when(s). */
+ if (hweight_long(mach->mach_params.i2s_link_mask) == 1) {
+ port = __ffs(mach->mach_params.i2s_link_mask);
+
+ snprintf(buf, len, route->source, port);
+ strncpy((char *)route->source, buf, len);
+ snprintf(buf, len, route->sink, port);
+ strncpy((char *)route->sink, buf, len);
+ if (route->control) {
+ snprintf(buf, len, route->control, port);
+ strncpy((char *)route->control, buf, len);
+ }
+ }
+
+ return 0;
+}
+
+static int avs_widget_load(struct snd_soc_component *comp, int index,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *dw)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct avs_tplg_path_template *template;
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg;
+
+ if (!le32_to_cpu(dw->priv.size))
+ return 0;
+
+ tplg = acomp->tplg;
+ mach = dev_get_platdata(comp->card->dev);
+
+ /* See parse_link_formatted_string() for dynamic naming when(s). */
+ if (hweight_long(mach->mach_params.i2s_link_mask) == 1) {
+ kfree(w->name);
+ /* w->name is freed later by soc_tplg_dapm_widget_create() */
+ w->name = kasprintf(GFP_KERNEL, dw->name, __ffs(mach->mach_params.i2s_link_mask));
+ if (!w->name)
+ return -ENOMEM;
+ }
+
+ template = avs_tplg_path_template_create(comp, tplg, dw->priv.array,
+ le32_to_cpu(dw->priv.size));
+ if (IS_ERR(template)) {
+ dev_err(comp->dev, "widget %s load failed: %ld\n", dw->name,
+ PTR_ERR(template));
+ return PTR_ERR(template);
+ }
+
+ w->priv = template; /* link path information to widget */
+ list_add_tail(&template->node, &tplg->path_tmpl_list);
+ return 0;
+}
+
+static int avs_dai_load(struct snd_soc_component *comp, int index,
+ struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm,
+ struct snd_soc_dai *dai)
+{
+ if (pcm)
+ dai_drv->ops = &avs_dai_fe_ops;
+ return 0;
+}
+
+static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg)
+{
+ if (!link->no_pcm) {
+ /* Stream control handled by IPCs. */
+ link->nonatomic = true;
+
+ /* Open LINK (BE) pipes last and close them first to prevent xruns. */
+ link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE;
+ link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE;
+ }
+
+ return 0;
+}
+
+static const struct avs_tplg_token_parser manifest_parsers[] = {
+ {
+ .token = AVS_TKN_MANIFEST_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg, name),
+ .parse = parse_link_formatted_string,
+ },
+ {
+ .token = AVS_TKN_MANIFEST_VERSION_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg, version),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int avs_manifest(struct snd_soc_component *comp, int index,
+ struct snd_soc_tplg_manifest *manifest)
+{
+ struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array;
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ size_t remaining = le32_to_cpu(manifest->priv.size);
+ u32 offset;
+ int ret;
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, &offset);
+ /* Manifest MUST begin with a header. */
+ if (!ret && !offset)
+ ret = -EINVAL;
+ if (ret) {
+ dev_err(comp->dev, "incorrect manifest format: %d\n", ret);
+ return ret;
+ }
+
+ /* Process header which precedes any of the dictionaries. */
+ ret = avs_parse_tokens(comp, acomp->tplg, manifest_parsers,
+ ARRAY_SIZE(manifest_parsers), tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_AFMTS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "audio formats lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Libraries dictionary. */
+ ret = avs_tplg_parse_libraries(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "modcfgs_base lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Audio formats dictionary. */
+ ret = avs_tplg_parse_audio_formats(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "modcfgs_ext lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Module configs-base dictionary. */
+ ret = avs_tplg_parse_modcfgs_base(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "pplcfgs lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Module configs-ext dictionary. */
+ ret = avs_tplg_parse_modcfgs_ext(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_BINDINGS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "bindings lookup failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Pipeline configs dictionary. */
+ ret = avs_tplg_parse_pplcfgs(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* Bindings dictionary. */
+ return avs_tplg_parse_bindings(comp, tuples, remaining);
+}
+
+static struct snd_soc_tplg_ops avs_tplg_ops = {
+ .dapm_route_load = avs_route_load,
+ .widget_load = avs_widget_load,
+ .dai_load = avs_dai_load,
+ .link_load = avs_link_load,
+ .manifest = avs_manifest,
+};
+
+struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp)
+{
+ struct avs_tplg *tplg;
+
+ tplg = devm_kzalloc(comp->card->dev, sizeof(*tplg), GFP_KERNEL);
+ if (!tplg)
+ return NULL;
+
+ tplg->comp = comp;
+ INIT_LIST_HEAD(&tplg->path_tmpl_list);
+
+ return tplg;
+}
+
+int avs_load_topology(struct snd_soc_component *comp, const char *filename)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, filename, comp->dev);
+ if (ret < 0) {
+ dev_err(comp->dev, "request topology \"%s\" failed: %d\n", filename, ret);
+ return ret;
+ }
+
+ ret = snd_soc_tplg_component_load(comp, &avs_tplg_ops, fw);
+ if (ret < 0)
+ dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret);
+
+ release_firmware(fw);
+ return ret;
+}
+
+int avs_remove_topology(struct snd_soc_component *comp)
+{
+ snd_soc_tplg_component_remove(comp);
+
+ return 0;
+}
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
new file mode 100644
index 000000000000..68e5f6312353
--- /dev/null
+++ b/sound/soc/intel/avs/topology.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_TPLG_H
+#define __SOUND_SOC_INTEL_AVS_TPLG_H
+
+#include <linux/list.h>
+#include "messages.h"
+
+#define INVALID_OBJECT_ID UINT_MAX
+
+struct snd_soc_component;
+
+struct avs_tplg {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ u32 version;
+ struct snd_soc_component *comp;
+
+ struct avs_tplg_library *libs;
+ u32 num_libs;
+ struct avs_audio_format *fmts;
+ u32 num_fmts;
+ struct avs_tplg_modcfg_base *modcfgs_base;
+ u32 num_modcfgs_base;
+ struct avs_tplg_modcfg_ext *modcfgs_ext;
+ u32 num_modcfgs_ext;
+ struct avs_tplg_pplcfg *pplcfgs;
+ u32 num_pplcfgs;
+ struct avs_tplg_binding *bindings;
+ u32 num_bindings;
+
+ struct list_head path_tmpl_list;
+};
+
+struct avs_tplg_library {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+};
+
+/* Matches header of struct avs_mod_cfg_base. */
+struct avs_tplg_modcfg_base {
+ u32 cpc;
+ u32 ibs;
+ u32 obs;
+ u32 is_pages;
+};
+
+struct avs_tplg_pin_format {
+ u32 pin_index;
+ u32 iobs;
+ struct avs_audio_format *fmt;
+};
+
+struct avs_tplg_modcfg_ext {
+ guid_t type;
+
+ union {
+ struct {
+ u16 num_input_pins;
+ u16 num_output_pins;
+ struct avs_tplg_pin_format *pin_fmts;
+ } generic;
+ struct {
+ struct avs_audio_format *out_fmt;
+ struct avs_audio_format *blob_fmt; /* optional override */
+ u32 feature_mask;
+ union avs_virtual_index vindex;
+ u32 dma_type;
+ u32 dma_buffer_size;
+ u32 config_length;
+ /* config_data part of priv data */
+ } copier;
+ struct {
+ u32 out_channel_config;
+ u32 coefficients_select;
+ s32 coefficients[AVS_CHANNELS_MAX];
+ u32 channel_map;
+ } updown_mix;
+ struct {
+ u32 out_freq;
+ } src;
+ struct {
+ u32 out_freq;
+ u8 mode;
+ u8 disable_jitter_buffer;
+ } asrc;
+ struct {
+ u32 cpc_lp_mode;
+ } wov;
+ struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ u32 cpc_lp_mode;
+ } aec;
+ struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ } mux;
+ struct {
+ struct avs_audio_format *out_fmt;
+ } micsel;
+ };
+};
+
+/* Specifies path behaviour during PCM ->trigger(START) command. */
+enum avs_tplg_trigger {
+ AVS_TPLG_TRIGGER_AUTO = 0,
+};
+
+struct avs_tplg_pplcfg {
+ u16 req_size;
+ u8 priority;
+ bool lp;
+ u16 attributes;
+ enum avs_tplg_trigger trigger;
+};
+
+struct avs_tplg_binding {
+ char target_tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ u32 target_path_tmpl_id;
+ u32 target_ppl_id;
+ u32 target_mod_id;
+ u8 target_mod_pin;
+ u32 mod_id;
+ u8 mod_pin;
+ u8 is_sink;
+};
+
+struct avs_tplg_path_template_id {
+ u32 id;
+ char tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+};
+
+struct avs_tplg_path_template {
+ u32 id;
+
+ struct list_head path_list;
+
+ struct avs_tplg *owner;
+ /* Driver path templates management. */
+ struct list_head node;
+};
+
+struct avs_tplg_path {
+ u32 id;
+
+ /* Path format requirements. */
+ struct avs_audio_format *fe_fmt;
+ struct avs_audio_format *be_fmt;
+
+ struct list_head ppl_list;
+
+ struct avs_tplg_path_template *owner;
+ /* Path template path-variants management. */
+ struct list_head node;
+};
+
+struct avs_tplg_pipeline {
+ u32 id;
+
+ struct avs_tplg_pplcfg *cfg;
+ struct avs_tplg_binding **bindings;
+ u32 num_bindings;
+ struct list_head mod_list;
+
+ struct avs_tplg_path *owner;
+ /* Path pipelines management. */
+ struct list_head node;
+};
+
+struct avs_tplg_module {
+ u32 id;
+
+ struct avs_tplg_modcfg_base *cfg_base;
+ struct avs_audio_format *in_fmt;
+ u8 core_id;
+ u8 domain;
+ struct avs_tplg_modcfg_ext *cfg_ext;
+
+ struct avs_tplg_pipeline *owner;
+ /* Pipeline modules management. */
+ struct list_head node;
+};
+
+struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp);
+
+int avs_load_topology(struct snd_soc_component *comp, const char *filename);
+int avs_remove_topology(struct snd_soc_component *comp);
+
+#endif
diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c
new file mode 100644
index 000000000000..fcb7cfc823d6
--- /dev/null
+++ b/sound/soc/intel/avs/trace.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/types.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#define BYTES_PER_LINE 16
+#define MAX_CHUNK_SIZE ((PAGE_SIZE - 150) /* Place for trace header */ \
+ / (2 * BYTES_PER_LINE + 4) /* chars per line */ \
+ * BYTES_PER_LINE)
+
+void trace_avs_msg_payload(const void *data, size_t size)
+{
+ size_t remaining = size;
+ size_t offset = 0;
+
+ while (remaining > 0) {
+ u32 chunk;
+
+ chunk = min(remaining, (size_t)MAX_CHUNK_SIZE);
+ trace_avs_ipc_msg_payload(data, chunk, offset, size);
+
+ remaining -= chunk;
+ offset += chunk;
+ }
+}
diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h
new file mode 100644
index 000000000000..855b06bb14b0
--- /dev/null
+++ b/sound/soc/intel/avs/trace.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM intel_avs
+
+#if !defined(_TRACE_INTEL_AVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_INTEL_AVS_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(avs_dsp_core_op,
+
+ TP_PROTO(unsigned int reg, unsigned int mask, const char *op, bool flag),
+
+ TP_ARGS(reg, mask, op, flag),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, reg )
+ __field(unsigned int, mask )
+ __string(op, op )
+ __field(bool, flag )
+ ),
+
+ TP_fast_assign(
+ __entry->reg = reg;
+ __entry->mask = mask;
+ __assign_str(op, op);
+ __entry->flag = flag;
+ ),
+
+ TP_printk("%s: %d, core mask: 0x%X, prev state: 0x%08X",
+ __get_str(op), __entry->flag, __entry->mask, __entry->reg)
+);
+
+#ifndef __TRACE_INTEL_AVS_TRACE_HELPER
+#define __TRACE_INTEL_AVS_TRACE_HELPER
+
+void trace_avs_msg_payload(const void *data, size_t size);
+
+#define trace_avs_request(msg, fwregs) \
+({ \
+ trace_avs_ipc_request_msg((msg)->header, fwregs); \
+ trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_reply(msg, fwregs) \
+({ \
+ trace_avs_ipc_reply_msg((msg)->header, fwregs); \
+ trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_notify(msg, fwregs) \
+({ \
+ trace_avs_ipc_notify_msg((msg)->header, fwregs); \
+ trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+#endif
+
+DECLARE_EVENT_CLASS(avs_ipc_msg_hdr,
+
+ TP_PROTO(u64 header, u64 fwregs),
+
+ TP_ARGS(header, fwregs),
+
+ TP_STRUCT__entry(
+ __field(u64, header)
+ __field(u64, fwregs)
+ ),
+
+ TP_fast_assign(
+ __entry->header = header;
+ __entry->fwregs = fwregs;
+ ),
+
+ TP_printk("primary: 0x%08X, extension: 0x%08X,\n"
+ "fwstatus: 0x%08X, fwerror: 0x%08X",
+ lower_32_bits(__entry->header), upper_32_bits(__entry->header),
+ lower_32_bits(__entry->fwregs), upper_32_bits(__entry->fwregs))
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg,
+ TP_PROTO(u64 header, u64 fwregs),
+ TP_ARGS(header, fwregs)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg,
+ TP_PROTO(u64 header, u64 fwregs),
+ TP_ARGS(header, fwregs)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg,
+ TP_PROTO(u64 header, u64 fwregs),
+ TP_ARGS(header, fwregs)
+);
+
+TRACE_EVENT_CONDITION(avs_ipc_msg_payload,
+
+ TP_PROTO(const u8 *data, size_t size, size_t offset, size_t total),
+
+ TP_ARGS(data, size, offset, total),
+
+ TP_CONDITION(data && size),
+
+ TP_STRUCT__entry(
+ __dynamic_array(u8, buf, size )
+ __field(size_t, offset )
+ __field(size_t, pos )
+ __field(size_t, total )
+ ),
+
+ TP_fast_assign(
+ memcpy(__get_dynamic_array(buf), data + offset, size);
+ __entry->offset = offset;
+ __entry->pos = offset + size;
+ __entry->total = total;
+ ),
+
+ TP_printk("range %zu-%zu out of %zu bytes%s",
+ __entry->offset, __entry->pos, __entry->total,
+ __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4,
+ __get_dynamic_array(buf),
+ __get_dynamic_array_len(buf), false))
+);
+
+TRACE_EVENT(avs_d0ix,
+
+ TP_PROTO(const char *op, bool proceed, u64 header),
+
+ TP_ARGS(op, proceed, header),
+
+ TP_STRUCT__entry(
+ __string(op, op )
+ __field(bool, proceed )
+ __field(u64, header )
+ ),
+
+ TP_fast_assign(
+ __assign_str(op, op);
+ __entry->proceed = proceed;
+ __entry->header = header;
+ ),
+
+ TP_printk("%s%s for request: 0x%08X 0x%08X",
+ __entry->proceed ? "" : "ignore ", __get_str(op),
+ lower_32_bits(__entry->header), upper_32_bits(__entry->header))
+);
+
+#endif /* _TRACE_INTEL_AVS_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c
index 6473e3ae4c6e..13611dee9787 100644
--- a/sound/soc/intel/avs/utils.c
+++ b/sound/soc/intel/avs/utils.c
@@ -7,6 +7,7 @@
//
#include <linux/firmware.h>
+#include <linux/kfifo.h>
#include <linux/slab.h>
#include "avs.h"
#include "messages.h"
@@ -299,3 +300,25 @@ void avs_release_firmwares(struct avs_dev *adev)
kfree(entry);
}
}
+
+unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
+ spinlock_t *lock)
+{
+ struct __kfifo *__fifo = &fifo->kfifo;
+ unsigned long flags;
+ unsigned int l, off;
+
+ spin_lock_irqsave(lock, flags);
+ len = min(len, kfifo_avail(fifo));
+ off = __fifo->in & __fifo->mask;
+ l = min(len, kfifo_size(fifo) - off);
+
+ memcpy_fromio(__fifo->data + off, src, l);
+ memcpy_fromio(__fifo->data, src + l, len - l);
+ /* Make sure data copied from SRAM is visible to all CPUs. */
+ smp_mb();
+ __fifo->in += len;
+ spin_unlock_irqrestore(lock, flags);
+
+ return len;
+}
diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c
index bc0eab1c304a..aae857fdcdb8 100644
--- a/sound/soc/intel/boards/bdw-rt5650.c
+++ b/sound/soc/intel/boards/bdw-rt5650.c
@@ -192,15 +192,15 @@ static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd)
}
/* Create and initialize headphone jack */
- if (snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ if (snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
SND_JACK_HEADPHONE, &headphone_jack,
&headphone_jack_pin, 1)) {
dev_err(component->dev, "Can't create headphone jack\n");
}
/* Create and initialize mic jack */
- if (snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
- &mic_jack, &mic_jack_pin, 1)) {
+ if (snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE, &mic_jack, &mic_jack_pin, 1)) {
dev_err(component->dev, "Can't create mic jack\n");
}
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
index 071557fada29..d0ecbba2febe 100644
--- a/sound/soc/intel/boards/bdw-rt5677.c
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -256,7 +256,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
}
/* Create and initialize headphone jack */
- if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ if (!snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
SND_JACK_HEADPHONE, &headphone_jack,
&headphone_jack_pin, 1)) {
headphone_jack_gpio.gpiod_dev = component->dev;
@@ -268,7 +268,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
}
/* Create and initialize mic jack */
- if (!snd_soc_card_jack_new(rtd->card, "Mic Jack",
+ if (!snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
SND_JACK_MICROPHONE, &mic_jack,
&mic_jack_pin, 1)) {
mic_jack_gpio.gpiod_dev = component->dev;
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index d37c74fd1a3c..c30a9dca6801 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -69,7 +69,7 @@ static int broadwell_rt286_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, "Headset",
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
if (ret)
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index 9bc7b88e346b..d98376da425a 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -234,7 +234,7 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
- &broxton_headset, NULL, 0);
+ &broxton_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -720,8 +720,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &broxton_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &broxton_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 05e833076499..75995d17597d 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -168,7 +168,7 @@ static int broxton_rt298_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, "Headset",
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&broxton_headset,
broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins));
@@ -544,8 +544,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &broxton_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &broxton_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 96d3201efbbd..0eed68a11f7e 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -87,11 +87,11 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_card_jack_new(card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &byt_cht_cx2072x_headset,
- byt_cht_cx2072x_headset_pins,
- ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &byt_cht_cx2072x_headset,
+ byt_cht_cx2072x_headset_pins,
+ ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
if (ret)
return ret;
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index e18371b5a771..a08507783e44 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -219,10 +219,10 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- ret = snd_soc_card_jack_new(card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &priv->jack, byt_cht_es8316_jack_pins,
- ARRAY_SIZE(byt_cht_es8316_jack_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, byt_cht_es8316_jack_pins,
+ ARRAY_SIZE(byt_cht_es8316_jack_pins));
if (ret) {
dev_err(card->dev, "jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index d76a505052fb..7b948a219177 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -773,6 +773,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_OVCD_SF_0P75 |
BYT_RT5640_MCLK_EN),
},
+ { /* HP Pro Tablet 408 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pro Tablet 408"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_1500UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{ /* HP Stream 7 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
@@ -1300,10 +1312,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
}
if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) {
- ret = snd_soc_card_jack_new(card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &priv->jack, rt5640_pins,
- ARRAY_SIZE(rt5640_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, rt5640_pins,
+ ARRAY_SIZE(rt5640_pins));
if (ret) {
dev_err(card->dev, "Jack creation failed %d\n", ret);
return ret;
@@ -1321,17 +1333,17 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
}
if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) {
- ret = snd_soc_card_jack_new(card, "Headset",
- SND_JACK_HEADSET,
- &priv->jack, rt5640_pins,
- ARRAY_SIZE(rt5640_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET,
+ &priv->jack, rt5640_pins,
+ ARRAY_SIZE(rt5640_pins));
if (ret)
return ret;
- ret = snd_soc_card_jack_new(card, "Headset 2",
- SND_JACK_HEADSET,
- &priv->jack2, rt5640_pins2,
- ARRAY_SIZE(rt5640_pins2));
+ ret = snd_soc_card_jack_new_pins(card, "Headset 2",
+ SND_JACK_HEADSET,
+ &priv->jack2, rt5640_pins2,
+ ARRAY_SIZE(rt5640_pins2));
if (ret)
return ret;
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 39348d2b242f..d467fcaa48ea 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -652,9 +652,10 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
report = SND_JACK_HEADSET;
if (report) {
- ret = snd_soc_card_jack_new(runtime->card, "Headset",
- report, &priv->jack, bytcr_jack_pins,
- ARRAY_SIZE(bytcr_jack_pins));
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",
+ report, &priv->jack,
+ bytcr_jack_pins,
+ ARRAY_SIZE(bytcr_jack_pins));
if (ret) {
dev_err(runtime->dev, "jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index 8d8e96e3cd2d..00384c6fbcaa 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -226,9 +226,9 @@ static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
jack_type = ARIZONA_JACK_MASK | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3;
- ret = snd_soc_card_jack_new(card, "Headset", jack_type,
- &priv->jack, byt_wm5102_pins,
- ARRAY_SIZE(byt_wm5102_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset", jack_type,
+ &priv->jack, byt_wm5102_pins,
+ ARRAY_SIZE(byt_wm5102_pins));
if (ret) {
dev_err(card->dev, "Error creating jack: %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index b3d7a0725ef2..a5160f27adea 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -201,9 +201,10 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
- ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
- jack_type, jack,
- hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset Jack",
+ jack_type, jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret) {
dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
return ret;
@@ -306,8 +307,7 @@ static int cht_max98090_headset_init(struct snd_soc_component *component)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3;
- ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type,
- jack, NULL, 0);
+ ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, jack);
if (ret) {
dev_err(card->dev, "Headset Jack creation failed %d\n", ret);
return ret;
@@ -538,7 +538,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
const char *platform_name;
bool sof_parent;
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
@@ -559,8 +559,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
}
/* override platform name, if required */
- snd_soc_card_cht.dev = &pdev->dev;
- mach = pdev->dev.platform_data;
+ snd_soc_card_cht.dev = dev;
+ mach = dev->platform_data;
platform_name = mach->mach_params.platform;
ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
@@ -576,9 +576,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
else
mclk_name = "pmc_plt_clk_3";
- drv->mclk = devm_clk_get(&pdev->dev, mclk_name);
+ drv->mclk = devm_clk_get(dev, mclk_name);
if (IS_ERR(drv->mclk)) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"Failed to get MCLK from %s: %ld\n",
mclk_name, PTR_ERR(drv->mclk));
return PTR_ERR(drv->mclk);
@@ -594,12 +594,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (drv->quirks & QUIRK_PMC_PLT_CLK_0) {
ret_val = clk_prepare_enable(drv->mclk);
if (ret_val < 0) {
- dev_err(&pdev->dev, "MCLK enable error: %d\n", ret_val);
+ dev_err(dev, "MCLK enable error: %d\n", ret_val);
return ret_val;
}
}
- sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
+ sof_parent = snd_soc_acpi_sof_parent(dev);
/* set card and driver name */
if (sof_parent) {
@@ -614,9 +614,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (sof_parent)
dev->driver->pm = &snd_soc_pm_ops;
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ ret_val = devm_snd_soc_register_card(dev, &snd_soc_card_cht);
if (ret_val) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"snd_soc_register_card failed %d\n", ret_val);
return ret_val;
}
diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c
index da6c659de266..4c1d83b317c7 100644
--- a/sound/soc/intel/boards/cht_bsw_nau8824.c
+++ b/sound/soc/intel/boards/cht_bsw_nau8824.c
@@ -108,8 +108,8 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
*/
jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3;
- ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, jack,
- cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,
+ jack, cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
if (ret) {
dev_err(runtime->dev,
"Headset Jack creation failed %d\n", ret);
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index c21561c6a464..45c301ea5e00 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -302,9 +302,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
else
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
- ret = snd_soc_card_jack_new(runtime->card, "Headset",
- jack_type, &ctx->jack,
- cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,
+ &ctx->jack, cht_bsw_jack_pins,
+ ARRAY_SIZE(cht_bsw_jack_pins));
if (ret) {
dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 9882aeb24d33..c80324f34b1b 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -221,12 +221,12 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
if (ret)
return ret;
- ret = snd_soc_card_jack_new(runtime->card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2,
- &ctx->headset,
- cht_bsw_headset_pins,
- ARRAY_SIZE(cht_bsw_headset_pins));
+ ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &ctx->headset,
+ cht_bsw_headset_pins,
+ ARRAY_SIZE(cht_bsw_headset_pins));
if (ret)
return ret;
diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c
index 27615acddacd..a99f74a15b5f 100644
--- a/sound/soc/intel/boards/cml_rt1011_rt5682.c
+++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c
@@ -141,7 +141,7 @@ static int cml_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
- &ctx->headset, NULL, 0);
+ &ctx->headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -338,8 +338,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
ret = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &hdmi_jack[i],
- NULL, 0);
+ SND_JACK_AVOUT, &hdmi_jack[i]);
if (ret)
return ret;
diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c
index e4bfb0fe5f12..170164baae7d 100644
--- a/sound/soc/intel/boards/glk_rt5682_max98357a.c
+++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c
@@ -176,7 +176,7 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
- &ctx->geminilake_headset, NULL, 0);
+ &ctx->geminilake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -571,8 +571,7 @@ static int glk_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &geminilake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &geminilake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c
index a4bdf634e9b9..ceabed85e9da 100644
--- a/sound/soc/intel/boards/kbl_da7219_max98357a.c
+++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c
@@ -182,7 +182,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
- &ctx->kabylake_headset, NULL, 0);
+ &ctx->kabylake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -587,8 +587,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &skylake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &skylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c
index 620a9fbcb08f..703ccff634b0 100644
--- a/sound/soc/intel/boards/kbl_da7219_max98927.c
+++ b/sound/soc/intel/boards/kbl_da7219_max98927.c
@@ -357,7 +357,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT,
- &ctx->kabylake_headset, NULL, 0);
+ &ctx->kabylake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -965,8 +965,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &kabylake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &kabylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c
index 1cb56ec363e8..2c7a547f63c9 100644
--- a/sound/soc/intel/boards/kbl_rt5660.c
+++ b/sound/soc/intel/boards/kbl_rt5660.c
@@ -173,9 +173,9 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
}
/* Create and initialize headphone jack, this jack is not mandatory, don't return if fails */
- ret = snd_soc_card_jack_new(rtd->card, "Lineout Jack",
- SND_JACK_LINEOUT, &lineout_jack,
- &lineout_jack_pin, 1);
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Lineout Jack",
+ SND_JACK_LINEOUT, &lineout_jack,
+ &lineout_jack_pin, 1);
if (ret)
dev_warn(component->dev, "Can't create Lineout jack\n");
else {
@@ -187,9 +187,9 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
}
/* Create and initialize mic jack, this jack is not mandatory, don't return if fails */
- ret = snd_soc_card_jack_new(rtd->card, "Mic Jack",
- SND_JACK_MICROPHONE, &mic_jack,
- &mic_jack_pin, 1);
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE, &mic_jack,
+ &mic_jack_pin, 1);
if (ret)
dev_warn(component->dev, "Can't create mic jack\n");
else {
@@ -485,8 +485,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &skylake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &skylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c
index f24e0ce5d49f..8d37b2676a81 100644
--- a/sound/soc/intel/boards/kbl_rt5663_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c
@@ -273,8 +273,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd)
*/
ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset,
- NULL, 0);
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &ctx->kabylake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
return ret;
@@ -919,8 +919,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &skylake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &skylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index 6874e981c8df..564c70a0fbc8 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -230,8 +230,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd)
*/
ret = snd_soc_card_jack_new(&kabylake_audio_card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset,
- NULL, 0);
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &ctx->kabylake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
return ret;
@@ -743,8 +743,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP,pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &ctx->kabylake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &ctx->kabylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c
index 07bfb2e64b3b..e9cefa4ae56d 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_common.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_common.c
@@ -150,17 +150,11 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &pcm->hdmi_jack,
- NULL, 0);
+ SND_JACK_AVOUT, &pcm->hdmi_jack);
if (err)
return err;
- err = snd_jack_add_new_kctl(pcm->hdmi_jack.jack,
- jack_name, SND_JACK_AVOUT);
- if (err)
- dev_warn(component->dev, "failed creating Jack kctl\n");
-
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
&pcm->hdmi_jack);
if (err < 0)
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
index 7297eb05613c..8e2d03e36079 100644
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
@@ -165,8 +165,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
*/
ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
- NULL, 0);
+ SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
return ret;
@@ -610,8 +609,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
SND_JACK_AVOUT,
- &skylake_hdmi[i],
- NULL, 0);
+ &skylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
index 68efde1633b3..501f0bbfc404 100644
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
@@ -184,8 +184,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
*/
ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
- NULL, 0);
+ SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
return ret;
@@ -651,8 +650,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
SND_JACK_AVOUT,
- &skylake_hdmi[i],
- NULL, 0);
+ &skylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
index eca4a78668af..e9f9520dcea4 100644
--- a/sound/soc/intel/boards/skl_rt286.c
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -125,7 +125,7 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "Headset",
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&skylake_headset,
skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
@@ -491,8 +491,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &skylake_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &skylake_hdmi[i]);
if (err)
return err;
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
index ce78c1879887..6a979c333bc5 100644
--- a/sound/soc/intel/boards/sof_cs42l42.c
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -102,7 +102,7 @@ static int sof_cs42l42_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
- jack, NULL, 0);
+ jack);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -186,8 +186,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &pcm->hdmi_jack,
- NULL, 0);
+ SND_JACK_AVOUT, &pcm->hdmi_jack);
if (err)
return err;
diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c
index b7b3b0bf994a..a83f30b687cf 100644
--- a/sound/soc/intel/boards/sof_da7219_max98373.c
+++ b/sound/soc/intel/boards/sof_da7219_max98373.c
@@ -160,7 +160,7 @@ static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3 | SND_JACK_LINEOUT,
- &headset, NULL, 0);
+ &headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index 9d617831dd20..23d03e0f7759 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -245,10 +245,10 @@ static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
if (ret)
return ret;
- ret = snd_soc_card_jack_new(card, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &priv->jack, sof_es8316_jack_pins,
- ARRAY_SIZE(sof_es8316_jack_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &priv->jack, sof_es8316_jack_pins,
+ ARRAY_SIZE(sof_es8316_jack_pins));
if (ret) {
dev_err(card->dev, "jack creation failed %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c
index 33de043b66c6..97dcd204a246 100644
--- a/sound/soc/intel/boards/sof_nau8825.c
+++ b/sound/soc/intel/boards/sof_nau8825.c
@@ -97,7 +97,7 @@ static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
- &ctx->sof_headset, NULL, 0);
+ &ctx->sof_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c
index a2bcbeee0216..2ab568c1d40b 100644
--- a/sound/soc/intel/boards/sof_realtek_common.c
+++ b/sound/soc/intel/boards/sof_realtek_common.c
@@ -459,5 +459,44 @@ void sof_rt1308_dai_link(struct snd_soc_dai_link *link)
}
EXPORT_SYMBOL_NS(sof_rt1308_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON);
+/*
+ * 2-amp Configuration for RT1019
+ */
+
+static const struct snd_soc_dapm_route rt1019_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "Speaker" },
+ { "Right Spk", NULL, "Speaker" },
+};
+
+static struct snd_soc_dai_link_component rt1019_components[] = {
+ {
+ .name = RT1019_DEV0_NAME,
+ .dai_name = RT1019_CODEC_DAI,
+ },
+};
+
+static int rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt1019_dapm_routes,
+ ARRAY_SIZE(rt1019_dapm_routes));
+ if (ret) {
+ dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+void sof_rt1019_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = rt1019_components;
+ link->num_codecs = ARRAY_SIZE(rt1019_components);
+ link->init = rt1019_init;
+}
+EXPORT_SYMBOL_NS(sof_rt1019_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON);
+
MODULE_DESCRIPTION("ASoC Intel SOF Realtek helpers");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h
index e0a5518e8dd2..ec3eea633e04 100644
--- a/sound/soc/intel/boards/sof_realtek_common.h
+++ b/sound/soc/intel/boards/sof_realtek_common.h
@@ -39,4 +39,9 @@ void sof_rt1015_codec_conf(struct snd_soc_card *card);
#define RT1308_DEV0_NAME "i2c-10EC1308:00"
void sof_rt1308_dai_link(struct snd_soc_dai_link *link);
+#define RT1019_CODEC_DAI "HiFi"
+#define RT1019_DEV0_NAME "RTL1019:00"
+
+void sof_rt1019_dai_link(struct snd_soc_dai_link *link);
+
#endif /* __SOF_REALTEK_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 7126fcb63d90..5d67a2c87a1d 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -60,6 +60,7 @@
#define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23)
#define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24)
#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25)
+#define SOF_RT1019_SPEAKER_AMP_PRESENT BIT(26)
/* Default: MCLK on, MCLK 19.2M, SSP0 */
@@ -298,7 +299,7 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
- &ctx->sof_headset, NULL, 0);
+ &ctx->sof_headset);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -454,8 +455,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &sof_hdmi[i],
- NULL, 0);
+ SND_JACK_AVOUT, &sof_hdmi[i]);
if (err)
return err;
@@ -734,6 +734,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
SOF_RT1015_SPEAKER_AMP_100FS) ? 100 : 64);
} else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) {
sof_rt1015p_dai_link(&links[id]);
+ } else if (sof_rt5682_quirk & SOF_RT1019_SPEAKER_AMP_PRESENT) {
+ sof_rt1019_dai_link(&links[id]);
} else if (sof_rt5682_quirk &
SOF_MAX98373_SPEAKER_AMP_PRESENT) {
links[id].codecs = max_98373_components;
@@ -1071,6 +1073,16 @@ static const struct platform_device_id board_ids[] = {
SOF_BT_OFFLOAD_SSP(2) |
SOF_SSP_BT_OFFLOAD_PRESENT),
},
+ {
+ .name = "adl_rt1019_rt5682s",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_RT5682S_HEADPHONE_CODEC_PRESENT |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT1019_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4)),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c
index ea55479609a8..3a9be8211586 100644
--- a/sound/soc/intel/boards/sof_sdw_rt5682.c
+++ b/sound/soc/intel/boards/sof_sdw_rt5682.c
@@ -82,13 +82,13 @@ static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_card_jack_new(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,
- rt5682_jack_pins,
- ARRAY_SIZE(rt5682_jack_pins));
+ 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,
+ rt5682_jack_pins,
+ ARRAY_SIZE(rt5682_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c
index bb9584c8f866..c93b1f5b9440 100644
--- a/sound/soc/intel/boards/sof_sdw_rt700.c
+++ b/sound/soc/intel/boards/sof_sdw_rt700.c
@@ -82,13 +82,13 @@ static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_card_jack_new(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,
- rt700_jack_pins,
- ARRAY_SIZE(rt700_jack_pins));
+ 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,
+ rt700_jack_pins,
+ ARRAY_SIZE(rt700_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c
index c38b70c9fac3..49ff0871e9e7 100644
--- a/sound/soc/intel/boards/sof_sdw_rt711.c
+++ b/sound/soc/intel/boards/sof_sdw_rt711.c
@@ -106,13 +106,13 @@ static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_card_jack_new(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,
- rt711_jack_pins,
- ARRAY_SIZE(rt711_jack_pins));
+ 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,
+ rt711_jack_pins,
+ ARRAY_SIZE(rt711_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
diff --git a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c b/sound/soc/intel/boards/sof_sdw_rt711_sdca.c
index 4215ddc36419..b3fc32bacfa8 100644
--- a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c
+++ b/sound/soc/intel/boards/sof_sdw_rt711_sdca.c
@@ -107,13 +107,13 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_card_jack_new(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,
- rt711_sdca_jack_pins,
- ARRAY_SIZE(rt711_sdca_jack_pins));
+ 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,
+ rt711_sdca_jack_pins,
+ ARRAY_SIZE(rt711_sdca_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c
index 88530e9de543..4a762e002ac7 100644
--- a/sound/soc/intel/boards/sof_ssp_amp.c
+++ b/sound/soc/intel/boards/sof_ssp_amp.c
@@ -9,6 +9,7 @@
#include <linux/acpi.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
@@ -78,6 +79,16 @@ struct sof_card_private {
bool idisp_codec;
};
+static const struct dmi_system_id chromebook_platforms[] = {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {},
+};
+
static const struct snd_soc_dapm_widget sof_ssp_amp_dapm_widgets[] = {
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
};
@@ -94,7 +105,7 @@ static int sof_card_late_probe(struct snd_soc_card *card)
char jack_name[NAME_SIZE];
struct sof_hdmi_pcm *pcm;
int err;
- int i = 0;
+ int i;
if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT))
return 0;
@@ -113,13 +124,13 @@ static int sof_card_late_probe(struct snd_soc_card *card)
return hda_dsp_hdmi_build_controls(card, component);
}
+ i = 0;
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &pcm->sof_hdmi,
- NULL, 0);
+ SND_JACK_AVOUT, &pcm->sof_hdmi);
if (err)
return err;
@@ -247,6 +258,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
sof_rt1308_dai_link(&links[id]);
} else if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) {
cs35l41_set_dai_link(&links[id]);
+
+ /* feedback from amplifier */
+ links[id].dpcm_capture = 1;
}
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
@@ -371,7 +385,7 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
struct snd_soc_dai_link *dai_links;
struct snd_soc_acpi_mach *mach;
struct sof_card_private *ctx;
- int dmic_be_num, hdmi_num = 0;
+ int dmic_be_num = 0, hdmi_num = 0;
int ret, ssp_codec;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
@@ -383,7 +397,8 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
mach = pdev->dev.platform_data;
- dmic_be_num = mach->mach_params.dmic_num;
+ if (dmi_check_system(chromebook_platforms) || mach->mach_params.dmic_num > 0)
+ dmic_be_num = 2;
ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK;
diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h
index 978a20b3f471..c17e948d9f52 100644
--- a/sound/soc/intel/catpt/messages.h
+++ b/sound/soc/intel/catpt/messages.h
@@ -219,11 +219,9 @@ int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id);
enum catpt_ssp_iface {
CATPT_SSP_IFACE_0 = 0,
CATPT_SSP_IFACE_1 = 1,
- CATPT_SSP_IFACE_LAST = CATPT_SSP_IFACE_1,
+ CATPT_SSP_COUNT,
};
-#define CATPT_SSP_COUNT (CATPT_SSP_IFACE_LAST + 1)
-
enum catpt_mclk_frequency {
CATPT_MCLK_OFF = 0,
CATPT_MCLK_FREQ_6_MHZ = 1,
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 8bfe7070b84a..e6e52c7b6803 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -456,6 +456,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
.sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg",
},
{
+ .id = "RTL5682",
+ .drv_name = "adl_rt1019_rt5682s",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_rt1019p_amp,
+ .sof_tplg_filename = "sof-adl-rt1019-rt5682.tplg",
+ },
+ {
.id = "10508825",
.drv_name = "sof_nau8825",
.sof_tplg_filename = "sof-adl-nau8825.tplg",
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 0d154350f180..9e5ce1a82639 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -179,6 +179,7 @@ config SND_SOC_MT8192_MT6359_RT1015_RT5682
select SND_SOC_RT1015
select SND_SOC_RT1015P
select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
select SND_SOC_DMIC
help
This adds ASoC driver for Mediatek MT8192 boards
@@ -198,34 +199,20 @@ config SND_SOC_MT8195
Select Y if you have such device.
If unsure select "N".
-config SND_SOC_MT8195_MT6359_RT1019_RT5682
- tristate "ASoC Audio driver for MT8195 with MT6359 RT1019 RT5682 codec"
- depends on I2C && GPIOLIB
- depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
- select SND_SOC_MT6359
- select SND_SOC_RT1015P
- select SND_SOC_RT5682_I2C
- select SND_SOC_RT5682S
- select SND_SOC_DMIC
- select SND_SOC_HDMI_CODEC
- help
- This adds ASoC driver for Mediatek MT8195 boards
- with the MT6359 RT1019 RT5682 audio codec.
- Select Y if you have such device.
- If unsure select "N".
-
-config SND_SOC_MT8195_MT6359_RT1011_RT5682
- tristate "ASoC Audio driver for MT8195 with MT6359 RT1011 RT5682 codec"
+config SND_SOC_MT8195_MT6359
+ tristate "ASoC Audio driver for MT8195 with MT6359 and I2S codecs"
depends on I2C && GPIOLIB
depends on SND_SOC_MT8195 && MTK_PMIC_WRAP
select SND_SOC_MT6359
select SND_SOC_RT1011
+ select SND_SOC_RT1015P
select SND_SOC_RT5682_I2C
select SND_SOC_RT5682S
+ select SND_SOC_MAX98390
select SND_SOC_DMIC
select SND_SOC_HDMI_CODEC
help
- This adds ASoC driver for Mediatek MT8195 boards
- with the MT6359 RT1011 RT5682 audio codec.
+ This adds support for ASoC machine driver for Mediatek MT8195
+ boards with the MT6359 and other I2S audio codecs.
Select Y if you have such device.
If unsure select "N".
diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
index f56de1b918bf..0cdf2ae36243 100644
--- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c
+++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
@@ -129,7 +129,8 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_platform_node;
}
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->codecs->name)
@@ -140,7 +141,7 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
if (ret) {
dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
- return ret;
+ goto put_codec_node;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
@@ -148,6 +149,10 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
+put_codec_node:
+ of_node_put(codec_node);
+put_platform_node:
+ of_node_put(platform_node);
return ret;
}
diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
index 4cb90da89262..c2b0619b6158 100644
--- a/sound/soc/mediatek/mt8173/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -70,10 +70,10 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)
struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component;
/* enable jack detection */
- ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE,
- &mt8173_max98090_jack,
- mt8173_max98090_jack_pins,
- ARRAY_SIZE(mt8173_max98090_jack_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADPHONE,
+ &mt8173_max98090_jack,
+ mt8173_max98090_jack_pins,
+ ARRAY_SIZE(mt8173_max98090_jack_pins));
if (ret) {
dev_err(card->dev, "Can't create a new Jack %d\n", ret);
return ret;
@@ -167,7 +167,8 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_platform_node;
}
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->codecs->name)
@@ -179,6 +180,8 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
of_node_put(codec_node);
+
+put_platform_node:
of_node_put(platform_node);
return ret;
}
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index b55122b99f07..12f40c81b101 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -86,7 +86,7 @@ static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime)
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &mt8173_rt5650_rt5514_jack, NULL, 0);
+ &mt8173_rt5650_rt5514_jack);
if (ret) {
dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
return ret;
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index 5716d9299066..70bf312e855f 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -99,7 +99,7 @@ static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime)
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &mt8173_rt5650_rt5676_jack, NULL, 0);
+ &mt8173_rt5650_rt5676_jack);
if (ret) {
dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
return ret;
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index fc164f4f95f8..d1c94acb4516 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -132,7 +132,7 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &mt8173_rt5650_jack, NULL, 0);
+ &mt8173_rt5650_jack);
if (ret) {
dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
return ret;
@@ -149,7 +149,7 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)
int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &mt8173_rt5650_hdmi_jack, NULL, 0);
+ &mt8173_rt5650_hdmi_jack);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
index f090dee0c7a4..b33cc9a73ed1 100644
--- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -364,7 +364,7 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &priv->hdmi_jack, NULL, 0);
+ &priv->hdmi_jack);
if (ret)
return ret;
@@ -546,8 +546,7 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_LINEOUT,
- &priv->headset_jack,
- NULL, 0);
+ &priv->headset_jack);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index 889f9e4a96aa..ab157db78335 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -383,7 +383,7 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &priv->hdmi_jack, NULL, 0);
+ &priv->hdmi_jack);
if (ret)
return ret;
@@ -613,8 +613,7 @@ mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component)
SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &priv->headset_jack,
- NULL, 0);
+ &priv->headset_jack);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
index ee91569c0911..d0f9d66627b1 100644
--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -28,8 +28,13 @@
#define RT1015_DEV0_NAME "rt1015.1-0028"
#define RT1015_DEV1_NAME "rt1015.1-0029"
-#define RT5682_CODEC_DAI "rt5682-aif1"
-#define RT5682_DEV0_NAME "rt5682.1-001a"
+#define RT1015_RT5682_CARD_NAME "mt8192_mt6359_rt1015_rt5682"
+#define RT1015P_RT5682_CARD_NAME "mt8192_mt6359_rt1015p_rt5682"
+#define RT1015P_RT5682S_CARD_NAME "mt8192_mt6359_rt1015p_rt5682s"
+
+#define RT1015_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015_rt5682"
+#define RT1015P_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682"
+#define RT1015P_RT5682S_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682s"
struct mt8192_mt6359_priv {
struct snd_soc_jack headset_jack;
@@ -71,8 +76,8 @@ static int mt8192_rt1015_i2s_hw_params(struct snd_pcm_substream *substream,
return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
}
-static int mt8192_rt5682_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int mt8192_rt5682x_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
@@ -121,8 +126,8 @@ static const struct snd_soc_ops mt8192_rt1015_i2s_ops = {
.hw_params = mt8192_rt1015_i2s_hw_params,
};
-static const struct snd_soc_ops mt8192_rt5682_i2s_ops = {
- .hw_params = mt8192_rt5682_i2s_hw_params,
+static const struct snd_soc_ops mt8192_rt5682x_i2s_ops = {
+ .hw_params = mt8192_rt5682x_i2s_hw_params,
};
static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
@@ -316,7 +321,7 @@ static int mt8192_rt5682_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
- jack, NULL, 0);
+ jack);
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -338,7 +343,7 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd)
int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &priv->hdmi_jack, NULL, 0);
+ &priv->hdmi_jack);
if (ret) {
dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
return ret;
@@ -604,17 +609,9 @@ SND_SOC_DAILINK_DEFS(i2s2,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
-SND_SOC_DAILINK_DEFS(i2s3_rt1015,
- DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
- DAILINK_COMP_ARRAY(COMP_CODEC(RT1015_DEV0_NAME,
- RT1015_CODEC_DAI),
- COMP_CODEC(RT1015_DEV1_NAME,
- RT1015_CODEC_DAI)),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(i2s3_rt1015p,
+SND_SOC_DAILINK_DEFS(i2s3,
DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
- DAILINK_COMP_ARRAY(COMP_CODEC("rt1015p", "HiFi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(i2s5,
@@ -634,14 +631,12 @@ SND_SOC_DAILINK_DEFS(i2s7,
SND_SOC_DAILINK_DEFS(i2s8,
DAILINK_COMP_ARRAY(COMP_CPU("I2S8")),
- DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
- RT5682_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(i2s9,
DAILINK_COMP_ARRAY(COMP_CPU("I2S9")),
- DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME,
- RT5682_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(connsys_i2s,
@@ -929,6 +924,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
.dpcm_playback = 1,
.ignore_suspend = 1,
.be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2s3),
},
{
.name = "I2S5",
@@ -962,7 +958,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
.init = mt8192_rt5682_init,
.be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
SND_SOC_DAILINK_REG(i2s8),
- .ops = &mt8192_rt5682_i2s_ops,
+ .ops = &mt8192_rt5682x_i2s_ops,
},
{
.name = "I2S9",
@@ -971,7 +967,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
.ignore_suspend = 1,
.be_hw_params_fixup = mt8192_i2s_hw_params_fixup,
SND_SOC_DAILINK_REG(i2s9),
- .ops = &mt8192_rt5682_i2s_ops,
+ .ops = &mt8192_rt5682x_i2s_ops,
},
{
.name = "CONNSYS_I2S",
@@ -1051,7 +1047,7 @@ static struct snd_soc_codec_conf rt1015_amp_conf[] = {
};
static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = {
- .name = "mt8192_mt6359_rt1015_rt5682",
+ .name = RT1015_RT5682_CARD_NAME,
.owner = THIS_MODULE,
.dai_link = mt8192_mt6359_dai_links,
.num_links = ARRAY_SIZE(mt8192_mt6359_dai_links),
@@ -1065,14 +1061,13 @@ static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = {
.num_configs = ARRAY_SIZE(rt1015_amp_conf),
};
-static const struct snd_soc_dapm_widget
-mt8192_mt6359_rt1015p_rt5682_widgets[] = {
+static const struct snd_soc_dapm_widget mt8192_mt6359_rt1015p_rt5682x_widgets[] = {
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
-static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682_routes[] = {
+static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682x_routes[] = {
/* speaker */
{ "Speakers", NULL, "Speaker" },
/* headset */
@@ -1081,74 +1076,107 @@ static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682_routes[] = {
{ "IN1P", NULL, "Headset Mic" },
};
-static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682_controls[] = {
+static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682x_controls[] = {
SOC_DAPM_PIN_SWITCH("Speakers"),
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
-static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682_card = {
- .name = "mt8192_mt6359_rt1015p_rt5682",
+static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = {
.owner = THIS_MODULE,
.dai_link = mt8192_mt6359_dai_links,
.num_links = ARRAY_SIZE(mt8192_mt6359_dai_links),
- .controls = mt8192_mt6359_rt1015p_rt5682_controls,
- .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_controls),
- .dapm_widgets = mt8192_mt6359_rt1015p_rt5682_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_widgets),
- .dapm_routes = mt8192_mt6359_rt1015p_rt5682_routes,
- .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_routes),
+ .controls = mt8192_mt6359_rt1015p_rt5682x_controls,
+ .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_controls),
+ .dapm_widgets = mt8192_mt6359_rt1015p_rt5682x_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_widgets),
+ .dapm_routes = mt8192_mt6359_rt1015p_rt5682x_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes),
};
+static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ struct device_node *node,
+ char *link_name)
+{
+ int ret;
+
+ if (node && strcmp(link->name, link_name) == 0) {
+ ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link);
+ if (ret < 0) {
+ dev_err_probe(card->dev, ret, "get dai link codecs fail\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
- struct device_node *platform_node, *hdmi_codec;
+ struct device_node *platform_node, *hdmi_codec, *headset_codec, *speaker_codec;
int ret, i;
struct snd_soc_dai_link *dai_link;
struct mt8192_mt6359_priv *priv;
- platform_node = of_parse_phandle(pdev->dev.of_node,
- "mediatek,platform", 0);
- if (!platform_node) {
- dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
+ if (!card)
return -EINVAL;
+ card->dev = &pdev->dev;
+
+ if (of_device_is_compatible(pdev->dev.of_node, RT1015P_RT5682_OF_NAME))
+ card->name = RT1015P_RT5682_CARD_NAME;
+ else if (of_device_is_compatible(pdev->dev.of_node, RT1015P_RT5682S_OF_NAME))
+ card->name = RT1015P_RT5682S_CARD_NAME;
+ else
+ dev_dbg(&pdev->dev, "No need to set card name\n");
+
+ hdmi_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,hdmi-codec", 0);
+ if (!hdmi_codec)
+ dev_dbg(&pdev->dev, "The machine has no hdmi-codec\n");
+
+ platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
+ if (!platform_node) {
+ ret = -EINVAL;
+ dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n");
+ goto err_platform_node;
}
- card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
- if (!card) {
+ speaker_codec = of_get_child_by_name(pdev->dev.of_node, "speaker-codecs");
+ if (!speaker_codec) {
ret = -EINVAL;
- goto put_platform_node;
+ dev_err_probe(&pdev->dev, ret, "Property 'speaker-codecs' missing or invalid\n");
+ goto err_speaker_codec;
}
- card->dev = &pdev->dev;
- hdmi_codec = of_parse_phandle(pdev->dev.of_node,
- "mediatek,hdmi-codec", 0);
+ headset_codec = of_get_child_by_name(pdev->dev.of_node, "headset-codec");
+ if (!headset_codec) {
+ ret = -EINVAL;
+ dev_err_probe(&pdev->dev, ret, "Property 'headset-codec' missing or invalid\n");
+ goto err_headset_codec;
+ }
for_each_card_prelinks(card, i, dai_link) {
- if (strcmp(dai_link->name, "I2S3") == 0) {
- if (card == &mt8192_mt6359_rt1015_rt5682_card) {
- dai_link->ops = &mt8192_rt1015_i2s_ops;
- dai_link->cpus = i2s3_rt1015_cpus;
- dai_link->num_cpus =
- ARRAY_SIZE(i2s3_rt1015_cpus);
- dai_link->codecs = i2s3_rt1015_codecs;
- dai_link->num_codecs =
- ARRAY_SIZE(i2s3_rt1015_codecs);
- dai_link->platforms = i2s3_rt1015_platforms;
- dai_link->num_platforms =
- ARRAY_SIZE(i2s3_rt1015_platforms);
- } else if (card == &mt8192_mt6359_rt1015p_rt5682_card) {
- dai_link->cpus = i2s3_rt1015p_cpus;
- dai_link->num_cpus =
- ARRAY_SIZE(i2s3_rt1015p_cpus);
- dai_link->codecs = i2s3_rt1015p_codecs;
- dai_link->num_codecs =
- ARRAY_SIZE(i2s3_rt1015p_codecs);
- dai_link->platforms = i2s3_rt1015p_platforms;
- dai_link->num_platforms =
- ARRAY_SIZE(i2s3_rt1015p_platforms);
- }
+ ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3");
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "%s set speaker_codec fail\n",
+ dai_link->name);
+ goto err_probe;
+ }
+
+ ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8");
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "%s set headset_codec fail\n",
+ dai_link->name);
+ goto err_probe;
+ }
+
+ ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9");
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "%s set headset_codec fail\n",
+ dai_link->name);
+ goto err_probe;
}
if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
@@ -1156,6 +1184,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
dai_link->ignore = 0;
}
+ if (strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0)
+ dai_link->ops = &mt8192_rt1015_i2s_ops;
+
if (!dai_link->platforms->name)
dai_link->platforms->of_node = platform_node;
}
@@ -1163,34 +1194,44 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
- goto put_hdmi_codec;
+ goto err_probe;
}
snd_soc_card_set_drvdata(card, priv);
ret = mt8192_afe_gpio_init(&pdev->dev);
if (ret) {
- dev_err(&pdev->dev, "init gpio error %d\n", ret);
- goto put_hdmi_codec;
+ dev_err_probe(&pdev->dev, ret, "%s init gpio error\n", __func__);
+ goto err_probe;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
-
-put_hdmi_codec:
- of_node_put(hdmi_codec);
-put_platform_node:
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
+
+err_probe:
+ of_node_put(headset_codec);
+err_headset_codec:
+ of_node_put(speaker_codec);
+err_speaker_codec:
of_node_put(platform_node);
+err_platform_node:
+ of_node_put(hdmi_codec);
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id mt8192_mt6359_dt_match[] = {
{
- .compatible = "mediatek,mt8192_mt6359_rt1015_rt5682",
+ .compatible = RT1015_RT5682_OF_NAME,
.data = &mt8192_mt6359_rt1015_rt5682_card,
},
{
- .compatible = "mediatek,mt8192_mt6359_rt1015p_rt5682",
- .data = &mt8192_mt6359_rt1015p_rt5682_card,
+ .compatible = RT1015P_RT5682_OF_NAME,
+ .data = &mt8192_mt6359_rt1015p_rt5682x_card,
+ },
+ {
+ .compatible = RT1015P_RT5682S_OF_NAME,
+ .data = &mt8192_mt6359_rt1015p_rt5682x_card,
},
{}
};
diff --git a/sound/soc/mediatek/mt8195/Makefile b/sound/soc/mediatek/mt8195/Makefile
index e5f0df5010b6..aae673ec751b 100644
--- a/sound/soc/mediatek/mt8195/Makefile
+++ b/sound/soc/mediatek/mt8195/Makefile
@@ -12,5 +12,4 @@ snd-soc-mt8195-afe-objs := \
obj-$(CONFIG_SND_SOC_MT8195) += snd-soc-mt8195-afe.o
# machine driver
-obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1019_RT5682) += mt8195-mt6359-rt1019-rt5682.o
-obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1011_RT5682) += mt8195-mt6359-rt1011-rt5682.o
+obj-$(CONFIG_SND_SOC_MT8195_MT6359) += mt8195-mt6359.o
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
index 151914c873ac..12644ded83d5 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
@@ -213,8 +213,6 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- int ret;
-
dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
__func__, dai->id, substream->stream,
dai->playback_widget->active, dai->capture_widget->active);
@@ -222,11 +220,7 @@ static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
if (dai->playback_widget->active || dai->capture_widget->active)
return 0;
- ret = mtk_dai_pcm_configure(substream, dai);
- if (ret)
- return ret;
-
- return 0;
+ return mtk_dai_pcm_configure(substream, dai);
}
static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
deleted file mode 100644
index 5443a29da7b1..000000000000
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c
+++ /dev/null
@@ -1,1198 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-//
-// mt8195-mt6359-rt1011-rt5682.c --
-// MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver
-//
-// Copyright (c) 2021 MediaTek Inc.
-// Author: Trevor Wu <trevor.wu@mediatek.com>
-//
-
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/pm_runtime.h>
-#include <sound/jack.h>
-#include <sound/pcm_params.h>
-#include <sound/rt5682.h>
-#include <sound/soc.h>
-#include "../../codecs/mt6359.h"
-#include "../../codecs/rt1011.h"
-#include "../../codecs/rt5682.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "mt8195-afe-clk.h"
-#include "mt8195-afe-common.h"
-
-#define RT1011_CODEC_DAI "rt1011-aif"
-#define RT1011_DEV0_NAME "rt1011.2-0038"
-#define RT1011_DEV1_NAME "rt1011.2-0039"
-
-#define RT5682_CODEC_DAI "rt5682-aif1"
-#define RT5682_DEV0_NAME "rt5682.2-001a"
-
-#define RT5682S_CODEC_DAI "rt5682s-aif1"
-#define RT5682S_DEV0_NAME "rt5682s.2-001a"
-
-struct mt8195_mt6359_rt1011_rt5682_priv {
- struct snd_soc_jack headset_jack;
- struct snd_soc_jack dp_jack;
- struct snd_soc_jack hdmi_jack;
- struct clk *i2so1_mclk;
-};
-
-static const struct snd_soc_dapm_widget
-mt8195_mt6359_rt1011_rt5682_widgets[] = {
- SND_SOC_DAPM_SPK("Left Speaker", NULL),
- SND_SOC_DAPM_SPK("Right Speaker", NULL),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route mt8195_mt6359_rt1011_rt5682_routes[] = {
- /* speaker */
- { "Left Speaker", NULL, "Left SPO" },
- { "Right Speaker", NULL, "Right SPO" },
- /* headset */
- { "Headphone Jack", NULL, "HPOL" },
- { "Headphone Jack", NULL, "HPOR" },
- { "IN1P", NULL, "Headset Mic" },
-};
-
-static const struct snd_kcontrol_new mt8195_mt6359_rt1011_rt5682_controls[] = {
- SOC_DAPM_PIN_SWITCH("Left Speaker"),
- SOC_DAPM_PIN_SWITCH("Right Speaker"),
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
-};
-
-static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- unsigned int rate = params_rate(params);
- int bitwidth;
- int ret;
-
- bitwidth = snd_pcm_format_width(params_format(params));
- if (bitwidth < 0) {
- dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
- return bitwidth;
- }
-
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
- if (ret) {
- dev_err(card->dev, "failed to set tdm slot\n");
- return ret;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK,
- rate * 256, rate * 512);
- if (ret) {
- dev_err(card->dev, "failed to set pll\n");
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
- rate * 512, SND_SOC_CLOCK_IN);
- if (ret) {
- dev_err(card->dev, "failed to set sysclk\n");
- return ret;
- }
-
- return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
- SND_SOC_CLOCK_OUT);
-}
-
-static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
- .hw_params = mt8195_rt5682_etdm_hw_params,
-};
-
-static int mt8195_rt1011_etdm_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;
- struct snd_soc_card *card = rtd->card;
- int srate, i, ret = 0;
-
- srate = params_rate(params);
-
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
- 64 * srate, 256 * srate);
- if (ret < 0) {
- dev_err(card->dev, "codec_dai clock not set\n");
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai,
- RT1011_FS_SYS_PRE_S_PLL1,
- 256 * srate, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "codec_dai clock not set\n");
- return ret;
- }
- }
- return ret;
-}
-
-static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
- .hw_params = mt8195_rt1011_etdm_hw_params,
-};
-
-#define CKSYS_AUD_TOP_CFG 0x032c
-#define CKSYS_AUD_TOP_MON 0x0330
-
-static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_component *cmpnt_afe =
- snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
- struct snd_soc_component *cmpnt_codec =
- asoc_rtd_to_codec(rtd, 0)->component;
- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
- struct mt8195_afe_private *afe_priv = afe->platform_priv;
- struct mtkaif_param *param = &afe_priv->mtkaif_params;
- int chosen_phase_1, chosen_phase_2, chosen_phase_3;
- int prev_cycle_1, prev_cycle_2, prev_cycle_3;
- int test_done_1, test_done_2, test_done_3;
- int cycle_1, cycle_2, cycle_3;
- int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
- int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
- int mtkaif_calibration_num_phase;
- bool mtkaif_calibration_ok;
- unsigned int monitor;
- int counter;
- int phase;
- int i;
-
- dev_dbg(afe->dev, "%s(), start\n", __func__);
-
- param->mtkaif_calibration_ok = false;
- for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) {
- param->mtkaif_chosen_phase[i] = -1;
- param->mtkaif_phase_cycle[i] = 0;
- mtkaif_chosen_phase[i] = -1;
- mtkaif_phase_cycle[i] = 0;
- }
-
- if (IS_ERR(afe_priv->topckgen)) {
- dev_info(afe->dev, "%s() Cannot find topckgen controller\n",
- __func__);
- return 0;
- }
-
- pm_runtime_get_sync(afe->dev);
- mt6359_mtkaif_calibration_enable(cmpnt_codec);
-
- /* set test type to synchronizer pulse */
- regmap_update_bits(afe_priv->topckgen,
- CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
- mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
- mtkaif_calibration_ok = true;
-
- for (phase = 0;
- phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok;
- phase++) {
- mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
- phase, phase, phase);
-
- regmap_update_bits(afe_priv->topckgen,
- CKSYS_AUD_TOP_CFG, 0x1, 0x1);
-
- test_done_1 = 0;
- test_done_2 = 0;
- test_done_3 = 0;
- cycle_1 = -1;
- cycle_2 = -1;
- cycle_3 = -1;
- counter = 0;
- while (!(test_done_1 & test_done_2 & test_done_3)) {
- regmap_read(afe_priv->topckgen,
- CKSYS_AUD_TOP_MON, &monitor);
- test_done_1 = (monitor >> 28) & 0x1;
- test_done_2 = (monitor >> 29) & 0x1;
- test_done_3 = (monitor >> 30) & 0x1;
- if (test_done_1 == 1)
- cycle_1 = monitor & 0xf;
-
- if (test_done_2 == 1)
- cycle_2 = (monitor >> 4) & 0xf;
-
- if (test_done_3 == 1)
- cycle_3 = (monitor >> 8) & 0xf;
-
- /* handle if never test done */
- if (++counter > 10000) {
- dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, cycle_3 %d, monitor 0x%x\n",
- __func__,
- cycle_1, cycle_2, cycle_3, monitor);
- mtkaif_calibration_ok = false;
- break;
- }
- }
-
- if (phase == 0) {
- prev_cycle_1 = cycle_1;
- prev_cycle_2 = cycle_2;
- prev_cycle_3 = cycle_3;
- }
-
- if (cycle_1 != prev_cycle_1 &&
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = phase - 1;
- mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] = prev_cycle_1;
- }
-
- if (cycle_2 != prev_cycle_2 &&
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = phase - 1;
- mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] = prev_cycle_2;
- }
-
- if (cycle_3 != prev_cycle_3 &&
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = phase - 1;
- mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] = prev_cycle_3;
- }
-
- regmap_update_bits(afe_priv->topckgen,
- CKSYS_AUD_TOP_CFG, 0x1, 0x0);
-
- if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] >= 0 &&
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] >= 0 &&
- mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] >= 0)
- break;
- }
-
- if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) {
- mtkaif_calibration_ok = false;
- chosen_phase_1 = 0;
- } else {
- chosen_phase_1 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0];
- }
-
- if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) {
- mtkaif_calibration_ok = false;
- chosen_phase_2 = 0;
- } else {
- chosen_phase_2 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1];
- }
-
- if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) {
- mtkaif_calibration_ok = false;
- chosen_phase_3 = 0;
- } else {
- chosen_phase_3 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2];
- }
-
- mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
- chosen_phase_1,
- chosen_phase_2,
- chosen_phase_3);
-
- mt6359_mtkaif_calibration_disable(cmpnt_codec);
- pm_runtime_put(afe->dev);
-
- param->mtkaif_calibration_ok = mtkaif_calibration_ok;
- param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = chosen_phase_1;
- param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = chosen_phase_2;
- param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = chosen_phase_3;
- for (i = 0; i < MT8195_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);
-
- return 0;
-}
-
-static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_component *cmpnt_codec =
- asoc_rtd_to_codec(rtd, 0)->component;
-
- /* set mtkaif protocol */
- mt6359_set_mtkaif_protocol(cmpnt_codec,
- MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
-
- /* mtkaif calibration */
- mt8195_mt6359_mtkaif_calibration(rtd);
-
- return 0;
-}
-
-static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_component *cmpnt_codec =
- asoc_rtd_to_codec(rtd, 0)->component;
- struct mt8195_mt6359_rt1011_rt5682_priv *priv =
- snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_jack *jack = &priv->headset_jack;
- struct snd_soc_component *cmpnt_afe =
- snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
- struct mt8195_afe_private *afe_priv = afe->platform_priv;
- int ret;
-
- priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
-
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2 |
- SND_JACK_BTN_3,
- jack, NULL, 0);
- 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(cmpnt_codec, jack, NULL);
- if (ret) {
- dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-};
-
-static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- /* fix BE i2s format to S24_LE, clean param mask first */
- snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
-
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
-
- return 0;
-}
-
-static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream)
-{
- static const unsigned int rates[] = {
- 48000
- };
- static const unsigned int channels[] = {
- 2, 4, 6, 8
- };
- static const struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
- };
- static const struct snd_pcm_hw_constraint_list constraints_channels = {
- .count = ARRAY_SIZE(channels),
- .list = channels,
- .mask = 0,
- };
-
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_rates);
- if (ret < 0) {
- dev_err(rtd->dev, "hw_constraint_list rate failed\n");
- return ret;
- }
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
- if (ret < 0) {
- dev_err(rtd->dev, "hw_constraint_list channel failed\n");
- return ret;
- }
-
- return 0;
-}
-
-static const struct snd_soc_ops mt8195_hdmitx_dptx_playback_ops = {
- .startup = mt8195_hdmitx_dptx_startup,
-};
-
-static int mt8195_dptx_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);
-
- return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256,
- SND_SOC_CLOCK_OUT);
-}
-
-static const struct snd_soc_ops mt8195_dptx_ops = {
- .hw_params = mt8195_dptx_hw_params,
-};
-
-static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct mt8195_mt6359_rt1011_rt5682_priv *priv =
- snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_component *cmpnt_codec =
- asoc_rtd_to_codec(rtd, 0)->component;
- int ret;
-
- ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
- &priv->dp_jack, NULL, 0);
- if (ret)
- return ret;
-
- return snd_soc_component_set_jack(cmpnt_codec, &priv->dp_jack, NULL);
-}
-
-static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct mt8195_mt6359_rt1011_rt5682_priv *priv =
- snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_component *cmpnt_codec =
- asoc_rtd_to_codec(rtd, 0)->component;
- int ret;
-
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &priv->hdmi_jack, NULL, 0);
- if (ret)
- return ret;
-
- return snd_soc_component_set_jack(cmpnt_codec, &priv->hdmi_jack, NULL);
-}
-
-static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-
-{
- /* fix BE i2s format to S24_LE, clean param mask first */
- snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
-
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
-
- return 0;
-}
-
-static int mt8195_playback_startup(struct snd_pcm_substream *substream)
-{
- static const unsigned int rates[] = {
- 48000
- };
- static const unsigned int channels[] = {
- 2
- };
- static const struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
- };
- static const struct snd_pcm_hw_constraint_list constraints_channels = {
- .count = ARRAY_SIZE(channels),
- .list = channels,
- .mask = 0,
- };
-
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_rates);
- if (ret < 0) {
- dev_err(rtd->dev, "hw_constraint_list rate failed\n");
- return ret;
- }
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
- if (ret < 0) {
- dev_err(rtd->dev, "hw_constraint_list channel failed\n");
- return ret;
- }
-
- return 0;
-}
-
-static const struct snd_soc_ops mt8195_playback_ops = {
- .startup = mt8195_playback_startup,
-};
-
-static int mt8195_capture_startup(struct snd_pcm_substream *substream)
-{
- static const unsigned int rates[] = {
- 48000
- };
- static const unsigned int channels[] = {
- 1, 2
- };
- static const struct snd_pcm_hw_constraint_list constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
- };
- static const struct snd_pcm_hw_constraint_list constraints_channels = {
- .count = ARRAY_SIZE(channels),
- .list = channels,
- .mask = 0,
- };
-
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_rates);
- if (ret < 0) {
- dev_err(rtd->dev, "hw_constraint_list rate failed\n");
- return ret;
- }
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_channels);
- if (ret < 0) {
- dev_err(rtd->dev, "hw_constraint_list channel failed\n");
- return ret;
- }
-
- return 0;
-}
-
-static const struct snd_soc_ops mt8195_capture_ops = {
- .startup = mt8195_capture_startup,
-};
-
-static int mt8195_set_bias_level_post(struct snd_soc_card *card,
- struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
-{
- struct snd_soc_component *component = dapm->component;
- struct mt8195_mt6359_rt1011_rt5682_priv *priv =
- snd_soc_card_get_drvdata(card);
- int ret;
-
- /*
- * It's required to control mclk directly in the set_bias_level_post
- * function for rt5682 and rt5682s codec, or the unexpected pop happens
- * at the end of playback.
- */
- if (!component ||
- (strcmp(component->name, RT5682_DEV0_NAME) &&
- strcmp(component->name, RT5682S_DEV0_NAME)))
- return 0;
-
- switch (level) {
- case SND_SOC_BIAS_OFF:
- if (!__clk_is_enabled(priv->i2so1_mclk))
- return 0;
-
- clk_disable_unprepare(priv->i2so1_mclk);
- dev_dbg(card->dev, "Disable i2so1 mclk\n");
- break;
- case SND_SOC_BIAS_ON:
- ret = clk_prepare_enable(priv->i2so1_mclk);
- if (ret) {
- dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret);
- return ret;
- }
- dev_dbg(card->dev, "Enable i2so1 mclk\n");
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-enum {
- DAI_LINK_DL2_FE,
- DAI_LINK_DL3_FE,
- DAI_LINK_DL6_FE,
- DAI_LINK_DL7_FE,
- DAI_LINK_DL8_FE,
- DAI_LINK_DL10_FE,
- DAI_LINK_DL11_FE,
- DAI_LINK_UL1_FE,
- DAI_LINK_UL2_FE,
- DAI_LINK_UL3_FE,
- DAI_LINK_UL4_FE,
- DAI_LINK_UL5_FE,
- DAI_LINK_UL6_FE,
- DAI_LINK_UL8_FE,
- DAI_LINK_UL9_FE,
- DAI_LINK_UL10_FE,
- DAI_LINK_DL_SRC_BE,
- DAI_LINK_DPTX_BE,
- DAI_LINK_ETDM1_IN_BE,
- DAI_LINK_ETDM2_IN_BE,
- DAI_LINK_ETDM1_OUT_BE,
- DAI_LINK_ETDM2_OUT_BE,
- DAI_LINK_ETDM3_OUT_BE,
- DAI_LINK_PCM1_BE,
- DAI_LINK_UL_SRC1_BE,
- DAI_LINK_UL_SRC2_BE,
-};
-
-/* FE */
-SND_SOC_DAILINK_DEFS(DL2_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL3_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL6_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL7_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL8_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL10_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL10")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DL11_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL11")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL1_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL2_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL3_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL4_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL5_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL6_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL8_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL9_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL10_FE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-/* BE */
-SND_SOC_DAILINK_DEFS(DL_SRC_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
- DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
- "mt6359-snd-codec-aif1")),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(DPTX_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("DPTX")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
- DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME,
- RT1011_CODEC_DAI),
- COMP_CODEC(RT1011_DEV1_NAME,
- RT1011_CODEC_DAI)),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(PCM1_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL_SRC1_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC1")),
- DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
- "mt6359-snd-codec-aif1"),
- COMP_CODEC("dmic-codec",
- "dmic-hifi")),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(UL_SRC2_BE,
- DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC2")),
- DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
- "mt6359-snd-codec-aif2")),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = {
- /* FE */
- [DAI_LINK_DL2_FE] = {
- .name = "DL2_FE",
- .stream_name = "DL2 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_playback_ops,
- SND_SOC_DAILINK_REG(DL2_FE),
- },
- [DAI_LINK_DL3_FE] = {
- .name = "DL3_FE",
- .stream_name = "DL3 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_playback_ops,
- SND_SOC_DAILINK_REG(DL3_FE),
- },
- [DAI_LINK_DL6_FE] = {
- .name = "DL6_FE",
- .stream_name = "DL6 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_playback_ops,
- SND_SOC_DAILINK_REG(DL6_FE),
- },
- [DAI_LINK_DL7_FE] = {
- .name = "DL7_FE",
- .stream_name = "DL7 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_PRE,
- SND_SOC_DPCM_TRIGGER_PRE,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- SND_SOC_DAILINK_REG(DL7_FE),
- },
- [DAI_LINK_DL8_FE] = {
- .name = "DL8_FE",
- .stream_name = "DL8 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_playback_ops,
- SND_SOC_DAILINK_REG(DL8_FE),
- },
- [DAI_LINK_DL10_FE] = {
- .name = "DL10_FE",
- .stream_name = "DL10 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_hdmitx_dptx_playback_ops,
- SND_SOC_DAILINK_REG(DL10_FE),
- },
- [DAI_LINK_DL11_FE] = {
- .name = "DL11_FE",
- .stream_name = "DL11 Playback",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_playback_ops,
- SND_SOC_DAILINK_REG(DL11_FE),
- },
- [DAI_LINK_UL1_FE] = {
- .name = "UL1_FE",
- .stream_name = "UL1 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_PRE,
- SND_SOC_DPCM_TRIGGER_PRE,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- SND_SOC_DAILINK_REG(UL1_FE),
- },
- [DAI_LINK_UL2_FE] = {
- .name = "UL2_FE",
- .stream_name = "UL2 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL2_FE),
- },
- [DAI_LINK_UL3_FE] = {
- .name = "UL3_FE",
- .stream_name = "UL3 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL3_FE),
- },
- [DAI_LINK_UL4_FE] = {
- .name = "UL4_FE",
- .stream_name = "UL4 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL4_FE),
- },
- [DAI_LINK_UL5_FE] = {
- .name = "UL5_FE",
- .stream_name = "UL5 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL5_FE),
- },
- [DAI_LINK_UL6_FE] = {
- .name = "UL6_FE",
- .stream_name = "UL6 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_PRE,
- SND_SOC_DPCM_TRIGGER_PRE,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- SND_SOC_DAILINK_REG(UL6_FE),
- },
- [DAI_LINK_UL8_FE] = {
- .name = "UL8_FE",
- .stream_name = "UL8 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL8_FE),
- },
- [DAI_LINK_UL9_FE] = {
- .name = "UL9_FE",
- .stream_name = "UL9 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL9_FE),
- },
- [DAI_LINK_UL10_FE] = {
- .name = "UL10_FE",
- .stream_name = "UL10 Capture",
- .trigger = {
- SND_SOC_DPCM_TRIGGER_POST,
- SND_SOC_DPCM_TRIGGER_POST,
- },
- .dynamic = 1,
- .dpcm_capture = 1,
- .ops = &mt8195_capture_ops,
- SND_SOC_DAILINK_REG(UL10_FE),
- },
- /* BE */
- [DAI_LINK_DL_SRC_BE] = {
- .name = "DL_SRC_BE",
- .init = mt8195_mt6359_init,
- .no_pcm = 1,
- .dpcm_playback = 1,
- SND_SOC_DAILINK_REG(DL_SRC_BE),
- },
- [DAI_LINK_DPTX_BE] = {
- .name = "DPTX_BE",
- .no_pcm = 1,
- .dpcm_playback = 1,
- .ops = &mt8195_dptx_ops,
- .be_hw_params_fixup = mt8195_dptx_hw_params_fixup,
- SND_SOC_DAILINK_REG(DPTX_BE),
- },
- [DAI_LINK_ETDM1_IN_BE] = {
- .name = "ETDM1_IN_BE",
- .no_pcm = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_capture = 1,
- SND_SOC_DAILINK_REG(ETDM1_IN_BE),
- },
- [DAI_LINK_ETDM2_IN_BE] = {
- .name = "ETDM2_IN_BE",
- .no_pcm = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_capture = 1,
- .init = mt8195_rt5682_init,
- .ops = &mt8195_rt5682_etdm_ops,
- .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
- SND_SOC_DAILINK_REG(ETDM2_IN_BE),
- },
- [DAI_LINK_ETDM1_OUT_BE] = {
- .name = "ETDM1_OUT_BE",
- .no_pcm = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_playback = 1,
- .ops = &mt8195_rt5682_etdm_ops,
- .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
- SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
- },
- [DAI_LINK_ETDM2_OUT_BE] = {
- .name = "ETDM2_OUT_BE",
- .no_pcm = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_playback = 1,
- .ops = &mt8195_rt1011_etdm_ops,
- .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
- SND_SOC_DAILINK_REG(ETDM2_OUT_BE),
- },
- [DAI_LINK_ETDM3_OUT_BE] = {
- .name = "ETDM3_OUT_BE",
- .no_pcm = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_playback = 1,
- SND_SOC_DAILINK_REG(ETDM3_OUT_BE),
- },
- [DAI_LINK_PCM1_BE] = {
- .name = "PCM1_BE",
- .no_pcm = 1,
- .dai_fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- SND_SOC_DAILINK_REG(PCM1_BE),
- },
- [DAI_LINK_UL_SRC1_BE] = {
- .name = "UL_SRC1_BE",
- .no_pcm = 1,
- .dpcm_capture = 1,
- SND_SOC_DAILINK_REG(UL_SRC1_BE),
- },
- [DAI_LINK_UL_SRC2_BE] = {
- .name = "UL_SRC2_BE",
- .no_pcm = 1,
- .dpcm_capture = 1,
- SND_SOC_DAILINK_REG(UL_SRC2_BE),
- },
-};
-
-static struct snd_soc_codec_conf rt1011_amp_conf[] = {
- {
- .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
- .name_prefix = "Left",
- },
- {
- .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
- .name_prefix = "Right",
- },
-};
-
-static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = {
- .name = "mt8195_r1011_5682",
- .owner = THIS_MODULE,
- .dai_link = mt8195_mt6359_rt1011_rt5682_dai_links,
- .num_links = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_dai_links),
- .controls = mt8195_mt6359_rt1011_rt5682_controls,
- .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_controls),
- .dapm_widgets = mt8195_mt6359_rt1011_rt5682_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_widgets),
- .dapm_routes = mt8195_mt6359_rt1011_rt5682_routes,
- .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes),
- .codec_conf = rt1011_amp_conf,
- .num_configs = ARRAY_SIZE(rt1011_amp_conf),
- .set_bias_level_post = mt8195_set_bias_level_post,
-};
-
-static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &mt8195_mt6359_rt1011_rt5682_soc_card;
- struct snd_soc_dai_link *dai_link;
- struct mt8195_mt6359_rt1011_rt5682_priv *priv;
- struct device_node *platform_node, *dp_node, *hdmi_node;
- int is5682s = 0;
- int ret, i;
-
- card->dev = &pdev->dev;
- ret = snd_soc_of_parse_card_name(card, "model");
- if (ret) {
- dev_err(&pdev->dev, "%s new card name parsing error %d\n",
- __func__, ret);
- return ret;
- }
-
- if (strstr(card->name, "_5682s"))
- is5682s = 1;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- platform_node = of_parse_phandle(pdev->dev.of_node,
- "mediatek,platform", 0);
- if (!platform_node) {
- dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n");
- return -EINVAL;
- }
-
- dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0);
- hdmi_node = of_parse_phandle(pdev->dev.of_node,
- "mediatek,hdmi-codec", 0);
-
- for_each_card_prelinks(card, i, dai_link) {
- if (!dai_link->platforms->name)
- dai_link->platforms->of_node = platform_node;
-
- if (strcmp(dai_link->name, "DPTX_BE") == 0) {
- if (!dp_node) {
- dev_dbg(&pdev->dev, "No property 'dptx-codec'\n");
- } else {
- dai_link->codecs->of_node = dp_node;
- dai_link->codecs->name = NULL;
- dai_link->codecs->dai_name = "i2s-hifi";
- dai_link->init = mt8195_dptx_codec_init;
- }
- } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
- if (!hdmi_node) {
- dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n");
- } else {
- dai_link->codecs->of_node = hdmi_node;
- dai_link->codecs->name = NULL;
- dai_link->codecs->dai_name = "i2s-hifi";
- dai_link->init = mt8195_hdmi_codec_init;
- }
- } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
- strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
- dai_link->codecs->name =
- is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME;
- dai_link->codecs->dai_name =
- is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
- }
- }
-
- snd_soc_card_set_drvdata(card, priv);
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
-
- of_node_put(platform_node);
- of_node_put(dp_node);
- of_node_put(hdmi_node);
- return ret;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id mt8195_mt6359_rt1011_rt5682_dt_match[] = {
- {.compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",},
- {}
-};
-#endif
-
-static const struct dev_pm_ops mt8195_mt6359_rt1011_rt5682_pm_ops = {
- .poweroff = snd_soc_poweroff,
- .restore = snd_soc_resume,
-};
-
-static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = {
- .driver = {
- .name = "mt8195_mt6359_rt1011_rt5682",
-#ifdef CONFIG_OF
- .of_match_table = mt8195_mt6359_rt1011_rt5682_dt_match,
-#endif
- .pm = &mt8195_mt6359_rt1011_rt5682_pm_ops,
- },
- .probe = mt8195_mt6359_rt1011_rt5682_dev_probe,
-};
-
-module_platform_driver(mt8195_mt6359_rt1011_rt5682_driver);
-
-/* Module information */
-MODULE_DESCRIPTION("MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver");
-MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("mt8195_mt6359_rt1011_rt5682 soc card");
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
index e3146311722f..54a00b0699b1 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -1,30 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * mt8195-mt6359-rt1019-rt5682.c --
- * MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver
+ * mt8195-mt6359.c --
+ * MT8195-MT6359 ALSA SoC machine driver code
*
- * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2022 MediaTek Inc.
* Author: Trevor Wu <trevor.wu@mediatek.com>
* YC Hung <yc.hung@mediatek.com>
*/
#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>
-#include <sound/sof.h>
#include <sound/soc.h>
#include "../../codecs/mt6359.h"
+#include "../../codecs/rt1011.h"
#include "../../codecs/rt5682.h"
#include "../common/mtk-afe-platform-driver.h"
#include "mt8195-afe-clk.h"
#include "mt8195-afe-common.h"
+#define RT1011_SPEAKER_AMP_PRESENT BIT(0)
+#define RT1019_SPEAKER_AMP_PRESENT BIT(1)
+#define MAX98390_SPEAKER_AMP_PRESENT BIT(2)
+
+#define RT1011_CODEC_DAI "rt1011-aif"
+#define RT1011_DEV0_NAME "rt1011.2-0038"
+#define RT1011_DEV1_NAME "rt1011.2-0039"
+
#define RT1019_CODEC_DAI "HiFi"
#define RT1019_DEV0_NAME "rt1019p"
+#define MAX98390_CODEC_DAI "max98390-aif1"
+#define MAX98390_DEV0_NAME "max98390.2-0038" /* right */
+#define MAX98390_DEV1_NAME "max98390.2-0039" /* left */
+
#define RT5682_CODEC_DAI "rt5682-aif1"
#define RT5682_DEV0_NAME "rt5682.2-001a"
@@ -36,6 +49,11 @@
#define SOF_DMA_UL4 "SOF_DMA_UL4"
#define SOF_DMA_UL5 "SOF_DMA_UL5"
+struct mt8195_card_data {
+ const char *name;
+ unsigned long quirk;
+};
+
struct sof_conn_stream {
const char *normal_link;
const char *sof_link;
@@ -43,17 +61,15 @@ struct sof_conn_stream {
int stream_dir;
};
-struct mt8195_mt6359_rt1019_rt5682_priv {
+struct mt8195_mt6359_priv {
struct snd_soc_jack headset_jack;
struct snd_soc_jack dp_jack;
struct snd_soc_jack hdmi_jack;
struct clk *i2so1_mclk;
};
-static const struct snd_soc_dapm_widget
- mt8195_mt6359_rt1019_rt5682_widgets[] = {
- SND_SOC_DAPM_SPK("Speakers", NULL),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
+static const struct snd_soc_dapm_widget mt8195_mt6359_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -61,12 +77,10 @@ static const struct snd_soc_dapm_widget
SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = {
- /* speaker */
- { "Speakers", NULL, "Speaker" },
+static const struct snd_soc_dapm_route mt8195_mt6359_routes[] = {
/* headset */
- { "Headphone Jack", NULL, "HPOL" },
- { "Headphone Jack", NULL, "HPOR" },
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
{ "IN1P", NULL, "Headset Mic" },
/* SOF Uplink */
{SOF_DMA_UL4, NULL, "O034"},
@@ -80,55 +94,41 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = {
{"I021", NULL, SOF_DMA_DL3},
};
-static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speakers"),
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+static const struct snd_kcontrol_new mt8195_mt6359_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
-static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- unsigned int rate = params_rate(params);
- int bitwidth;
- int ret;
+static const struct snd_soc_dapm_widget mt8195_dual_speaker_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
- bitwidth = snd_pcm_format_width(params_format(params));
- if (bitwidth < 0) {
- dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
- return bitwidth;
- }
+static const struct snd_kcontrol_new mt8195_dual_speaker_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
- if (ret) {
- dev_err(card->dev, "failed to set tdm slot\n");
- return ret;
- }
+static const struct snd_soc_dapm_widget mt8195_speaker_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
- ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK,
- rate * 256, rate * 512);
- if (ret) {
- dev_err(card->dev, "failed to set pll\n");
- return ret;
- }
+static const struct snd_kcontrol_new mt8195_speaker_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
- rate * 512, SND_SOC_CLOCK_IN);
- if (ret) {
- dev_err(card->dev, "failed to set sysclk\n");
- return ret;
- }
+static const struct snd_soc_dapm_route mt8195_rt1011_routes[] = {
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
- return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
- SND_SOC_CLOCK_OUT);
-}
+static const struct snd_soc_dapm_route mt8195_rt1019_routes[] = {
+ { "Ext Spk", NULL, "Speaker" },
+};
-static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
- .hw_params = mt8195_rt5682_etdm_hw_params,
+static const struct snd_soc_dapm_route mt8195_max98390_routes[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
};
#define CKSYS_AUD_TOP_CFG 0x032c
@@ -143,20 +143,20 @@ static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
struct mt8195_afe_private *afe_priv = afe->platform_priv;
struct mtkaif_param *param = &afe_priv->mtkaif_params;
- int phase;
- unsigned int monitor;
- int mtkaif_calibration_num_phase;
+ int chosen_phase_1, chosen_phase_2, chosen_phase_3;
+ int prev_cycle_1, prev_cycle_2, prev_cycle_3;
int test_done_1, test_done_2, test_done_3;
int cycle_1, cycle_2, cycle_3;
- int prev_cycle_1, prev_cycle_2, prev_cycle_3;
- int chosen_phase_1, chosen_phase_2, chosen_phase_3;
- int counter;
- bool mtkaif_calibration_ok;
int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM];
int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM];
+ int mtkaif_calibration_num_phase;
+ bool mtkaif_calibration_ok;
+ unsigned int monitor;
+ int counter;
+ int phase;
int i;
- dev_info(afe->dev, "%s(), start\n", __func__);
+ dev_dbg(afe->dev, "%s(), start\n", __func__);
param->mtkaif_calibration_ok = false;
for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) {
@@ -312,57 +312,6 @@ static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_component *cmpnt_codec =
- asoc_rtd_to_codec(rtd, 0)->component;
- struct mt8195_mt6359_rt1019_rt5682_priv *priv =
- snd_soc_card_get_drvdata(rtd->card);
- struct snd_soc_jack *jack = &priv->headset_jack;
- struct snd_soc_component *cmpnt_afe =
- snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
- struct mt8195_afe_private *afe_priv = afe->platform_priv;
- int ret;
-
- priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
-
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2 |
- SND_JACK_BTN_3,
- jack, NULL, 0);
- 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(cmpnt_codec, jack, NULL);
- if (ret) {
- dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-};
-
-static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- /* fix BE i2s format to S24_LE, clean param mask first */
- snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
- 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
-
- params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
-
- return 0;
-}
-
static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream)
{
static const unsigned int rates[] = {
@@ -414,11 +363,8 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- unsigned int rate = params_rate(params);
- unsigned int mclk_fs_ratio = 256;
- unsigned int mclk_fs = rate * mclk_fs_ratio;
- return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs,
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256,
SND_SOC_CLOCK_OUT);
}
@@ -428,14 +374,13 @@ static const struct snd_soc_ops mt8195_dptx_ops = {
static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
{
- struct mt8195_mt6359_rt1019_rt5682_priv *priv =
- snd_soc_card_get_drvdata(rtd->card);
+ struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_component *cmpnt_codec =
asoc_rtd_to_codec(rtd, 0)->component;
- int ret = 0;
+ int ret;
ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
- &priv->dp_jack, NULL, 0);
+ &priv->dp_jack);
if (ret)
return ret;
@@ -444,14 +389,13 @@ static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
{
- struct mt8195_mt6359_rt1019_rt5682_priv *priv =
- snd_soc_card_get_drvdata(rtd->card);
+ struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_component *cmpnt_codec =
asoc_rtd_to_codec(rtd, 0)->component;
- int ret = 0;
+ int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
- &priv->hdmi_jack, NULL, 0);
+ &priv->hdmi_jack);
if (ret)
return ret;
@@ -460,7 +404,6 @@ static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
-
{
/* fix BE i2s format to S24_LE, clean param mask first */
snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
@@ -563,12 +506,223 @@ static const struct snd_soc_ops mt8195_capture_ops = {
.startup = mt8195_capture_startup,
};
+static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK,
+ rate * 256, rate * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8195_rt5682_etdm_ops = {
+ .hw_params = mt8195_rt5682_etdm_hw_params,
+};
+
+static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+ struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &priv->headset_jack;
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
+
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack);
+ 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(cmpnt_codec, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static int mt8195_rt1011_etdm_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;
+ struct snd_soc_card *card = rtd->card;
+ int srate, i, ret;
+
+ srate = params_rate(params);
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK,
+ 64 * srate, 256 * srate);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ RT1011_FS_SYS_PRE_S_PLL1,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops mt8195_rt1011_etdm_ops = {
+ .hw_params = mt8195_rt1011_etdm_hw_params,
+};
+
+static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_dual_speaker_widgets,
+ ARRAY_SIZE(mt8195_dual_speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8195_dual_speaker_controls,
+ ARRAY_SIZE(mt8195_dual_speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_rt1011_routes,
+ ARRAY_SIZE(mt8195_rt1011_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int mt8195_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_speaker_widgets,
+ ARRAY_SIZE(mt8195_speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8195_speaker_controls,
+ ARRAY_SIZE(mt8195_speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_rt1019_routes,
+ ARRAY_SIZE(mt8195_rt1019_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int mt8195_max98390_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_dual_speaker_widgets,
+ ARRAY_SIZE(mt8195_dual_speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8195_dual_speaker_controls,
+ ARRAY_SIZE(mt8195_dual_speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_max98390_routes,
+ ARRAY_SIZE(mt8195_max98390_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to S24_LE, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
static int mt8195_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
{
struct snd_soc_component *component = dapm->component;
- struct mt8195_mt6359_rt1019_rt5682_priv *priv =
- snd_soc_card_get_drvdata(card);
+ struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
int ret;
/*
@@ -581,7 +735,6 @@ static int mt8195_set_bias_level_post(struct snd_soc_card *card,
strcmp(component->name, RT5682S_DEV0_NAME)))
return 0;
-
switch (level) {
case SND_SOC_BIAS_OFF:
if (!__clk_is_enabled(priv->i2so1_mclk))
@@ -753,8 +906,7 @@ SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
- DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME,
- RT1019_CODEC_DAI)),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE,
@@ -801,6 +953,23 @@ SND_SOC_DAILINK_DEFS(AFE_SOF_UL5,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
+/* codec */
+SND_SOC_DAILINK_DEF(rt1019_comps,
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME,
+ RT1019_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(rt1011_comps,
+ DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME,
+ RT1011_CODEC_DAI),
+ COMP_CODEC(RT1011_DEV1_NAME,
+ RT1011_CODEC_DAI)));
+
+SND_SOC_DAILINK_DEF(max98390_comps,
+ DAILINK_COMP_ARRAY(COMP_CODEC(MAX98390_DEV0_NAME,
+ MAX98390_CODEC_DAI),
+ COMP_CODEC(MAX98390_DEV1_NAME,
+ MAX98390_CODEC_DAI)));
+
static const struct sof_conn_stream g_sof_conn_streams[] = {
{ "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK},
{ "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK},
@@ -808,128 +977,7 @@ static const struct sof_conn_stream g_sof_conn_streams[] = {
{ "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE},
};
-/* fixup the BE DAI link to match any values from topology */
-static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai_link *sof_dai_link = NULL;
- struct snd_soc_pcm_runtime *runtime;
- struct snd_soc_dai *cpu_dai;
- int i, j, ret = 0;
-
- for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) {
- const struct sof_conn_stream *conn = &g_sof_conn_streams[i];
-
- if (strcmp(rtd->dai_link->name, conn->normal_link))
- continue;
-
- for_each_card_rtds(card, runtime) {
- if (strcmp(runtime->dai_link->name, conn->sof_link))
- continue;
-
- for_each_rtd_cpu_dais(runtime, j, cpu_dai) {
- if (cpu_dai->stream_active[conn->stream_dir] > 0) {
- sof_dai_link = runtime->dai_link;
- break;
- }
- }
- break;
- }
-
- if (sof_dai_link && sof_dai_link->be_hw_params_fixup)
- ret = sof_dai_link->be_hw_params_fixup(runtime, params);
-
- break;
- }
-
- if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") ||
- !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) {
- mt8195_etdm_hw_params_fixup(runtime, params);
- }
-
- return ret;
-}
-
-static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card)
-{
- struct snd_soc_pcm_runtime *runtime;
- struct snd_soc_component *sof_comp = NULL;
- int i;
-
- /* 1. find sof component */
- for_each_card_rtds(card, runtime) {
- for (i = 0; i < runtime->num_components; i++) {
- if (!runtime->components[i]->driver->name)
- continue;
- if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) {
- sof_comp = runtime->components[i];
- break;
- }
- }
- }
-
- if (!sof_comp) {
- dev_info(card->dev, " probe without component\n");
- return 0;
- }
- /* 2. add route path and fixup callback */
- for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) {
- const struct sof_conn_stream *conn = &g_sof_conn_streams[i];
- struct snd_soc_pcm_runtime *sof_rtd = NULL;
- struct snd_soc_pcm_runtime *normal_rtd = NULL;
- struct snd_soc_pcm_runtime *rtd = NULL;
-
- for_each_card_rtds(card, rtd) {
- if (!strcmp(rtd->dai_link->name, conn->sof_link)) {
- sof_rtd = rtd;
- continue;
- }
- if (!strcmp(rtd->dai_link->name, conn->normal_link)) {
- normal_rtd = rtd;
- continue;
- }
- if (normal_rtd && sof_rtd)
- break;
- }
- if (normal_rtd && sof_rtd) {
- int j;
- struct snd_soc_dai *cpu_dai;
-
- for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) {
- struct snd_soc_dapm_route route;
- struct snd_soc_dapm_path *p = NULL;
- struct snd_soc_dapm_widget *play_widget =
- cpu_dai->playback_widget;
- struct snd_soc_dapm_widget *cap_widget =
- cpu_dai->capture_widget;
- memset(&route, 0, sizeof(route));
- if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE &&
- cap_widget) {
- snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) {
- route.source = conn->sof_dma;
- route.sink = p->sink->name;
- snd_soc_dapm_add_routes(&card->dapm, &route, 1);
- }
- } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK &&
- play_widget){
- snd_soc_dapm_widget_for_each_source_path(play_widget, p) {
- route.source = p->source->name;
- route.sink = conn->sof_dma;
- snd_soc_dapm_add_routes(&card->dapm, &route, 1);
- }
- } else {
- dev_err(cpu_dai->dev, "stream dir and widget not pair\n");
- }
- }
- normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup;
- }
- }
-
- return 0;
-}
-
-static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
+static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
/* FE */
[DAI_LINK_DL2_FE] = {
.name = "DL2_FE",
@@ -1234,20 +1282,162 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = {
},
};
-static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = {
- .name = "mt8195_r1019_5682",
+static struct snd_soc_codec_conf rt1011_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME),
+ .name_prefix = "Right",
+ },
+};
+
+static struct snd_soc_codec_conf max98390_codec_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(MAX98390_DEV0_NAME),
+ .name_prefix = "Right",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(MAX98390_DEV1_NAME),
+ .name_prefix = "Left",
+ },
+};
+
+static struct snd_soc_card mt8195_mt6359_soc_card = {
.owner = THIS_MODULE,
- .dai_link = mt8195_mt6359_rt1019_rt5682_dai_links,
- .num_links = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links),
- .controls = mt8195_mt6359_rt1019_rt5682_controls,
- .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_controls),
- .dapm_widgets = mt8195_mt6359_rt1019_rt5682_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_widgets),
- .dapm_routes = mt8195_mt6359_rt1019_rt5682_routes,
- .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes),
+ .dai_link = mt8195_mt6359_dai_links,
+ .num_links = ARRAY_SIZE(mt8195_mt6359_dai_links),
+ .controls = mt8195_mt6359_controls,
+ .num_controls = ARRAY_SIZE(mt8195_mt6359_controls),
+ .dapm_widgets = mt8195_mt6359_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_widgets),
+ .dapm_routes = mt8195_mt6359_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_routes),
.set_bias_level_post = mt8195_set_bias_level_post,
};
+/* fixup the BE DAI link to match any values from topology */
+static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai_link *sof_dai_link = NULL;
+ struct snd_soc_pcm_runtime *runtime;
+ struct snd_soc_dai *cpu_dai;
+ int i, j, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) {
+ const struct sof_conn_stream *conn = &g_sof_conn_streams[i];
+
+ if (strcmp(rtd->dai_link->name, conn->normal_link))
+ continue;
+
+ for_each_card_rtds(card, runtime) {
+ if (strcmp(runtime->dai_link->name, conn->sof_link))
+ continue;
+
+ for_each_rtd_cpu_dais(runtime, j, cpu_dai) {
+ if (cpu_dai->stream_active[conn->stream_dir] > 0) {
+ sof_dai_link = runtime->dai_link;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (sof_dai_link && sof_dai_link->be_hw_params_fixup)
+ ret = sof_dai_link->be_hw_params_fixup(runtime, params);
+
+ break;
+ }
+
+ if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") ||
+ !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) {
+ mt8195_etdm_hw_params_fixup(runtime, params);
+ }
+
+ return ret;
+}
+
+static int mt8195_mt6359_card_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *runtime;
+ struct snd_soc_component *sof_comp = NULL;
+ int i;
+
+ /* 1. find sof component */
+ for_each_card_rtds(card, runtime) {
+ for (i = 0; i < runtime->num_components; i++) {
+ if (!runtime->components[i]->driver->name)
+ continue;
+ if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) {
+ sof_comp = runtime->components[i];
+ break;
+ }
+ }
+ }
+
+ if (!sof_comp) {
+ dev_info(card->dev, " probe without component\n");
+ return 0;
+ }
+ /* 2. add route path and fixup callback */
+ for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) {
+ const struct sof_conn_stream *conn = &g_sof_conn_streams[i];
+ struct snd_soc_pcm_runtime *sof_rtd = NULL;
+ struct snd_soc_pcm_runtime *normal_rtd = NULL;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+
+ for_each_card_rtds(card, rtd) {
+ if (!strcmp(rtd->dai_link->name, conn->sof_link)) {
+ sof_rtd = rtd;
+ continue;
+ }
+ if (!strcmp(rtd->dai_link->name, conn->normal_link)) {
+ normal_rtd = rtd;
+ continue;
+ }
+ if (normal_rtd && sof_rtd)
+ break;
+ }
+ if (normal_rtd && sof_rtd) {
+ int j;
+ struct snd_soc_dai *cpu_dai;
+
+ for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) {
+ struct snd_soc_dapm_route route;
+ struct snd_soc_dapm_path *p = NULL;
+ struct snd_soc_dapm_widget *play_widget =
+ cpu_dai->playback_widget;
+ struct snd_soc_dapm_widget *cap_widget =
+ cpu_dai->capture_widget;
+ memset(&route, 0, sizeof(route));
+ if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE &&
+ cap_widget) {
+ snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) {
+ route.source = conn->sof_dma;
+ route.sink = p->sink->name;
+ snd_soc_dapm_add_routes(&card->dapm, &route, 1);
+ }
+ } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK &&
+ play_widget){
+ snd_soc_dapm_widget_for_each_source_path(play_widget, p) {
+ route.source = p->source->name;
+ route.sink = conn->sof_dma;
+ snd_soc_dapm_add_routes(&card->dapm, &route, 1);
+ }
+ } else {
+ dev_err(cpu_dai->dev, "stream dir and widget not pair\n");
+ }
+ }
+ normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup;
+ }
+ }
+
+ return 0;
+}
+
static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np,
const char *propname)
{
@@ -1258,7 +1448,7 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node
num_links = of_property_count_strings(np, "mediatek,dai-link");
- if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) {
+ if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_dai_links)) {
dev_dbg(dev, "number of dai-link is invalid\n");
return -EINVAL;
}
@@ -1278,9 +1468,9 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node
return -EINVAL;
}
- for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links); j++) {
- if (!strcmp(dai_name, mt8195_mt6359_rt1019_rt5682_dai_links[j].name)) {
- memcpy(link, &mt8195_mt6359_rt1019_rt5682_dai_links[j],
+ for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_dai_links); j++) {
+ if (!strcmp(dai_name, mt8195_mt6359_dai_links[j].name)) {
+ memcpy(link, &mt8195_mt6359_dai_links[j],
sizeof(struct snd_soc_dai_link));
link++;
card->num_links++;
@@ -1295,17 +1485,19 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node
return 0;
}
-static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
+static int mt8195_mt6359_dev_probe(struct platform_device *pdev)
{
- struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card;
+ struct snd_soc_card *card = &mt8195_mt6359_soc_card;
struct snd_soc_dai_link *dai_link;
- struct mt8195_mt6359_rt1019_rt5682_priv *priv;
+ struct mt8195_mt6359_priv *priv;
struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node;
+ struct mt8195_card_data *card_data;
int is5682s = 0;
int init6359 = 0;
int sof_on = 0;
int ret, i;
+ card_data = (struct mt8195_card_data *)of_device_get_match_data(&pdev->dev);
card->dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(card, "model");
@@ -1315,6 +1507,9 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
return ret;
}
+ if (!card->name)
+ card->name = card_data->name;
+
if (strstr(card->name, "_5682s"))
is5682s = 1;
@@ -1322,6 +1517,18 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
+ ret = mt8195_dailink_parse_of(card, pdev->dev.of_node,
+ "mediatek,dai-link");
+ if (ret) {
+ dev_dbg(&pdev->dev, "Parse dai-link fail\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!sof_on)
+ card->num_links = DAI_LINK_REGULAR_NUM;
+ }
+
platform_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,platform", 0);
if (!platform_node) {
@@ -1337,19 +1544,6 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
hdmi_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,hdmi-codec", 0);
- if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
- ret = mt8195_dailink_parse_of(card, pdev->dev.of_node,
- "mediatek,dai-link");
- if (ret) {
- dev_dbg(&pdev->dev, "Parse dai-link fail\n");
- ret = -EINVAL;
- goto put_node;
- }
- } else {
- if (!sof_on)
- card->num_links = DAI_LINK_REGULAR_NUM;
- }
-
for_each_card_prelinks(card, i, dai_link) {
if (!dai_link->platforms->name) {
if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on)
@@ -1389,17 +1583,42 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev)
dai_link->init = mt8195_mt6359_init;
init6359 = 1;
}
+ } else if (strcmp(dai_link->name, "ETDM2_OUT_BE") == 0) {
+ switch (card_data->quirk) {
+ case RT1011_SPEAKER_AMP_PRESENT:
+ dai_link->codecs = rt1011_comps;
+ dai_link->num_codecs = ARRAY_SIZE(rt1011_comps);
+ dai_link->init = mt8195_rt1011_init;
+ dai_link->ops = &mt8195_rt1011_etdm_ops;
+ dai_link->be_hw_params_fixup = mt8195_etdm_hw_params_fixup;
+ card->codec_conf = rt1011_codec_conf;
+ card->num_configs = ARRAY_SIZE(rt1011_codec_conf);
+ break;
+ case RT1019_SPEAKER_AMP_PRESENT:
+ dai_link->codecs = rt1019_comps;
+ dai_link->num_codecs = ARRAY_SIZE(rt1019_comps);
+ dai_link->init = mt8195_rt1019_init;
+ break;
+ case MAX98390_SPEAKER_AMP_PRESENT:
+ dai_link->codecs = max98390_comps;
+ dai_link->num_codecs = ARRAY_SIZE(max98390_comps);
+ dai_link->init = mt8195_max98390_init;
+ card->codec_conf = max98390_codec_conf;
+ card->num_configs = ARRAY_SIZE(max98390_codec_conf);
+ break;
+ default:
+ break;
+ }
}
}
if (sof_on)
- card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe;
+ card->late_probe = mt8195_mt6359_card_late_probe;
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, card);
-put_node:
of_node_put(platform_node);
of_node_put(adsp_node);
of_node_put(dp_node);
@@ -1407,34 +1626,56 @@ put_node:
return ret;
}
-#ifdef CONFIG_OF
-static const struct of_device_id mt8195_mt6359_rt1019_rt5682_dt_match[] = {
- {.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",},
- {}
+static struct mt8195_card_data mt8195_mt6359_rt1019_rt5682_card = {
+ .name = "mt8195_r1019_5682",
+ .quirk = RT1019_SPEAKER_AMP_PRESENT,
+};
+
+static struct mt8195_card_data mt8195_mt6359_rt1011_rt5682_card = {
+ .name = "mt8195_r1011_5682",
+ .quirk = RT1011_SPEAKER_AMP_PRESENT,
+};
+
+static struct mt8195_card_data mt8195_mt6359_max98390_rt5682_card = {
+ .name = "mt8195_m98390_r5682",
+ .quirk = MAX98390_SPEAKER_AMP_PRESENT,
+};
+
+static const struct of_device_id mt8195_mt6359_dt_match[] = {
+ {
+ .compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",
+ .data = &mt8195_mt6359_rt1019_rt5682_card,
+ },
+ {
+ .compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",
+ .data = &mt8195_mt6359_rt1011_rt5682_card,
+ },
+ {
+ .compatible = "mediatek,mt8195_mt6359_max98390_rt5682",
+ .data = &mt8195_mt6359_max98390_rt5682_card,
+ },
+ {},
};
-#endif
-static const struct dev_pm_ops mt8195_mt6359_rt1019_rt5682_pm_ops = {
+static const struct dev_pm_ops mt8195_mt6359_pm_ops = {
.poweroff = snd_soc_poweroff,
.restore = snd_soc_resume,
};
-static struct platform_driver mt8195_mt6359_rt1019_rt5682_driver = {
+static struct platform_driver mt8195_mt6359_driver = {
.driver = {
- .name = "mt8195_mt6359_rt1019_rt5682",
-#ifdef CONFIG_OF
- .of_match_table = mt8195_mt6359_rt1019_rt5682_dt_match,
-#endif
- .pm = &mt8195_mt6359_rt1019_rt5682_pm_ops,
+ .name = "mt8195_mt6359",
+ .of_match_table = mt8195_mt6359_dt_match,
+ .pm = &mt8195_mt6359_pm_ops,
},
- .probe = mt8195_mt6359_rt1019_rt5682_dev_probe,
+ .probe = mt8195_mt6359_dev_probe,
};
-module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver);
+module_platform_driver(mt8195_mt6359_driver);
/* Module information */
-MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver");
+MODULE_DESCRIPTION("MT8195-MT6359 ALSA SoC machine driver");
MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
MODULE_AUTHOR("YC Hung <yc.hung@mediatek.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card");
+MODULE_ALIAS("mt8195_mt6359 soc card");
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 879c1221a809..7afe1a1acc56 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -754,6 +754,7 @@ static int mxs_saif_probe(struct platform_device *pdev)
saif->master_id = saif->id;
} else {
ret = of_alias_get_id(master, "saif");
+ of_node_put(master);
if (ret < 0)
return ret;
else
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index cecbec2a09d7..a045693d5bc2 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -45,7 +45,7 @@ config SND_PXA2XX_SOC_CORGI
tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C
select SND_PXA2XX_SOC_I2S
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Say Y if you want to add support for SoC audio on Sharp
Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
@@ -71,7 +71,7 @@ config SND_PXA2XX_SOC_POODLE
tristate "SoC Audio support for Poodle"
depends on SND_PXA2XX_SOC && MACH_POODLE && I2C
select SND_PXA2XX_SOC_I2S
- select SND_SOC_WM8731
+ select SND_SOC_WM8731_I2C
help
Say Y if you want to add support for SoC audio on Sharp
Zaurus SL-5600 model (Poodle).
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
index 7334fac758de..9a816156f012 100644
--- a/sound/soc/pxa/hx4700.c
+++ b/sound/soc/pxa/hx4700.c
@@ -122,9 +122,9 @@ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
int err;
/* Jack detection API stuff */
- err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin,
- ARRAY_SIZE(hs_jack_pin));
+ err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack,
+ hs_jack_pin, ARRAY_SIZE(hs_jack_pin));
if (err)
return err;
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index b92ea1a0453f..65257f7fe4c4 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -71,9 +71,10 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
int err;
/* Jack detection API stuff */
- err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
+ err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (err)
return err;
diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c
index d5f2961b1a3e..6cc970bb2aac 100644
--- a/sound/soc/pxa/ttc-dkb.c
+++ b/sound/soc/pxa/ttc-dkb.c
@@ -64,12 +64,14 @@ static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
/* Headset jack detection */
- snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
- &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
- snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE,
- &mic_jack, mic_jack_pins,
- ARRAY_SIZE(mic_jack_pins));
+ snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &hs_jack,
+ hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+ snd_soc_card_jack_new_pins(rtd->card, "Microphone Jack",
+ SND_JACK_MICROPHONE, &mic_jack,
+ mic_jack_pins, ARRAY_SIZE(mic_jack_pins));
/* headphone, microphone detection & headset short detection */
pm860x_hs_jack_detect(component, &hs_jack, SND_JACK_HEADPHONE,
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c
index edf2b9eec5b8..f4a7cfe22115 100644
--- a/sound/soc/pxa/z2.c
+++ b/sound/soc/pxa/z2.c
@@ -132,9 +132,10 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
int ret;
/* Jack detection API stuff */
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
- &hs_jack, hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret)
goto err;
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 28d0dfb4033c..750653404ba3 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -197,6 +197,8 @@ config SND_SOC_SC7280
select SND_SOC_LPASS_MACRO_COMMON
imply SND_SOC_LPASS_RX_MACRO
imply SND_SOC_LPASS_TX_MACRO
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
help
Add support for audio on Qualcomm Technologies Inc.
SC7280 SoC-based systems.
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index f9d69375320e..b0a4f7ca2751 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -96,7 +96,7 @@ static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4,
- &pdata->jack, NULL, 0);
+ &pdata->jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add Headphone Jack\n");
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 74d62f377dfd..f03a7ae49d50 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -1160,7 +1160,7 @@ static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *comp
break;
}
- buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WT);
+ buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WC);
return 0;
}
diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c
index 37225ef2563a..efccb5c0b3e0 100644
--- a/sound/soc/qcom/sc7180.c
+++ b/sound/soc/qcom/sc7180.c
@@ -57,7 +57,7 @@ static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pdata->hs_jack, NULL, 0);
+ &pdata->hs_jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add Headset Jack\n");
@@ -89,7 +89,7 @@ static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
rval = snd_soc_card_jack_new(
card, "HDMI Jack",
SND_JACK_LINEOUT,
- &pdata->hdmi_jack, NULL, 0);
+ &pdata->hdmi_jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add HDMI Jack\n");
diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c
index bd0bf9c8cb28..34cdb99d4ed6 100644
--- a/sound/soc/qcom/sc7280.c
+++ b/sound/soc/qcom/sc7280.c
@@ -12,14 +12,21 @@
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/soc.h>
+#include <sound/rt5682s.h>
#include <linux/soundwire/sdw.h>
+#include "../codecs/rt5682.h"
+#include "../codecs/rt5682s.h"
#include "common.h"
#include "lpass.h"
+#define DEFAULT_MCLK_RATE 19200000
+#define RT5682_PLL_FREQ (48000 * 512)
+
struct sc7280_snd_data {
struct snd_soc_card card;
struct sdw_stream_runtime *sruntime[LPASS_MAX_PORTS];
+ u32 pri_mi2s_clk_count;
struct snd_soc_jack hs_jack;
struct snd_soc_jack hdmi_jack;
bool jack_setup;
@@ -50,7 +57,7 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &pdata->hs_jack, NULL, 0);
+ &pdata->hs_jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add Headset Jack\n");
@@ -69,6 +76,7 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd)
pdata->jack_setup = true;
}
switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
case LPASS_CDC_DMA_RX0:
case LPASS_CDC_DMA_TX3:
for_each_rtd_codec_dais(rtd, i, codec_dai) {
@@ -96,7 +104,7 @@ static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd)
int rval;
rval = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
- &pdata->hdmi_jack, NULL, 0);
+ &pdata->hdmi_jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add HDMI Jack\n");
@@ -110,11 +118,51 @@ static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd)
return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
}
+static int sc7280_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ 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 snd_soc_card *card = rtd->card;
+ struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ if (++data->pri_mi2s_clk_count == 1) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ LPASS_MCLK0,
+ DEFAULT_MCLK_RATE,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBC_CFC |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_I2S);
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ DEFAULT_MCLK_RATE, RT5682_PLL_FREQ);
+ if (ret) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ,
+ SND_SOC_CLOCK_IN);
+
+ if (ret) {
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int sc7280_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
case LPASS_CDC_DMA_TX3:
return sc7280_headset_init(rtd);
case LPASS_CDC_DMA_RX0:
@@ -227,10 +275,54 @@ static int sc7280_snd_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+static void sc7280_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ if (--data->pri_mi2s_clk_count == 0) {
+ snd_soc_dai_set_sysclk(cpu_dai,
+ LPASS_MCLK0,
+ 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int sc7280_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int ret = 0;
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ ret = sc7280_rt5682_init(rtd);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
static const struct snd_soc_ops sc7280_ops = {
+ .startup = sc7280_snd_startup,
.hw_params = sc7280_snd_hw_params,
.hw_free = sc7280_snd_hw_free,
.prepare = sc7280_snd_prepare,
+ .shutdown = sc7280_snd_shutdown,
+};
+
+static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
static int sc7280_snd_platform_probe(struct platform_device *pdev)
@@ -252,6 +344,9 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev)
card->driver_name = "SC7280";
card->dev = dev;
+ card->dapm_widgets = sc7280_snd_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sc7280_snd_widgets);
+
ret = qcom_snd_parse_of(card);
if (ret)
return ret;
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 5c1d13eccbee..61fda790f375 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -247,7 +247,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pdata->jack, NULL, 0);
+ &pdata->jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add Headphone Jack\n");
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
index 114a29e01c0f..6e1184c8b672 100644
--- a/sound/soc/qcom/sm8250.c
+++ b/sound/soc/qcom/sm8250.c
@@ -41,7 +41,7 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &data->jack, NULL, 0);
+ &data->jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add Headphone Jack\n");
diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c
index b052642ea620..bcdeddeba80c 100644
--- a/sound/soc/rockchip/rk3288_hdmi_analog.c
+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c
@@ -124,10 +124,10 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime)
/* Enable Headset Jack detection */
if (gpio_is_valid(machine->gpio_hp_det)) {
- snd_soc_card_jack_new(runtime->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &headphone_jack,
- headphone_jack_pins,
- ARRAY_SIZE(headphone_jack_pins));
+ snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &headphone_jack,
+ headphone_jack_pins,
+ ARRAY_SIZE(headphone_jack_pins));
rk_hp_jack_gpio.gpio = machine->gpio_hp_det;
snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
}
diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c
index eeef3ed70037..2540b9ba37c8 100644
--- a/sound/soc/rockchip/rk3399_gru_sound.c
+++ b/sound/soc/rockchip/rk3399_gru_sound.c
@@ -174,7 +174,7 @@ static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd)
/* Enable jack detection. */
ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT,
- &cdn_dp_card_jack, NULL, 0);
+ &cdn_dp_card_jack);
if (ret) {
dev_err(card->dev, "Can't create DP Jack %d\n", ret);
return ret;
@@ -204,13 +204,13 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
}
/* Enable Headset and 4 Buttons Jack detection */
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_LINEOUT |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &rockchip_sound_jack,
- rockchip_sound_jack_pins,
- ARRAY_SIZE(rockchip_sound_jack_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rockchip_sound_jack,
+ rockchip_sound_jack_pins,
+ ARRAY_SIZE(rockchip_sound_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret);
diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c
index c8f1a28a92b7..150ac524a590 100644
--- a/sound/soc/rockchip/rockchip_max98090.c
+++ b/sound/soc/rockchip/rockchip_max98090.c
@@ -231,7 +231,7 @@ static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime)
/* enable jack detection */
ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
- &rk_hdmi_jack, NULL, 0);
+ &rk_hdmi_jack);
if (ret) {
dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
return ret;
@@ -345,13 +345,13 @@ static int rk_98090_headset_init(struct snd_soc_component *component)
int ret;
/* Enable Headset and 4 Buttons Jack detection */
- ret = snd_soc_card_jack_new(component->card, "Headset Jack",
- SND_JACK_HEADSET |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &headset_jack,
- headset_jack_pins,
- ARRAY_SIZE(headset_jack_pins));
+ ret = snd_soc_card_jack_new_pins(component->card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
if (ret)
return ret;
diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c
index 16ca2ad92426..d07cc5c813f2 100644
--- a/sound/soc/rockchip/rockchip_rt5645.c
+++ b/sound/soc/rockchip/rockchip_rt5645.c
@@ -107,7 +107,7 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime)
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &headset_jack, NULL, 0);
+ &headset_jack);
if (ret) {
dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
return ret;
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
index 5265e546b124..bb0cf4244e00 100644
--- a/sound/soc/samsung/aries_wm8994.c
+++ b/sound/soc/samsung/aries_wm8994.c
@@ -343,7 +343,7 @@ static int aries_late_probe(struct snd_soc_card *card)
struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
int ret, irq;
- ret = snd_soc_card_jack_new(card, "Dock", SND_JACK_LINEOUT,
+ ret = snd_soc_card_jack_new_pins(card, "Dock", SND_JACK_LINEOUT,
&aries_dock, dock_pins, ARRAY_SIZE(dock_pins));
if (ret)
return ret;
@@ -361,7 +361,7 @@ static int aries_late_probe(struct snd_soc_card *card)
else
snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
- ret = snd_soc_card_jack_new(card, "Headset",
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&aries_headset,
jack_pins, ARRAY_SIZE(jack_pins));
@@ -585,10 +585,10 @@ static int aries_audio_probe(struct platform_device *pdev)
extcon_np = of_parse_phandle(np, "extcon", 0);
priv->usb_extcon = extcon_find_edev_by_node(extcon_np);
+ of_node_put(extcon_np);
if (IS_ERR(priv->usb_extcon))
return dev_err_probe(dev, PTR_ERR(priv->usb_extcon),
"Failed to get extcon device");
- of_node_put(extcon_np);
priv->adc = devm_iio_channel_get(dev, "headset-detect");
if (IS_ERR(priv->adc))
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index 8b83f39c3ac9..76998a4a4cad 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -386,11 +386,11 @@ static struct snd_soc_codec_conf bells_codec_conf[] = {
},
};
-static struct snd_soc_dapm_widget bells_widgets[] = {
+static const struct snd_soc_dapm_widget bells_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
-static struct snd_soc_dapm_route bells_routes[] = {
+static const struct snd_soc_dapm_route bells_routes[] = {
{ "Sub CLK_SYS", NULL, "OPCLK" },
{ "CLKIN", NULL, "OPCLK" },
diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c
index c994e67d1eaf..907266aee839 100644
--- a/sound/soc/samsung/h1940_uda1380.c
+++ b/sound/soc/samsung/h1940_uda1380.c
@@ -151,7 +151,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+ snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE,
&hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index 34067cc314ff..411e25cec591 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -228,7 +228,7 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("WM1250 Output"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
@@ -239,7 +239,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "Headphone", NULL, "HPOUT1L" },
{ "Headphone", NULL, "HPOUT1R" },
@@ -285,7 +285,7 @@ static int littlemill_late_probe(struct snd_soc_card *card)
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &littlemill_headset, NULL, 0);
+ &littlemill_headset);
if (ret)
return ret;
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 7b12ccd2a9b2..b44f5e92224f 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -51,10 +51,11 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &lowland_headset, lowland_headset_pins,
- ARRAY_SIZE(lowland_headset_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
+ SND_JACK_LINEOUT | SND_JACK_HEADSET |
+ SND_JACK_BTN_0,
+ &lowland_headset, lowland_headset_pins,
+ ARRAY_SIZE(lowland_headset_pins));
if (ret)
return ret;
@@ -140,7 +141,7 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -150,7 +151,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_MIC("Main DMIC", NULL),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "Sub IN1", NULL, "HPOUT2L" },
{ "Sub IN2", NULL, "HPOUT2R" },
diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c
index 5e9dc18687cc..6931b9a45b3e 100644
--- a/sound/soc/samsung/midas_wm1811.c
+++ b/sound/soc/samsung/midas_wm1811.c
@@ -309,7 +309,7 @@ static int midas_late_probe(struct snd_soc_card *card)
SND_JACK_HEADSET | SND_JACK_MECHANICAL |
SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &priv->headset_jack, NULL, 0);
+ &priv->headset_jack);
if (ret)
return ret;
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
index 6ea1c8cc9167..ff3acc94a454 100644
--- a/sound/soc/samsung/rx1950_uda1380.c
+++ b/sound/soc/samsung/rx1950_uda1380.c
@@ -201,7 +201,8 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream,
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
{
- snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+ snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE,
&hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
index cee39ad16667..29bf917242fe 100644
--- a/sound/soc/samsung/smartq_wm8987.c
+++ b/sound/soc/samsung/smartq_wm8987.c
@@ -139,10 +139,10 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_nc_pin(dapm, "ROUT1");
/* Headphone jack detection */
- err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
- SND_JACK_HEADPHONE, &smartq_jack,
- smartq_jack_pins,
- ARRAY_SIZE(smartq_jack_pins));
+ err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &smartq_jack,
+ smartq_jack_pins,
+ ARRAY_SIZE(smartq_jack_pins));
if (err)
return err;
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 226c359892e9..47b6d19e43ff 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -467,8 +467,7 @@ static int spdif_remove(struct platform_device *pdev)
iounmap(spdif->regs);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mem_res)
- release_mem_region(mem_res->start, resource_size(mem_res));
+ release_mem_region(mem_res->start, resource_size(mem_res));
clk_disable_unprepare(spdif->sclk);
clk_disable_unprepare(spdif->pclk);
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index 37b1f4f60b21..69d7b0115b38 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -156,10 +156,12 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
- ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &speyside_headset, speyside_headset_pins,
- ARRAY_SIZE(speyside_headset_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
+ SND_JACK_LINEOUT | SND_JACK_HEADSET |
+ SND_JACK_BTN_0,
+ &speyside_headset,
+ speyside_headset_pins,
+ ARRAY_SIZE(speyside_headset_pins));
if (ret)
return ret;
@@ -261,7 +263,7 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -271,7 +273,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_MIC("Main DMIC", NULL),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "IN1RN", NULL, "MICB1" },
{ "IN1RP", NULL, "MICB1" },
{ "IN1RN", NULL, "MICB2" },
diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c
index 8d3149a47a4c..9287a1d0eef1 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -130,7 +130,7 @@ static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("DMIC"),
};
-static struct snd_soc_dapm_widget widgets[] = {
+static const struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -140,7 +140,7 @@ static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_SPK("Main Speaker", NULL),
};
-static struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route audio_paths[] = {
{ "Headphone", NULL, "HPOUTL" },
{ "Headphone", NULL, "HPOUTR" },
@@ -189,10 +189,10 @@ static int tobermory_late_probe(struct snd_soc_card *card)
if (ret < 0)
return ret;
- ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET |
- SND_JACK_BTN_0, &tobermory_headset,
- tobermory_headset_pins,
- ARRAY_SIZE(tobermory_headset_pins));
+ ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET |
+ SND_JACK_BTN_0, &tobermory_headset,
+ tobermory_headset_pins,
+ ARRAY_SIZE(tobermory_headset_pins));
if (ret)
return ret;
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index ae46f187cc2a..97916e3ca9dd 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -47,7 +47,7 @@ config SND_SOC_RCAR
config SND_SOC_RZ
tristate "RZ/G2L series SSIF-2 support"
- depends on ARCH_R9A07G044 || COMPILE_TEST
+ depends on ARCH_RZG2L || COMPILE_TEST
help
This option enables RZ/G2L SSIF-2 sound support.
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 6a8fe0da7670..eb762ab94d3e 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -755,7 +755,7 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
/* set clock master for audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBP_CFP:
rdai->clk_master = 0;
break;
@@ -1159,6 +1159,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
struct device_node *capture)
{
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np;
int i;
@@ -1169,7 +1170,11 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
for_each_child_of_node(node, np) {
struct rsnd_mod *mod;
- i = rsnd_node_fixed_index(np, name, i);
+ i = rsnd_node_fixed_index(dev, np, name, i);
+ if (i < 0) {
+ of_node_put(np);
+ break;
+ }
mod = mod_get(priv, i);
@@ -1183,7 +1188,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
of_node_put(node);
}
-int rsnd_node_fixed_index(struct device_node *node, char *name, int idx)
+int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx)
{
char node_name[16];
@@ -1210,6 +1215,8 @@ int rsnd_node_fixed_index(struct device_node *node, char *name, int idx)
return idx;
}
+ dev_err(dev, "strange node numbering (%s)",
+ of_node_full_name(node));
return -EINVAL;
}
@@ -1221,10 +1228,8 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name
i = 0;
for_each_child_of_node(node, np) {
- i = rsnd_node_fixed_index(np, name, i);
+ i = rsnd_node_fixed_index(dev, np, name, i);
if (i < 0) {
- dev_err(dev, "strange node numbering (%s)",
- of_node_full_name(node));
of_node_put(np);
return 0;
}
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 03e0d4eca781..463ab237d7bd 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -240,12 +240,19 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
struct rsnd_mod *mod, char *x)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
struct dma_chan *chan = NULL;
struct device_node *np;
int i = 0;
for_each_child_of_node(of_node, np) {
- i = rsnd_node_fixed_index(np, name, i);
+ i = rsnd_node_fixed_index(dev, np, name, i);
+ if (i < 0) {
+ chan = NULL;
+ of_node_put(np);
+ break;
+ }
if (i == rsnd_mod_id_raw(mod) && (!chan))
chan = of_dma_request_slave_channel(np, x);
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 6580bab0e229..d9cd190d7e19 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -460,7 +460,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
struct device_node *playback,
struct device_node *capture);
int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name);
-int rsnd_node_fixed_index(struct device_node *node, char *name, int idx);
+int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx);
int rsnd_channel_normalization(int chan);
#define rsnd_runtime_channel_original(io) \
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 42a100c6303d..0ea84ae57c6a 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -676,7 +676,12 @@ int rsnd_src_probe(struct rsnd_priv *priv)
if (!of_device_is_available(np))
goto skip;
- i = rsnd_node_fixed_index(np, SRC_NAME, i);
+ i = rsnd_node_fixed_index(dev, np, SRC_NAME, i);
+ if (i < 0) {
+ ret = -EINVAL;
+ of_node_put(np);
+ goto rsnd_src_probe_done;
+ }
src = rsnd_src_get(priv, i);
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 87e606f688d3..43c5e27dc5c8 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -1105,6 +1105,7 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
struct device_node *capture)
{
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *node;
struct device_node *np;
int i;
@@ -1117,7 +1118,11 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
for_each_child_of_node(node, np) {
struct rsnd_mod *mod;
- i = rsnd_node_fixed_index(np, SSI_NAME, i);
+ i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
+ if (i < 0) {
+ of_node_put(np);
+ break;
+ }
mod = rsnd_ssi_mod_get(priv, i);
@@ -1182,7 +1187,12 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
if (!of_device_is_available(np))
goto skip;
- i = rsnd_node_fixed_index(np, SSI_NAME, i);
+ i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
+ if (i < 0) {
+ ret = -EINVAL;
+ of_node_put(np);
+ goto rsnd_ssi_probe_done;
+ }
ssi = rsnd_ssi_get(priv, i);
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 0d8f97633dd2..4b8a63e336c7 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -102,6 +102,8 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
shift = 1;
offset = 1;
break;
+ default:
+ goto out;
}
for (i = 0; i < 4; i++) {
@@ -120,7 +122,7 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
}
rsnd_mod_write(mod, reg, val);
}
-
+out:
return error;
}
@@ -460,6 +462,7 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
struct device_node *capture)
{
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *node = rsnd_ssiu_of_node(priv);
struct rsnd_dai_stream *io_p = &rdai->playback;
struct rsnd_dai_stream *io_c = &rdai->capture;
@@ -472,7 +475,11 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
for_each_child_of_node(node, np) {
struct rsnd_mod *mod;
- i = rsnd_node_fixed_index(np, SSIU_NAME, i);
+ i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
+ if (i < 0) {
+ of_node_put(np);
+ break;
+ }
mod = rsnd_ssiu_mod_get(priv, i);
diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c
index e8edaed05d4c..e392de7a262e 100644
--- a/sound/soc/sh/rz-ssi.c
+++ b/sound/soc/sh/rz-ssi.c
@@ -59,9 +59,7 @@
#define SSIFSR_RDC_MASK 0x3f
#define SSIFSR_RDC_SHIFT 8
-#define SSIFSR_TDC(x) (((x) & 0x1f) << 24)
#define SSIFSR_TDE BIT(16)
-#define SSIFSR_RDC(x) (((x) & 0x1f) << 8)
#define SSIFSR_RDF BIT(0)
#define SSIOFR_LRCONT BIT(8)
@@ -978,22 +976,24 @@ static int rz_ssi_probe(struct platform_device *pdev)
/* Error Interrupt */
ssi->irq_int = platform_get_irq_byname(pdev, "int_req");
- if (ssi->irq_int < 0)
- return dev_err_probe(&pdev->dev, -ENODEV,
- "Unable to get SSI int_req IRQ\n");
+ if (ssi->irq_int < 0) {
+ rz_ssi_release_dma_channels(ssi);
+ return ssi->irq_int;
+ }
ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt,
0, dev_name(&pdev->dev), ssi);
- if (ret < 0)
+ if (ret < 0) {
+ rz_ssi_release_dma_channels(ssi);
return dev_err_probe(&pdev->dev, ret,
"irq request error (int_req)\n");
+ }
if (!rz_ssi_is_dma_enabled(ssi)) {
/* Tx and Rx interrupts (pio only) */
ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx");
if (ssi->irq_tx < 0)
- return dev_err_probe(&pdev->dev, -ENODEV,
- "Unable to get SSI dma_tx IRQ\n");
+ return ssi->irq_tx;
ret = devm_request_irq(&pdev->dev, ssi->irq_tx,
&rz_ssi_interrupt, 0,
@@ -1004,8 +1004,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx");
if (ssi->irq_rx < 0)
- return dev_err_probe(&pdev->dev, -ENODEV,
- "Unable to get SSI dma_rx IRQ\n");
+ return ssi->irq_rx;
ret = devm_request_irq(&pdev->dev, ssi->irq_rx,
&rz_ssi_interrupt, 0,
@@ -1016,13 +1015,16 @@ static int rz_ssi_probe(struct platform_device *pdev)
}
ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
- if (IS_ERR(ssi->rstc))
+ if (IS_ERR(ssi->rstc)) {
+ rz_ssi_release_dma_channels(ssi);
return PTR_ERR(ssi->rstc);
+ }
reset_control_deassert(ssi->rstc);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0) {
+ rz_ssi_release_dma_channels(ssi);
pm_runtime_disable(ssi->dev);
reset_control_assert(ssi->rstc);
return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n");
diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c
index 41c586b86dc3..4158f5aacfd3 100644
--- a/sound/soc/soc-card.c
+++ b/sound/soc/soc-card.c
@@ -42,8 +42,42 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
}
EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
+static int jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack, bool initial_kctl)
+{
+ mutex_init(&jack->mutex);
+ jack->card = card;
+ INIT_LIST_HEAD(&jack->pins);
+ INIT_LIST_HEAD(&jack->jack_zones);
+ BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
+
+ return snd_jack_new(card->snd_card, id, type, &jack->jack, initial_kctl, false);
+}
+
/**
- * snd_soc_card_jack_new - Create a new jack
+ * snd_soc_card_jack_new - Create a new jack without pins
+ * @card: ASoC card
+ * @id: an identifying string for this jack
+ * @type: a bitmask of enum snd_jack_type values that can be detected by
+ * this jack
+ * @jack: structure to use for the jack
+ *
+ * Creates a new jack object without pins. If adding pins later,
+ * snd_soc_card_jack_new_pins() should be used instead with 0 as num_pins
+ * argument.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jack will be initialised.
+ */
+int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack)
+{
+ return soc_card_ret(card, jack_new(card, id, type, jack, true));
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
+
+/**
+ * snd_soc_card_jack_new_pins - Create a new jack with pins
* @card: ASoC card
* @id: an identifying string for this jack
* @type: a bitmask of enum snd_jack_type values that can be detected by
@@ -52,24 +86,20 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
* @pins: Array of jack pins to be added to the jack or NULL
* @num_pins: Number of elements in the @pins array
*
- * Creates a new jack object.
+ * Creates a new jack object with pins. If not adding pins,
+ * snd_soc_card_jack_new() should be used instead.
*
* Returns zero if successful, or a negative error code on failure.
* On success jack will be initialised.
*/
-int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
- struct snd_soc_jack *jack,
- struct snd_soc_jack_pin *pins, unsigned int num_pins)
+int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id,
+ int type, struct snd_soc_jack *jack,
+ struct snd_soc_jack_pin *pins,
+ unsigned int num_pins)
{
int ret;
- mutex_init(&jack->mutex);
- jack->card = card;
- INIT_LIST_HEAD(&jack->pins);
- INIT_LIST_HEAD(&jack->jack_zones);
- BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
-
- ret = snd_jack_new(card->snd_card, id, type, &jack->jack, false, false);
+ ret = jack_new(card, id, type, jack, false);
if (ret)
goto end;
@@ -78,7 +108,7 @@ int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
end:
return soc_card_ret(card, ret);
}
-EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new_pins);
int snd_soc_card_suspend_pre(struct snd_soc_card *card)
{
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index c0664f94990c..e12f8244242b 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -932,6 +932,20 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
return 0;
}
+static bool snd_soc_component_is_codec_on_rtd(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_component *component)
+{
+ struct snd_soc_dai *dai;
+ int i;
+
+ for_each_rtd_codec_dais(rtd, i, dai) {
+ if (dai->component == component)
+ return true;
+ }
+
+ return false;
+}
+
void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
snd_pcm_sframes_t *cpu_delay,
snd_pcm_sframes_t *codec_delay)
@@ -953,7 +967,7 @@ void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream,
delay = component->driver->delay(component, substream);
- if (snd_soc_component_is_codec(component))
+ if (snd_soc_component_is_codec_on_rtd(rtd, component))
*codec_delay = max(*codec_delay, delay);
else
*cpu_delay = max(*cpu_delay, delay);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 8c7da82a62ca..9574f86dd4de 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1230,7 +1230,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
/*
* Flip the polarity for the "CPU" end of a CODEC<->CODEC link
*/
- inv_dai_fmt = snd_soc_daifmt_clock_provider_fliped(dai_fmt);
+ inv_dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt);
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
unsigned int fmt = dai_fmt;
@@ -2497,7 +2497,7 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
for (i = 0; i < count; i++) {
dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&
- !snd_soc_component_is_codec(component));
+ !component->driver->non_legacy_dai_naming);
if (dai == NULL) {
ret = -ENOMEM;
goto err;
@@ -2763,6 +2763,11 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
"ASoC: Property '%s' does not exist\n", propname);
return -EINVAL;
}
+ if (!num_widgets) {
+ dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
+ propname);
+ return -EINVAL;
+ }
if (num_widgets & 1) {
dev_err(card->dev,
"ASoC: Property '%s' length is not even\n", propname);
@@ -2770,11 +2775,6 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
}
num_widgets /= 2;
- if (!num_widgets) {
- dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
- propname);
- return -EINVAL;
- }
widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets),
GFP_KERNEL);
@@ -3035,7 +3035,7 @@ int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname)
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_aux_devs);
-unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt)
+unsigned int snd_soc_daifmt_clock_provider_flipped(unsigned int dai_fmt)
{
unsigned int inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
@@ -3056,7 +3056,7 @@ unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt)
return inv_dai_fmt;
}
-EXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_fliped);
+EXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_flipped);
unsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame)
{
@@ -3395,6 +3395,86 @@ err:
}
EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
+/*
+ * snd_soc_of_put_dai_link_cpus - Dereference device nodes in the codecs array
+ * @dai_link: DAI link
+ *
+ * Dereference device nodes acquired by snd_soc_of_get_dai_link_cpus().
+ */
+void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link)
+{
+ struct snd_soc_dai_link_component *component;
+ int index;
+
+ for_each_link_cpus(dai_link, index, component) {
+ if (!component->of_node)
+ break;
+ of_node_put(component->of_node);
+ component->of_node = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_cpus);
+
+/*
+ * snd_soc_of_get_dai_link_cpus - Parse a list of CPU DAIs in the devicetree
+ * @dev: Card device
+ * @of_node: Device node
+ * @dai_link: DAI link
+ *
+ * Is analogous to snd_soc_of_get_dai_link_codecs but parses a list of CPU DAIs
+ * instead.
+ *
+ * Returns 0 for success
+ */
+int snd_soc_of_get_dai_link_cpus(struct device *dev,
+ struct device_node *of_node,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct of_phandle_args args;
+ struct snd_soc_dai_link_component *component;
+ char *name;
+ int index, num_codecs, ret;
+
+ /* Count the number of CODECs */
+ name = "sound-dai";
+ num_codecs = of_count_phandle_with_args(of_node, name,
+ "#sound-dai-cells");
+ if (num_codecs <= 0) {
+ if (num_codecs == -ENOENT)
+ dev_err(dev, "No 'sound-dai' property\n");
+ else
+ dev_err(dev, "Bad phandle in 'sound-dai'\n");
+ return num_codecs;
+ }
+ component = devm_kcalloc(dev,
+ num_codecs, sizeof(*component),
+ GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+ dai_link->cpus = component;
+ dai_link->num_cpus = num_codecs;
+
+ /* Parse the list */
+ for_each_link_cpus(dai_link, index, component) {
+ ret = of_parse_phandle_with_args(of_node, name,
+ "#sound-dai-cells",
+ index, &args);
+ if (ret)
+ goto err;
+ component->of_node = args.np;
+ ret = snd_soc_get_dai_name(&args, &component->dai_name);
+ if (ret < 0)
+ goto err;
+ }
+ return 0;
+err:
+ snd_soc_of_put_dai_link_codecs(dai_link);
+ dai_link->cpus = NULL;
+ dai_link->num_cpus = 0;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus);
+
static int __init snd_soc_init(void)
{
snd_soc_debugfs_init();
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index ca917a849c42..869c76506b66 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3437,7 +3437,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
update.val = val;
card->update = &update;
}
- change |= reg_change;
ret = soc_dapm_mixer_update_power(card, kcontrol, connect,
rconnect);
@@ -3539,7 +3538,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
card->update = &update;
}
- change |= reg_change;
ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 285441d6aeed..87858462bba9 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -79,29 +79,19 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component,
{
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
- int (*prepare_slave_config)(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct dma_slave_config *slave_config);
struct dma_slave_config slave_config;
+ int ret;
- memset(&slave_config, 0, sizeof(slave_config));
-
- if (!pcm->config)
- prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
- else
- prepare_slave_config = pcm->config->prepare_slave_config;
+ if (!pcm->config->prepare_slave_config)
+ return 0;
- if (prepare_slave_config) {
- int ret = prepare_slave_config(substream, params, &slave_config);
- if (ret)
- return ret;
+ memset(&slave_config, 0, sizeof(slave_config));
- ret = dmaengine_slave_config(chan, &slave_config);
- if (ret)
- return ret;
- }
+ ret = pcm->config->prepare_slave_config(substream, params, &slave_config);
+ if (ret)
+ return ret;
- return 0;
+ return dmaengine_slave_config(chan, &slave_config);
}
static int
@@ -121,7 +111,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
return -EINVAL;
}
- if (pcm->config && pcm->config->pcm_hardware)
+ if (pcm->config->pcm_hardware)
return snd_soc_set_runtime_hwparams(substream,
pcm->config->pcm_hardware);
@@ -188,7 +178,6 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
{
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
struct snd_dmaengine_dai_dma_data *dma_data;
- dma_filter_fn fn = NULL;
if (rtd->num_cpus > 1) {
dev_err(rtd->dev,
@@ -201,13 +190,11 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
return pcm->chan[0];
- if (pcm->config && pcm->config->compat_request_channel)
+ if (pcm->config->compat_request_channel)
return pcm->config->compat_request_channel(rtd, substream);
- if (pcm->config)
- fn = pcm->config->compat_filter_fn;
-
- return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
+ return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn,
+ dma_data->filter_data);
}
static bool dmaengine_pcm_can_report_residue(struct device *dev,
@@ -239,12 +226,12 @@ static int dmaengine_pcm_new(struct snd_soc_component *component,
size_t max_buffer_size;
unsigned int i;
- if (config && config->prealloc_buffer_size)
+ if (config->prealloc_buffer_size)
prealloc_buffer_size = config->prealloc_buffer_size;
else
prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024;
- if (config && config->pcm_hardware && config->pcm_hardware->buffer_bytes_max)
+ if (config->pcm_hardware && config->pcm_hardware->buffer_bytes_max)
max_buffer_size = config->pcm_hardware->buffer_bytes_max;
else
max_buffer_size = SIZE_MAX;
@@ -254,7 +241,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component,
if (!substream)
continue;
- if (!pcm->chan[i] && config && config->chan_names[i])
+ if (!pcm->chan[i] && config->chan_names[i])
pcm->chan[i] = dma_request_slave_channel(dev,
config->chan_names[i]);
@@ -367,10 +354,10 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
struct dma_chan *chan;
if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || (!dev->of_node &&
- !(config && config->dma_dev && config->dma_dev->of_node)))
+ !(config->dma_dev && config->dma_dev->of_node)))
return 0;
- if (config && config->dma_dev) {
+ if (config->dma_dev) {
/*
* If this warning is seen, it probably means that your Linux
* device structure does not match your HW device structure.
@@ -387,7 +374,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
name = "rx-tx";
else
name = dmaengine_pcm_dma_channel_names[i];
- if (config && config->chan_names[i])
+ if (config->chan_names[i])
name = config->chan_names[i];
chan = dma_request_chan(dev, name);
if (IS_ERR(chan)) {
@@ -425,6 +412,10 @@ static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
}
}
+static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
/**
* snd_dmaengine_pcm_register - Register a dmaengine based PCM device
* @dev: The parent device for the PCM device
@@ -445,6 +436,8 @@ int snd_dmaengine_pcm_register(struct device *dev,
#ifdef CONFIG_DEBUG_FS
pcm->component.debugfs_prefix = "dma";
#endif
+ if (!config)
+ config = &snd_dmaengine_pcm_default_config;
pcm->config = config;
pcm->flags = flags;
@@ -452,7 +445,7 @@ int snd_dmaengine_pcm_register(struct device *dev,
if (ret)
goto err_free_dma;
- if (config && config->process)
+ if (config->process)
driver = &dmaengine_pcm_component_process;
else
driver = &dmaengine_pcm_component;
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index d798765d168c..fcece5ca38c6 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_get_type);
/**
* snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
*
- * @jack: ASoC jack
+ * @jack: ASoC jack created with snd_soc_card_jack_new_pins()
* @count: Number of pins
* @pins: Array of pins
*
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 11c9853e9e80..6f43db35a5c8 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2090,6 +2090,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
int cmd)
{
struct snd_soc_pcm_runtime *be;
+ bool pause_stop_transition;
struct snd_soc_dpcm *dpcm;
unsigned long flags;
int ret = 0;
@@ -2121,6 +2122,13 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
if (be->dpcm[stream].be_start != 1)
goto next;
+ if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_PAUSED)
+ ret = soc_pcm_trigger(be_substream,
+ SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ else
+ ret = soc_pcm_trigger(be_substream,
+ SNDRV_PCM_TRIGGER_START);
+
ret = soc_pcm_trigger(be_substream, cmd);
if (ret) {
be->dpcm[stream].be_start--;
@@ -2148,10 +2156,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!be->dpcm[stream].be_start &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
goto next;
+ fe->dpcm[stream].fe_pause = false;
+ be->dpcm[stream].be_pause--;
+
be->dpcm[stream].be_start++;
if (be->dpcm[stream].be_start != 1)
goto next;
@@ -2175,14 +2185,33 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
if (be->dpcm[stream].be_start != 0)
goto next;
- ret = soc_pcm_trigger(be_substream, cmd);
+ pause_stop_transition = false;
+ if (fe->dpcm[stream].fe_pause) {
+ pause_stop_transition = true;
+ fe->dpcm[stream].fe_pause = false;
+ be->dpcm[stream].be_pause--;
+ }
+
+ if (be->dpcm[stream].be_pause != 0)
+ ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+ else
+ ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_STOP);
+
if (ret) {
if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
be->dpcm[stream].be_start++;
+ if (pause_stop_transition) {
+ fe->dpcm[stream].fe_pause = true;
+ be->dpcm[stream].be_pause++;
+ }
goto next;
}
- be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+ if (be->dpcm[stream].be_pause != 0)
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
+ else
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
@@ -2204,6 +2233,9 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
goto next;
+ fe->dpcm[stream].fe_pause = true;
+ be->dpcm[stream].be_pause++;
+
be->dpcm[stream].be_start--;
if (be->dpcm[stream].be_start != 0)
goto next;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 3bb90a819650..3f9d314fba16 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -40,7 +40,7 @@
*/
#define SOC_TPLG_PASS_MANIFEST 0
#define SOC_TPLG_PASS_VENDOR 1
-#define SOC_TPLG_PASS_MIXER 2
+#define SOC_TPLG_PASS_CONTROL 2
#define SOC_TPLG_PASS_WIDGET 3
#define SOC_TPLG_PASS_PCM_DAI 4
#define SOC_TPLG_PASS_GRAPH 5
@@ -104,13 +104,13 @@ static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
return 0;
}
-static inline int soc_tplg_is_eof(struct soc_tplg *tplg)
+static inline bool soc_tplg_is_eof(struct soc_tplg *tplg)
{
const u8 *end = tplg->hdr_pos;
if (end >= tplg->fw->data + tplg->fw->size)
- return 1;
- return 0;
+ return true;
+ return false;
}
static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg)
@@ -237,7 +237,7 @@ static inline void soc_control_err(struct soc_tplg *tplg,
struct snd_soc_tplg_ctl_hdr *hdr, const char *name)
{
dev_err(tplg->dev,
- "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n",
+ "ASoC: no complete control IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n",
name, hdr->ops.get, hdr->ops.put, hdr->ops.info,
soc_tplg_get_offset(tplg));
}
@@ -360,7 +360,7 @@ static void remove_mixer(struct snd_soc_component *comp,
{
struct snd_card *card = comp->card->snd_card;
- if (pass != SOC_TPLG_PASS_MIXER)
+ if (pass != SOC_TPLG_PASS_CONTROL)
return;
if (dobj->ops && dobj->ops->control_unload)
@@ -376,7 +376,7 @@ static void remove_enum(struct snd_soc_component *comp,
{
struct snd_card *card = comp->card->snd_card;
- if (pass != SOC_TPLG_PASS_MIXER)
+ if (pass != SOC_TPLG_PASS_CONTROL)
return;
if (dobj->ops && dobj->ops->control_unload)
@@ -392,7 +392,7 @@ static void remove_bytes(struct snd_soc_component *comp,
{
struct snd_card *card = comp->card->snd_card;
- if (pass != SOC_TPLG_PASS_MIXER)
+ if (pass != SOC_TPLG_PASS_CONTROL)
return;
if (dobj->ops && dobj->ops->control_unload)
@@ -618,7 +618,7 @@ int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w,
EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event);
/* optionally pass new dynamic kcontrol to component driver. */
-static int soc_tplg_init_kcontrol(struct soc_tplg *tplg,
+static int soc_tplg_control_load(struct soc_tplg *tplg,
struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
{
if (tplg->ops && tplg->ops->control_load)
@@ -676,175 +676,156 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg,
return 0;
}
-static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
- size_t size)
+static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
{
struct snd_soc_tplg_bytes_control *be;
struct soc_bytes_ext *sbe;
struct snd_kcontrol_new kc;
- int i;
- int err = 0;
+ int ret = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_bytes_control),
- count, size, "mixer bytes"))
+ 1, size, "mixer bytes"))
return -EINVAL;
- for (i = 0; i < count; i++) {
- be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+ be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
- /* validate kcontrol */
- if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
+ /* validate kcontrol */
+ if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
- sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
- if (sbe == NULL)
- return -ENOMEM;
+ sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
+ if (sbe == NULL)
+ return -ENOMEM;
- tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
- le32_to_cpu(be->priv.size));
+ tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+ le32_to_cpu(be->priv.size));
- dev_dbg(tplg->dev,
- "ASoC: adding bytes kcontrol %s with access 0x%x\n",
- be->hdr.name, be->hdr.access);
-
- memset(&kc, 0, sizeof(kc));
- kc.name = be->hdr.name;
- kc.private_value = (long)sbe;
- kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = le32_to_cpu(be->hdr.access);
-
- sbe->max = le32_to_cpu(be->max);
- sbe->dobj.type = SND_SOC_DOBJ_BYTES;
- sbe->dobj.ops = tplg->ops;
- INIT_LIST_HEAD(&sbe->dobj.list);
-
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg);
- if (err) {
- soc_control_err(tplg, &be->hdr, be->hdr.name);
- break;
- }
+ dev_dbg(tplg->dev,
+ "ASoC: adding bytes kcontrol %s with access 0x%x\n",
+ be->hdr.name, be->hdr.access);
- /* pass control to driver for optional further init */
- err = soc_tplg_init_kcontrol(tplg, &kc,
- (struct snd_soc_tplg_ctl_hdr *)be);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- be->hdr.name);
- break;
- }
+ memset(&kc, 0, sizeof(kc));
+ kc.name = be->hdr.name;
+ kc.private_value = (long)sbe;
+ kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc.access = le32_to_cpu(be->hdr.access);
- /* register control here */
- err = soc_tplg_add_kcontrol(tplg, &kc,
- &sbe->dobj.control.kcontrol);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to add %s\n",
- be->hdr.name);
- break;
- }
+ sbe->max = le32_to_cpu(be->max);
+ sbe->dobj.type = SND_SOC_DOBJ_BYTES;
+ sbe->dobj.ops = tplg->ops;
+ INIT_LIST_HEAD(&sbe->dobj.list);
- list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+ /* map io handlers */
+ ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg);
+ if (ret) {
+ soc_control_err(tplg, &be->hdr, be->hdr.name);
+ goto err;
}
- return err;
+ /* pass control to driver for optional further init */
+ ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)be);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name);
+ 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);
+ goto err;
+ }
+
+ list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+
+err:
+ return ret;
}
-static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
- size_t size)
+static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
{
struct snd_soc_tplg_mixer_control *mc;
struct soc_mixer_control *sm;
struct snd_kcontrol_new kc;
- int i;
- int err = 0;
+ int ret = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_mixer_control),
- count, size, "mixers"))
+ 1, size, "mixers"))
return -EINVAL;
- for (i = 0; i < count; i++) {
- mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
+ mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
- /* validate kcontrol */
- if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
+ /* validate kcontrol */
+ if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
- sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
- if (sm == NULL)
- return -ENOMEM;
- tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
- le32_to_cpu(mc->priv.size));
+ sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
+ if (sm == NULL)
+ return -ENOMEM;
+ tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+ le32_to_cpu(mc->priv.size));
- dev_dbg(tplg->dev,
- "ASoC: adding mixer kcontrol %s with access 0x%x\n",
- mc->hdr.name, mc->hdr.access);
-
- memset(&kc, 0, sizeof(kc));
- kc.name = mc->hdr.name;
- kc.private_value = (long)sm;
- kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = le32_to_cpu(mc->hdr.access);
-
- /* we only support FL/FR channel mapping atm */
- sm->reg = tplc_chan_get_reg(tplg, mc->channel,
- SNDRV_CHMAP_FL);
- sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
- SNDRV_CHMAP_FR);
- sm->shift = tplc_chan_get_shift(tplg, mc->channel,
- SNDRV_CHMAP_FL);
- sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
- SNDRV_CHMAP_FR);
-
- sm->max = le32_to_cpu(mc->max);
- sm->min = le32_to_cpu(mc->min);
- sm->invert = le32_to_cpu(mc->invert);
- sm->platform_max = le32_to_cpu(mc->platform_max);
- sm->dobj.index = tplg->index;
- sm->dobj.ops = tplg->ops;
- sm->dobj.type = SND_SOC_DOBJ_MIXER;
- INIT_LIST_HEAD(&sm->dobj.list);
-
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg);
- if (err) {
- soc_control_err(tplg, &mc->hdr, mc->hdr.name);
- break;
- }
+ dev_dbg(tplg->dev,
+ "ASoC: adding mixer kcontrol %s with access 0x%x\n",
+ mc->hdr.name, mc->hdr.access);
- /* create any TLV data */
- err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
- mc->hdr.name);
- break;
- }
+ memset(&kc, 0, sizeof(kc));
+ kc.name = mc->hdr.name;
+ kc.private_value = (long)sm;
+ kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc.access = le32_to_cpu(mc->hdr.access);
- /* pass control to driver for optional further init */
- err = soc_tplg_init_kcontrol(tplg, &kc,
- (struct snd_soc_tplg_ctl_hdr *) mc);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- mc->hdr.name);
- break;
- }
+ /* we only support FL/FR channel mapping atm */
+ sm->reg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL);
+ sm->rreg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR);
+ sm->shift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
+ sm->rshift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
- /* register control here */
- err = soc_tplg_add_kcontrol(tplg, &kc,
- &sm->dobj.control.kcontrol);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to add %s\n",
- mc->hdr.name);
- break;
- }
+ sm->max = le32_to_cpu(mc->max);
+ sm->min = le32_to_cpu(mc->min);
+ sm->invert = le32_to_cpu(mc->invert);
+ sm->platform_max = le32_to_cpu(mc->platform_max);
+ sm->dobj.index = tplg->index;
+ sm->dobj.ops = tplg->ops;
+ sm->dobj.type = SND_SOC_DOBJ_MIXER;
+ INIT_LIST_HEAD(&sm->dobj.list);
+
+ /* map io handlers */
+ ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg);
+ if (ret) {
+ soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+ goto err;
+ }
- list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+ /* create any TLV data */
+ ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name);
+ goto err;
}
- return err;
+ /* pass control to driver for optional further init */
+ ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)mc);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name);
+ 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);
+ goto err;
+ }
+
+ list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+
+err:
+ return ret;
}
static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se,
@@ -911,117 +892,108 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *
return 0;
}
-static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
- size_t size)
+static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
{
struct snd_soc_tplg_enum_control *ec;
struct soc_enum *se;
struct snd_kcontrol_new kc;
- int i;
- int err = 0;
+ int ret = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_enum_control),
- count, size, "enums"))
+ 1, size, "enums"))
return -EINVAL;
- for (i = 0; i < count; i++) {
- ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+ ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
- /* validate kcontrol */
- if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
+ /* validate kcontrol */
+ if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
- se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL);
- if (se == NULL)
- return -ENOMEM;
+ se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL);
+ if (se == NULL)
+ return -ENOMEM;
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- le32_to_cpu(ec->priv.size));
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ le32_to_cpu(ec->priv.size));
- dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
- ec->hdr.name, ec->items);
+ dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
+ ec->hdr.name, ec->items);
- memset(&kc, 0, sizeof(kc));
- kc.name = ec->hdr.name;
- kc.private_value = (long)se;
- kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = le32_to_cpu(ec->hdr.access);
+ memset(&kc, 0, sizeof(kc));
+ kc.name = ec->hdr.name;
+ kc.private_value = (long)se;
+ kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc.access = le32_to_cpu(ec->hdr.access);
- se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
- se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FL);
- se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FL);
+ se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+ SNDRV_CHMAP_FL);
+ se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+ SNDRV_CHMAP_FL);
- se->mask = le32_to_cpu(ec->mask);
- se->dobj.index = tplg->index;
- se->dobj.type = SND_SOC_DOBJ_ENUM;
- se->dobj.ops = tplg->ops;
- INIT_LIST_HEAD(&se->dobj.list);
+ se->mask = le32_to_cpu(ec->mask);
+ se->dobj.index = tplg->index;
+ se->dobj.type = SND_SOC_DOBJ_ENUM;
+ se->dobj.ops = tplg->ops;
+ INIT_LIST_HEAD(&se->dobj.list);
- switch (le32_to_cpu(ec->hdr.ops.info)) {
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
- case SND_SOC_TPLG_CTL_ENUM_VALUE:
- err = soc_tplg_denum_create_values(tplg, se, ec);
- if (err < 0) {
- dev_err(tplg->dev,
- "ASoC: could not create values for %s\n",
- ec->hdr.name);
- goto err_denum;
- }
- fallthrough;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- err = soc_tplg_denum_create_texts(tplg, se, ec);
- if (err < 0) {
- dev_err(tplg->dev,
- "ASoC: could not create texts for %s\n",
- ec->hdr.name);
- goto err_denum;
- }
- break;
- default:
- err = -EINVAL;
+ switch (le32_to_cpu(ec->hdr.ops.info)) {
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ ret = soc_tplg_denum_create_values(tplg, se, ec);
+ if (ret < 0) {
dev_err(tplg->dev,
- "ASoC: invalid enum control type %d for %s\n",
- ec->hdr.ops.info, ec->hdr.name);
- goto err_denum;
- }
-
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
- if (err) {
- soc_control_err(tplg, &ec->hdr, ec->hdr.name);
- goto err_denum;
- }
-
- /* pass control to driver for optional further init */
- err = soc_tplg_init_kcontrol(tplg, &kc,
- (struct snd_soc_tplg_ctl_hdr *) ec);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ "ASoC: could not create values for %s\n",
ec->hdr.name);
- goto err_denum;
+ goto err;
}
-
- /* register control here */
- err = soc_tplg_add_kcontrol(tplg,
- &kc, &se->dobj.control.kcontrol);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n",
+ fallthrough;
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ ret = soc_tplg_denum_create_texts(tplg, se, ec);
+ if (ret < 0) {
+ dev_err(tplg->dev,
+ "ASoC: could not create texts for %s\n",
ec->hdr.name);
- goto err_denum;
+ goto err;
}
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(tplg->dev,
+ "ASoC: invalid enum control type %d for %s\n",
+ ec->hdr.ops.info, ec->hdr.name);
+ goto err;
+ }
- list_add(&se->dobj.list, &tplg->comp->dobj_list);
+ /* map io handlers */
+ ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
+ if (ret) {
+ soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+ goto err;
}
- return 0;
-err_denum:
- return err;
+ /* pass control to driver for optional further init */
+ ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)ec);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name);
+ 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);
+ goto err;
+ }
+
+ list_add(&se->dobj.list, &tplg->comp->dobj_list);
+
+err:
+ return ret;
}
static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
@@ -1049,20 +1021,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
case SND_SOC_TPLG_CTL_RANGE:
case SND_SOC_TPLG_DAPM_CTL_VOLSW:
case SND_SOC_TPLG_DAPM_CTL_PIN:
- ret = soc_tplg_dmixer_create(tplg, 1,
- le32_to_cpu(hdr->payload_size));
+ ret = soc_tplg_dmixer_create(tplg, le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_ENUM:
case SND_SOC_TPLG_CTL_ENUM_VALUE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
- ret = soc_tplg_denum_create(tplg, 1,
- le32_to_cpu(hdr->payload_size));
+ ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_BYTES:
- ret = soc_tplg_dbytes_create(tplg, 1,
- le32_to_cpu(hdr->payload_size));
+ ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size));
break;
default:
soc_bind_err(tplg, control_hdr, i);
@@ -1224,8 +1193,7 @@ 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_init_kcontrol(tplg, kc,
- (struct snd_soc_tplg_ctl_hdr *)mc);
+ err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)mc);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
@@ -1309,8 +1277,7 @@ 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_init_kcontrol(tplg, kc,
- (struct snd_soc_tplg_ctl_hdr *)ec);
+ err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)ec);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
ec->hdr.name);
@@ -1362,8 +1329,7 @@ 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_init_kcontrol(tplg, kc,
- (struct snd_soc_tplg_ctl_hdr *)be);
+ err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)be);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
be->hdr.name);
@@ -2498,7 +2464,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
case SND_SOC_TPLG_TYPE_MIXER:
case SND_SOC_TPLG_TYPE_ENUM:
case SND_SOC_TPLG_TYPE_BYTES:
- hdr_pass = SOC_TPLG_PASS_MIXER;
+ hdr_pass = SOC_TPLG_PASS_CONTROL;
elem_load = soc_tplg_kcontrol_elems_load;
break;
case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
@@ -2550,10 +2516,8 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
{
int ret;
- tplg->pass = SOC_TPLG_PASS_START;
-
/* process the header types from start to end */
- while (tplg->pass <= SOC_TPLG_PASS_END) {
+ for (tplg->pass = SOC_TPLG_PASS_START; tplg->pass <= SOC_TPLG_PASS_END; tplg->pass++) {
struct snd_soc_tplg_hdr *hdr;
tplg->hdr_pos = tplg->fw->data;
@@ -2585,8 +2549,6 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
}
- /* next data type pass */
- tplg->pass++;
}
/* signal DAPM we are complete */
@@ -2653,10 +2615,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
{
struct snd_card *card = comp->card->snd_card;
struct snd_soc_dobj *dobj, *next_dobj;
- int pass = SOC_TPLG_PASS_END;
+ int pass;
/* process the header types from end to start */
- while (pass >= SOC_TPLG_PASS_START) {
+ for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) {
/* remove mixer controls */
down_write(&card->controls_rwsem);
@@ -2699,7 +2661,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
}
}
up_write(&card->controls_rwsem);
- pass--;
}
/* let caller know if FW can be freed when no objects are left */
diff --git a/sound/soc/soc-utils-test.c b/sound/soc/soc-utils-test.c
new file mode 100644
index 000000000000..5ad8e23af49a
--- /dev/null
+++ b/sound/soc/soc-utils-test.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2022 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/test.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <uapi/sound/asound.h>
+
+static const struct {
+ u32 rate;
+ snd_pcm_format_t fmt;
+ u8 channels;
+ u8 tdm_width;
+ u8 tdm_slots;
+ u8 slot_multiple;
+ u32 bclk;
+} tdm_params_to_bclk_cases[] = {
+ /* rate fmt channels tdm_width tdm_slots slot_multiple bclk */
+
+ /* From params only */
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 0, 128000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 0, 256000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 0, 192000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 0, 384000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 0, 256000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 0, 512000 },
+ { 44100, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 0, 705600 },
+ { 44100, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 0, 1411200 },
+ { 44100, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 0, 1058400 },
+ { 44100, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 0, 2116800 },
+ { 44100, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 0, 1411200 },
+ { 44100, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 0, 2822400 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 0, 6144000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 0, 12288000 },
+ { 384000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 0, 9216000 },
+ { 384000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 0, 18432000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 0, 12288000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 0, 24576000 },
+
+ /* I2S from params */
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 2, 256000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 2, 256000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 2, 384000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 2, 384000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 2, 512000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 2, 512000 },
+ { 44100, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 2, 1411200 },
+ { 44100, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 2, 1411200 },
+ { 44100, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 2, 2116800 },
+ { 44100, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 2, 2116800 },
+ { 44100, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 2, 2822400 },
+ { 44100, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 2, 2822400 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 2, 12288000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 2, 12288000 },
+ { 384000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 2, 18432000 },
+ { 384000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 2, 18432000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 2, 24576000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 2, 24576000 },
+
+ /* Fixed 8-slot TDM, other values from params */
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 8, 0, 1024000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 8, 0, 1024000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 3, 0, 8, 0, 1024000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 4, 0, 8, 0, 1024000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 8, 0, 2048000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 8, 0, 2048000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 3, 0, 8, 0, 2048000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 4, 0, 8, 0, 2048000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 8, 0, 49152000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 8, 0, 49152000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 3, 0, 8, 0, 49152000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 4, 0, 8, 0, 49152000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 8, 0, 98304000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 8, 0, 98304000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 3, 0, 8, 0, 98304000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 4, 0, 8, 0, 98304000 },
+
+ /* Fixed 32-bit TDM, other values from params */
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 32, 0, 0, 256000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 32, 0, 0, 512000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 3, 32, 0, 0, 768000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 4, 32, 0, 0, 1024000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 32, 0, 0, 256000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 32, 0, 0, 512000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 3, 32, 0, 0, 768000 },
+ { 8000, SNDRV_PCM_FORMAT_S32_LE, 4, 32, 0, 0, 1024000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 32, 0, 0, 12288000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 32, 0, 0, 24576000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 3, 32, 0, 0, 36864000 },
+ { 384000, SNDRV_PCM_FORMAT_S16_LE, 4, 32, 0, 0, 49152000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 32, 0, 0, 12288000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 32, 0, 0, 24576000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 3, 32, 0, 0, 36864000 },
+ { 384000, SNDRV_PCM_FORMAT_S32_LE, 4, 32, 0, 0, 49152000 },
+
+ /* Fixed 6-slot 24-bit TDM, other values from params */
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 3, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S16_LE, 4, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 1, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 2, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 3, 24, 6, 0, 1152000 },
+ { 8000, SNDRV_PCM_FORMAT_S24_LE, 4, 24, 6, 0, 1152000 },
+ { 192000, SNDRV_PCM_FORMAT_S16_LE, 1, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S16_LE, 2, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S16_LE, 3, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S16_LE, 4, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S24_LE, 1, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S24_LE, 2, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S24_LE, 3, 24, 6, 0, 27648000 },
+ { 192000, SNDRV_PCM_FORMAT_S24_LE, 4, 24, 6, 0, 27648000 },
+};
+
+static void test_tdm_params_to_bclk_one(struct kunit *test,
+ unsigned int rate, snd_pcm_format_t fmt,
+ unsigned int channels,
+ unsigned int tdm_width, unsigned int tdm_slots,
+ unsigned int slot_multiple,
+ unsigned int expected_bclk)
+{
+ struct snd_pcm_hw_params params;
+ int got_bclk;
+
+ _snd_pcm_hw_params_any(&params);
+ snd_mask_none(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT));
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->min = rate;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->max = rate;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = channels;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->max = channels;
+ params_set_format(&params, fmt);
+
+ got_bclk = snd_soc_tdm_params_to_bclk(&params, tdm_width, tdm_slots, slot_multiple);
+ pr_debug("%s: r=%u sb=%u ch=%u tw=%u ts=%u sm=%u expected=%u got=%d\n",
+ __func__,
+ rate, params_width(&params), channels, tdm_width, tdm_slots, slot_multiple,
+ expected_bclk, got_bclk);
+ KUNIT_ASSERT_EQ(test, expected_bclk, (unsigned int)got_bclk);
+}
+
+static void test_tdm_params_to_bclk(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tdm_params_to_bclk_cases); ++i) {
+ test_tdm_params_to_bclk_one(test,
+ tdm_params_to_bclk_cases[i].rate,
+ tdm_params_to_bclk_cases[i].fmt,
+ tdm_params_to_bclk_cases[i].channels,
+ tdm_params_to_bclk_cases[i].tdm_width,
+ tdm_params_to_bclk_cases[i].tdm_slots,
+ tdm_params_to_bclk_cases[i].slot_multiple,
+ tdm_params_to_bclk_cases[i].bclk);
+
+ if (tdm_params_to_bclk_cases[i].slot_multiple > 0)
+ continue;
+
+ /* Slot multiple 1 should have the same effect as multiple 0 */
+ test_tdm_params_to_bclk_one(test,
+ tdm_params_to_bclk_cases[i].rate,
+ tdm_params_to_bclk_cases[i].fmt,
+ tdm_params_to_bclk_cases[i].channels,
+ tdm_params_to_bclk_cases[i].tdm_width,
+ tdm_params_to_bclk_cases[i].tdm_slots,
+ 1,
+ tdm_params_to_bclk_cases[i].bclk);
+ }
+}
+
+static struct kunit_case soc_utils_test_cases[] = {
+ KUNIT_CASE(test_tdm_params_to_bclk),
+ {}
+};
+
+static struct kunit_suite soc_utils_test_suite = {
+ .name = "soc-utils",
+ .test_cases = soc_utils_test_cases,
+};
+
+kunit_test_suites(&soc_utils_test_suite);
+
+MODULE_DESCRIPTION("ASoC soc-utils kunit test");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index a4efe7e52a8b..594cb311ff30 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -9,6 +9,7 @@
#include <linux/platform_device.h>
#include <linux/export.h>
+#include <linux/math.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -52,6 +53,50 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
}
EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
+/**
+ * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info.
+ *
+ * Calculate the bclk from the params sample rate and the tdm slot count and
+ * tdm slot width. Either or both of tdm_width and tdm_slots can be 0.
+ *
+ * If tdm_width == 0 and tdm_slots > 0: the params_width will be used.
+ * If tdm_width > 0 and tdm_slots == 0: the params_channels will be used
+ * as the slot count.
+ * Both tdm_width and tdm_slots are 0: this is equivalent to calling
+ * snd_soc_params_to_bclk().
+ *
+ * If slot_multiple > 1 the slot count (or params_channels if tdm_slots == 0)
+ * will be rounded up to a multiple of this value. This is mainly useful for
+ * I2S mode, which has a left and right phase so the number of slots is always
+ * a multiple of 2.
+ *
+ * @params: Pointer to struct_pcm_hw_params.
+ * @tdm_width: Width in bits of the tdm slots.
+ * @tdm_slots: Number of tdm slots per frame.
+ * @slot_multiple: If >1 roundup slot count to a multiple of this value.
+ *
+ * Return: bclk frequency in Hz, else a negative error code if params format
+ * is invalid.
+ */
+int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params,
+ int tdm_width, int tdm_slots, int slot_multiple)
+{
+ if (!tdm_slots)
+ tdm_slots = params_channels(params);
+
+ if (slot_multiple > 1)
+ tdm_slots = roundup(tdm_slots, slot_multiple);
+
+ if (!tdm_width) {
+ tdm_width = snd_pcm_format_width(params_format(params));
+ if (tdm_width < 0)
+ return tdm_width;
+ }
+
+ return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk);
+
static const struct snd_pcm_hardware dummy_dma_hardware = {
/* Random values to keep userspace happy when checking constraints */
.info = SNDRV_PCM_INFO_INTERLEAVED |
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 18acbc001b9a..92b5e83601be 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -2,7 +2,9 @@
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
- ipc3-topology.o ipc3.o ipc3-control.o ipc3-pcm.o
+ ipc3-topology.o ipc3-control.o ipc3.o ipc3-pcm.o ipc3-loader.o\
+ ipc3-dtrace.o\
+ ipc4.o ipc4-loader.o
ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),)
snd-sof-objs += sof-client.o
endif
diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c
index 903b6cc3dda3..c9482b27cbe3 100644
--- a/sound/soc/sof/amd/acp-trace.c
+++ b/sound/soc/sof/amd/acp-trace.c
@@ -34,7 +34,7 @@ int acp_sof_trace_release(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON);
-int acp_sof_trace_init(struct snd_sof_dev *sdev,
+int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
struct acp_dsp_stream *stream;
@@ -46,7 +46,7 @@ int acp_sof_trace_init(struct snd_sof_dev *sdev,
if (!stream)
return -ENODEV;
- stream->dmab = &sdev->dmatb;
+ stream->dmab = dmab;
stream->num_pages = NUM_PAGES;
ret = acp_dsp_stream_config(sdev, stream);
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 71d71c152342..0c272573df97 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -138,23 +138,75 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
return ret;
}
-static int psp_fw_validate(struct acp_dev_data *adata)
+/*
+ * psp_mbox_ready- function to poll ready bit of psp mbox
+ * @adata: acp device data
+ * @ack: bool variable to check ready bit status or psp ack
+ */
+
+static int psp_mbox_ready(struct acp_dev_data *adata, bool ack)
{
struct snd_sof_dev *sdev = adata->dev;
int timeout;
u32 data;
- smn_write(adata->smn_dev, MP0_C2PMSG_26_REG, MBOX_ACP_SHA_DMA_COMMAND);
-
for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
msleep(20);
- smn_read(adata->smn_dev, MP0_C2PMSG_26_REG, &data);
+ smn_read(adata->smn_dev, MP0_C2PMSG_114_REG, &data);
if (data & MBOX_READY_MASK)
return 0;
}
- dev_err(sdev->dev, "FW validation timedout: status %x\n", data & MBOX_STATUS_MASK);
- return -ETIMEDOUT;
+ dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK);
+
+ if (ack)
+ return -ETIMEDOUT;
+
+ return -EBUSY;
+}
+
+/*
+ * psp_send_cmd - function to send psp command over mbox
+ * @adata: acp device data
+ * @cmd: non zero integer value for command type
+ */
+
+static int psp_send_cmd(struct acp_dev_data *adata, int cmd)
+{
+ struct snd_sof_dev *sdev = adata->dev;
+ int ret, timeout;
+ u32 data;
+
+ if (!cmd)
+ return -EINVAL;
+
+ /* Get a non-zero Doorbell value from PSP */
+ for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
+ msleep(MBOX_DELAY);
+ smn_read(adata->smn_dev, MP0_C2PMSG_73_REG, &data);
+ if (data)
+ break;
+ }
+
+ if (!timeout) {
+ dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG);
+ return -EINVAL;
+ }
+
+ /* Check if PSP is ready for new command */
+ ret = psp_mbox_ready(adata, 0);
+ if (ret)
+ return ret;
+
+ smn_write(adata->smn_dev, MP0_C2PMSG_114_REG, cmd);
+
+ /* Ring the Doorbell for PSP */
+ smn_write(adata->smn_dev, MP0_C2PMSG_73_REG, data);
+
+ /* Check MBOX ready as PSP ack */
+ ret = psp_mbox_ready(adata, 1);
+
+ return ret;
}
int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
@@ -196,7 +248,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
return ret;
}
- ret = psp_fw_validate(adata);
+ ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);
if (ret)
return ret;
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 35e46fe6676a..291b44c54bcc 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -57,8 +57,10 @@
#define ACP_SHA_STAT 0x8000
#define ACP_PSP_TIMEOUT_COUNTER 5
#define ACP_EXT_INTR_ERROR_STAT 0x20000000
-#define MP0_C2PMSG_26_REG 0x03810570
-#define MBOX_ACP_SHA_DMA_COMMAND 0x330000
+#define MP0_C2PMSG_114_REG 0x3810AC8
+#define MP0_C2PMSG_73_REG 0x3810A24
+#define MBOX_ACP_SHA_DMA_COMMAND 0x70000
+#define MBOX_DELAY 1000
#define MBOX_READY_MASK 0x80000000
#define MBOX_STATUS_MASK 0xFFFF
@@ -204,13 +206,13 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr
struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params);
-extern const struct snd_sof_dsp_ops sof_renoir_ops;
+extern struct snd_sof_dsp_ops sof_renoir_ops;
/* Machine configuration */
int snd_amd_acp_find_config(struct pci_dev *pci);
/* Trace */
-int acp_sof_trace_init(struct snd_sof_dev *sdev,
+int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
struct sof_ipc_dma_trace_params_ext *dtrace_params);
int acp_sof_trace_release(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c
index 392ffbdf6417..d5d9bcc2c997 100644
--- a/sound/soc/sof/amd/pci-rn.c
+++ b/sound/soc/sof/amd/pci-rn.c
@@ -54,9 +54,17 @@ static const struct sof_dev_desc renoir_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &renoir_chip_info,
- .default_fw_path = "amd/sof",
- .default_tplg_path = "amd/sof-tplg",
- .default_fw_filename = "sof-rn.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "amd/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "amd/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-rn.ri",
+ },
.nocodec_tplg_filename = "sof-acp.tplg",
.ops = &sof_renoir_ops,
};
@@ -93,6 +101,7 @@ static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci
res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
if (!res) {
sof_pci_remove(pci);
+ platform_device_unregister(dmic_dev);
return -ENOMEM;
}
diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c
index 409fd57448b8..70190365328c 100644
--- a/sound/soc/sof/amd/renoir.c
+++ b/sound/soc/sof/amd/renoir.c
@@ -123,7 +123,7 @@ static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev
}
/* AMD Renoir DSP ops */
-const struct snd_sof_dsp_ops sof_renoir_ops = {
+struct snd_sof_dsp_ops sof_renoir_ops = {
/* probe and remove */
.probe = amd_sof_acp_probe,
.remove = amd_sof_acp_remove,
@@ -136,9 +136,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
.block_read = acp_dsp_block_read,
.block_write = acp_dsp_block_write,
- /* Module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
.pre_fw_run = acp_dsp_pre_fw_run,
@@ -152,7 +149,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
.ipc_msg_data = acp_sof_ipc_msg_data,
.get_mailbox_offset = acp_sof_ipc_get_mailbox_offset,
.irq_thread = acp_sof_ipc_irq_thread,
- .fw_ready = sof_fw_ready,
/* DAI drivers */
.drv = renoir_sof_dai,
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index a8e908e50101..47639b6344c8 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -147,8 +147,7 @@ static int sof_compr_free(struct snd_soc_component *component,
stream.comp_id = spcm->stream[cstream->direction].comp_id;
if (spcm->prepared[cstream->direction]) {
- ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd,
- &stream, sizeof(stream),
+ ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
&reply, sizeof(reply));
if (!ret)
spcm->prepared[cstream->direction] = false;
@@ -209,7 +208,7 @@ static int sof_compr_set_params(struct snd_soc_component *component,
snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3;
pcm.params.host_period_bytes = params->buffer.fragment_size;
- ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+ ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
&ipc_params_reply, sizeof(ipc_params_reply));
if (ret < 0) {
dev_err(component->dev, "error ipc failed\n");
@@ -268,8 +267,7 @@ static int sof_compr_trigger(struct snd_soc_component *component,
break;
}
- return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd,
- &stream, sizeof(stream),
+ return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
&reply, sizeof(reply));
}
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index de1778c4002b..e0e9efd25d34 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -15,36 +15,6 @@
#include "sof-priv.h"
#include "sof-audio.h"
-static void update_mute_led(struct snd_sof_control *scontrol,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int temp = 0;
- int mask;
- int i;
-
- mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- for (i = 0; i < scontrol->num_channels; i++) {
- if (ucontrol->value.integer.value[i]) {
- temp |= mask;
- break;
- }
- }
-
- if (temp == scontrol->led_ctl.led_value)
- return;
-
- scontrol->led_ctl.led_value = temp;
-
-#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
- if (!scontrol->led_ctl.direction)
- ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON);
- else
- ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON);
-#endif
-}
-
int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -121,9 +91,6 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- if (scontrol->led_ctl.use_led)
- update_mute_led(scontrol, kcontrol, ucontrol);
-
if (tplg_ops->control->switch_put)
return tplg_ops->control->switch_put(scontrol, ucontrol);
@@ -220,10 +187,9 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
int ret, err;
- ret = pm_runtime_get_sync(scomp->dev);
+ ret = pm_runtime_resume_and_get(scomp->dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret);
- pm_runtime_put_noidle(scomp->dev);
return ret;
}
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index e91631618bff..53719c04658f 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -250,14 +250,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
}
if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) {
- sdev->dtrace_is_supported = true;
+ sdev->fw_trace_is_supported = true;
- /* init DMA trace */
- ret = snd_sof_init_trace(sdev);
+ /* init firmware tracing */
+ ret = sof_fw_trace_init(sdev);
if (ret < 0) {
/* non fatal */
- dev_warn(sdev->dev,
- "warning: failed to initialize trace %d\n",
+ dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n",
ret);
}
} else {
@@ -308,7 +307,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
sof_machine_err:
snd_sof_machine_unregister(sdev, plat_data);
fw_trace_err:
- snd_sof_free_trace(sdev);
+ sof_fw_trace_free(sdev);
fw_run_err:
snd_sof_fw_unload(sdev);
fw_load_err:
@@ -342,6 +341,7 @@ static void sof_probe_work(struct work_struct *work)
int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
{
struct snd_sof_dev *sdev;
+ int ret;
sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
if (!sdev)
@@ -357,11 +357,23 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->first_boot = true;
dev_set_drvdata(dev, sdev);
+ /* check IPC support */
+ if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
+ dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
+ plat_data->ipc_type, plat_data->desc->ipc_supported_mask);
+ return -EINVAL;
+ }
+
+ /* init ops, if necessary */
+ ret = sof_ops_init(sdev);
+ if (ret < 0)
+ return ret;
+
/* check all mandatory ops */
if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
!sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
!sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
- !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->fw_ready) {
+ !sof_ops(sdev)->ipc_msg_data) {
dev_err(dev, "error: missing mandatory ops\n");
return -EINVAL;
}
@@ -434,7 +446,7 @@ int snd_sof_device_remove(struct device *dev)
snd_sof_machine_unregister(sdev, pdata);
if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
- snd_sof_free_trace(sdev);
+ sof_fw_trace_free(sdev);
ret = snd_sof_dsp_power_down_notify(sdev);
if (ret < 0)
dev_warn(dev, "error: %d failed to prepare DSP for device removal",
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index 7b1139961a99..cf1271eb29b2 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -229,14 +229,13 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
if (!reply)
return -ENOMEM;
- ret = pm_runtime_get_sync(sdev->dev);
+ ret = pm_runtime_resume_and_get(sdev->dev);
if (ret < 0 && ret != -EACCES) {
- pm_runtime_put_noidle(sdev->dev);
dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
goto error;
}
- ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
+ ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
pm_runtime_mark_last_busy(sdev->dev);
pm_runtime_put_autosuspend(sdev->dev);
if (ret < 0 || reply->rhdr.error < 0) {
@@ -331,7 +330,7 @@ EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init);
int snd_sof_dbg_init(struct snd_sof_dev *sdev)
{
- const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
+ struct snd_sof_dsp_ops *ops = sof_ops(sdev);
const struct snd_sof_debugfs_map *map;
int i;
int err;
@@ -444,6 +443,6 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev)
snd_sof_ipc_dump(sdev);
snd_sof_dsp_dbg_dump(sdev, "Firmware exception",
SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
- snd_sof_trace_notify_for_error(sdev);
+ sof_fw_trace_fw_crashed(sdev);
}
EXPORT_SYMBOL(snd_sof_handle_fw_exception);
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index 825bd2b9b7a1..2844d9a8040a 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -487,7 +487,7 @@ static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev,
}
/* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8_ops = {
+static struct snd_sof_dsp_ops sof_imx8_ops = {
/* probe and remove */
.probe = imx8_probe,
.remove = imx8_remove,
@@ -504,16 +504,14 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = {
/* ipc */
.send_msg = imx8_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = imx8_get_mailbox_offset,
.get_window_offset = imx8_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
.set_stream_data_offset = sof_set_stream_data_offset,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
.get_bar_index = imx8_get_bar_index,
+
/* firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -550,7 +548,7 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = {
};
/* i.MX8X ops */
-static const struct snd_sof_dsp_ops sof_imx8x_ops = {
+static struct snd_sof_dsp_ops sof_imx8x_ops = {
/* probe and remove */
.probe = imx8_probe,
.remove = imx8_remove,
@@ -567,16 +565,14 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = {
/* ipc */
.send_msg = imx8_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = imx8_get_mailbox_offset,
.get_window_offset = imx8_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
.set_stream_data_offset = sof_set_stream_data_offset,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
.get_bar_index = imx8_get_bar_index,
+
/* firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -613,17 +609,33 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = {
};
static struct sof_dev_desc sof_of_imx8qxp_desc = {
- .default_fw_path = "imx/sof",
- .default_tplg_path = "imx/sof-tplg",
- .default_fw_filename = "sof-imx8x.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "imx/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "imx/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-imx8x.ri",
+ },
.nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
.ops = &sof_imx8x_ops,
};
static struct sof_dev_desc sof_of_imx8qm_desc = {
- .default_fw_path = "imx/sof",
- .default_tplg_path = "imx/sof-tplg",
- .default_fw_filename = "sof-imx8.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "imx/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "imx/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-imx8.ri",
+ },
.nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
.ops = &sof_imx8_ops,
};
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
index 803d6be6b4fb..1243f8a6141e 100644
--- a/sound/soc/sof/imx/imx8m.c
+++ b/sound/soc/sof/imx/imx8m.c
@@ -412,7 +412,7 @@ static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state
}
/* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8m_ops = {
+static struct snd_sof_dsp_ops sof_imx8m_ops = {
/* probe and remove */
.probe = imx8m_probe,
.remove = imx8m_remove,
@@ -430,16 +430,14 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = {
/* ipc */
.send_msg = imx8m_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = imx8m_get_mailbox_offset,
.get_window_offset = imx8m_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
.set_stream_data_offset = sof_set_stream_data_offset,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
.get_bar_index = imx8m_get_bar_index,
+
/* firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -473,9 +471,17 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = {
};
static struct sof_dev_desc sof_of_imx8mp_desc = {
- .default_fw_path = "imx/sof",
- .default_tplg_path = "imx/sof-tplg",
- .default_fw_filename = "sof-imx8m.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "imx/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "imx/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-imx8m.ri",
+ },
.nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
.ops = &sof_imx8m_ops,
};
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 172419392b33..0def2aa5581d 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -260,7 +260,7 @@ config SND_SOC_SOF_HDA
'select' statements at a higher level.
config SND_SOC_SOF_HDA_PROBES
- bool
+ tristate
select SND_SOC_SOF_DEBUG_PROBES
help
The option enables the data probing for Intel(R) Skylake and newer
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index 1f473d4d8416..b9d51dc39ffa 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -6,7 +6,7 @@ snd-sof-acpi-intel-bdw-objs := bdw.o
snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \
hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \
hda-dai.o hda-bus.o \
- apl.o cnl.o tgl.o icl.o
+ apl.o cnl.o tgl.o icl.o hda-common-ops.o
snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
snd-sof-intel-hda-objs := hda-codec.o
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 6721c8f95161..0cea280a6d2d 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -15,6 +15,8 @@
* Hardware interface for audio DSP on Apollolake and GeminiLake
*/
+#include <sound/sof/ext_manifest4.h>
+#include "../ipc4-priv.h"
#include "../sof-priv.h"
#include "hda.h"
#include "../sof-audio.h"
@@ -26,108 +28,62 @@ static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = {
};
/* apollolake ops */
-const struct snd_sof_dsp_ops sof_apl_ops = {
+struct snd_sof_dsp_ops sof_apl_ops;
+EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+int sof_apl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_apl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
/* probe/remove/shutdown */
- .probe = hda_dsp_probe,
- .remove = hda_dsp_remove,
- .shutdown = hda_dsp_shutdown,
-
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
-
- /* doorbell */
- .irq_thread = hda_dsp_ipc_irq_thread,
-
- /* ipc */
- .send_msg = hda_dsp_ipc_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
- .get_window_offset = hda_dsp_ipc_get_window_offset,
-
- .ipc_msg_data = hda_ipc_msg_data,
- .set_stream_data_offset = hda_set_stream_data_offset,
-
- /* machine driver */
- .machine_select = hda_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = hda_set_mach_params,
+ sof_apl_ops.shutdown = hda_dsp_shutdown;
+
+ if (sdev->pdata->ipc_type == SOF_IPC) {
+ /* doorbell */
+ sof_apl_ops.irq_thread = hda_dsp_ipc_irq_thread;
+
+ /* ipc */
+ sof_apl_ops.send_msg = hda_dsp_ipc_send_msg;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ /* doorbell */
+ sof_apl_ops.irq_thread = hda_dsp_ipc4_irq_thread;
+
+ /* ipc */
+ sof_apl_ops.send_msg = hda_dsp_ipc4_send_msg;
+ }
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_apl_ops);
/* debug */
- .debug_map = apl_dsp_debugfs,
- .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs),
- .dbg_dump = hda_dsp_dump,
- .ipc_dump = hda_ipc_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
- /* stream callbacks */
- .pcm_open = hda_dsp_pcm_open,
- .pcm_close = hda_dsp_pcm_close,
- .pcm_hw_params = hda_dsp_pcm_hw_params,
- .pcm_hw_free = hda_dsp_stream_hw_free,
- .pcm_trigger = hda_dsp_pcm_trigger,
- .pcm_pointer = hda_dsp_pcm_pointer,
- .pcm_ack = hda_dsp_pcm_ack,
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_raw,
+ sof_apl_ops.debug_map = apl_dsp_debugfs;
+ sof_apl_ops.debug_map_count = ARRAY_SIZE(apl_dsp_debugfs);
+ sof_apl_ops.ipc_dump = hda_ipc_dump;
/* firmware run */
- .run = hda_dsp_cl_boot_firmware,
+ sof_apl_ops.run = hda_dsp_cl_boot_firmware;
/* pre/post fw run */
- .pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = hda_dsp_post_fw_run,
-
- /* parse platform specific extended manifest */
- .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+ sof_apl_ops.post_fw_run = hda_dsp_post_fw_run;
/* dsp core get/put */
- .core_get = hda_dsp_core_get,
-
- /* trace callback */
- .trace_init = hda_dsp_trace_init,
- .trace_release = hda_dsp_trace_release,
- .trace_trigger = hda_dsp_trace_trigger,
-
- /* client ops */
- .register_ipc_clients = hda_register_clients,
- .unregister_ipc_clients = hda_unregister_clients,
-
- /* DAI drivers */
- .drv = skl_dai,
- .num_drv = SOF_SKL_NUM_DAIS,
-
- /* PM */
- .suspend = hda_dsp_suspend,
- .resume = hda_dsp_resume,
- .runtime_suspend = hda_dsp_runtime_suspend,
- .runtime_resume = hda_dsp_runtime_resume,
- .runtime_idle = hda_dsp_runtime_idle,
- .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
- .set_power_state = hda_dsp_set_power_state,
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- .dsp_arch_ops = &sof_xtensa_arch_ops,
+ sof_apl_ops.core_get = hda_dsp_core_get;
+
+ return 0;
};
-EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_apl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
const struct sof_intel_dsp_desc apl_chip_info = {
/* Apollolake */
@@ -139,9 +95,12 @@ const struct sof_intel_dsp_desc apl_chip_info = {
.ipc_ack = HDA_DSP_REG_HIPCIE,
.ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE,
.ipc_ctl = HDA_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 150,
.ssp_count = APL_SSP_COUNT,
.ssp_base_offset = APL_SSP_BASE_OFFSET,
.quirks = SOF_INTEL_PROCEN_FMT_QUIRK,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_1_5_PLUS,
};
EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index fb9682b2fe32..26df780c702e 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -567,7 +567,7 @@ static struct snd_soc_dai_driver bdw_dai[] = {
};
/* broadwell ops */
-static const struct snd_sof_dsp_ops sof_bdw_ops = {
+static struct snd_sof_dsp_ops sof_bdw_ops = {
/*Device init */
.probe = bdw_probe,
@@ -591,7 +591,6 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
/* ipc */
.send_msg = bdw_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = bdw_get_mailbox_offset,
.get_window_offset = bdw_get_window_offset,
@@ -614,9 +613,6 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
.pcm_open = sof_stream_pcm_open,
.pcm_close = sof_stream_pcm_close,
- /* Module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -637,6 +633,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
static const struct sof_intel_dsp_desc bdw_chip_info = {
.cores_num = 1,
.host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_BROADWELL,
};
static const struct sof_dev_desc sof_acpi_broadwell_desc = {
@@ -646,9 +643,17 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = 0,
.chip_info = &bdw_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-bdw.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-bdw.ri",
+ },
.nocodec_tplg_filename = "sof-bdw-nocodec.tplg",
.ops = &sof_bdw_ops,
};
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index bb84a4aa587a..4ed8381eceda 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -216,7 +216,7 @@ irq:
}
/* baytrail ops */
-static const struct snd_sof_dsp_ops sof_byt_ops = {
+static struct snd_sof_dsp_ops sof_byt_ops = {
/* device init */
.probe = byt_acpi_probe,
.remove = byt_remove,
@@ -245,7 +245,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
/* ipc */
.send_msg = atom_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = atom_get_mailbox_offset,
.get_window_offset = atom_get_window_offset,
@@ -268,9 +267,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.pcm_open = sof_stream_pcm_open,
.pcm_close = sof_stream_pcm_close,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -295,10 +291,11 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
static const struct sof_intel_dsp_desc byt_chip_info = {
.cores_num = 1,
.host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_BAYTRAIL,
};
/* cherrytrail and braswell ops */
-static const struct snd_sof_dsp_ops sof_cht_ops = {
+static struct snd_sof_dsp_ops sof_cht_ops = {
/* device init */
.probe = byt_acpi_probe,
.remove = byt_remove,
@@ -327,7 +324,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
/* ipc */
.send_msg = atom_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = atom_get_mailbox_offset,
.get_window_offset = atom_get_window_offset,
@@ -350,9 +346,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.pcm_open = sof_stream_pcm_open,
.pcm_close = sof_stream_pcm_close,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -378,6 +371,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
static const struct sof_intel_dsp_desc cht_chip_info = {
.cores_num = 1,
.host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_BAYTRAIL,
};
/* BYTCR uses different IRQ index */
@@ -388,9 +382,17 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = {
.resindex_imr_base = 2,
.irqindex_host_ipc = 0,
.chip_info = &byt_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-byt.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-byt.ri",
+ },
.nocodec_tplg_filename = "sof-byt-nocodec.tplg",
.ops = &sof_byt_ops,
};
@@ -402,9 +404,17 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = {
.resindex_imr_base = 2,
.irqindex_host_ipc = 5,
.chip_info = &byt_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-byt.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-byt.ri",
+ },
.nocodec_tplg_filename = "sof-byt-nocodec.tplg",
.ops = &sof_byt_ops,
};
@@ -416,9 +426,17 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = {
.resindex_imr_base = 2,
.irqindex_host_ipc = 5,
.chip_info = &cht_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-cht.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-cht.ri",
+ },
.nocodec_tplg_filename = "sof-cht-nocodec.tplg",
.ops = &sof_cht_ops,
};
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 6a96470b967f..cd6e5f8a5eb4 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -15,6 +15,9 @@
* Hardware interface for audio DSP on Cannonlake.
*/
+#include <sound/sof/ext_manifest4.h>
+#include <sound/sof/ipc4/header.h>
+#include "../ipc4-priv.h"
#include "../ops.h"
#include "hda.h"
#include "hda-ipc.h"
@@ -29,6 +32,68 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = {
static void cnl_ipc_host_done(struct snd_sof_dev *sdev);
static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev);
+irqreturn_t cnl_ipc4_irq_thread(int irq, void *context)
+{
+ struct sof_ipc4_msg notification_data = {{ 0 }};
+ struct snd_sof_dev *sdev = context;
+ bool ipc_irq = false;
+ u32 hipcida, hipctdr;
+
+ hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
+ if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) {
+ /* DSP received the message */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR,
+ CNL_DSP_REG_HIPCCTL,
+ CNL_DSP_REG_HIPCCTL_DONE, 0);
+ cnl_ipc_dsp_done(sdev);
+
+ ipc_irq = true;
+ }
+
+ hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
+ if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) {
+ /* Message from DSP (reply or notification) */
+ u32 hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+ CNL_DSP_REG_HIPCTDD);
+ u32 primary = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
+ u32 extension = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
+
+ if (primary & SOF_IPC4_MSG_DIR_MASK) {
+ /* Reply received */
+ struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+ data->primary = primary;
+ data->extension = extension;
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ snd_sof_ipc_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, data->primary);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ /* Notification received */
+ notification_data.primary = primary;
+ notification_data.extension = extension;
+
+ sdev->ipc->msg.rx_data = &notification_data;
+ snd_sof_ipc_msgs_rx(sdev);
+ sdev->ipc->msg.rx_data = NULL;
+ }
+
+ /* Let DSP know that we have finished processing the message */
+ cnl_ipc_host_done(sdev);
+
+ ipc_irq = true;
+ }
+
+ if (!ipc_irq)
+ /* This interrupt is not shared so no need to return IRQ_NONE. */
+ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+ return IRQ_HANDLED;
+}
+
irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
@@ -176,6 +241,22 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
return false;
}
+int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ /* send the message via mailbox */
+ if (msg_data->data_size)
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+ msg_data->data_size);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, msg_data->extension);
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
+ msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
+
+ return 0;
+}
+
int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
@@ -244,108 +325,63 @@ void cnl_ipc_dump(struct snd_sof_dev *sdev)
}
/* cannonlake ops */
-const struct snd_sof_dsp_ops sof_cnl_ops = {
+struct snd_sof_dsp_ops sof_cnl_ops;
+EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+int sof_cnl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_cnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
/* probe/remove/shutdown */
- .probe = hda_dsp_probe,
- .remove = hda_dsp_remove,
- .shutdown = hda_dsp_shutdown,
+ sof_cnl_ops.shutdown = hda_dsp_shutdown;
+
+ /* ipc */
+ if (sdev->pdata->ipc_type == SOF_IPC) {
+ /* doorbell */
+ sof_cnl_ops.irq_thread = cnl_ipc_irq_thread;
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
+ /* ipc */
+ sof_cnl_ops.send_msg = cnl_ipc_send_msg;
+ }
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
+ if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_fw_data *ipc4_data;
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
+ sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
- /* doorbell */
- .irq_thread = cnl_ipc_irq_thread,
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
- /* ipc */
- .send_msg = cnl_ipc_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
- .get_window_offset = hda_dsp_ipc_get_window_offset,
+ /* doorbell */
+ sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread;
- .ipc_msg_data = hda_ipc_msg_data,
- .set_stream_data_offset = hda_set_stream_data_offset,
+ /* ipc */
+ sof_cnl_ops.send_msg = cnl_ipc4_send_msg;
+ }
- /* machine driver */
- .machine_select = hda_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = hda_set_mach_params,
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_cnl_ops);
/* debug */
- .debug_map = cnl_dsp_debugfs,
- .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs),
- .dbg_dump = hda_dsp_dump,
- .ipc_dump = cnl_ipc_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
- /* stream callbacks */
- .pcm_open = hda_dsp_pcm_open,
- .pcm_close = hda_dsp_pcm_close,
- .pcm_hw_params = hda_dsp_pcm_hw_params,
- .pcm_hw_free = hda_dsp_stream_hw_free,
- .pcm_trigger = hda_dsp_pcm_trigger,
- .pcm_pointer = hda_dsp_pcm_pointer,
- .pcm_ack = hda_dsp_pcm_ack,
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_raw,
+ sof_cnl_ops.debug_map = cnl_dsp_debugfs;
+ sof_cnl_ops.debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs);
+ sof_cnl_ops.ipc_dump = cnl_ipc_dump;
/* pre/post fw run */
- .pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = hda_dsp_post_fw_run,
+ sof_cnl_ops.post_fw_run = hda_dsp_post_fw_run;
- /* parse platform specific extended manifest */
- .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+ /* firmware run */
+ sof_cnl_ops.run = hda_dsp_cl_boot_firmware;
/* dsp core get/put */
- .core_get = hda_dsp_core_get,
+ sof_cnl_ops.core_get = hda_dsp_core_get;
- /* firmware run */
- .run = hda_dsp_cl_boot_firmware,
-
- /* trace callback */
- .trace_init = hda_dsp_trace_init,
- .trace_release = hda_dsp_trace_release,
- .trace_trigger = hda_dsp_trace_trigger,
-
- /* client ops */
- .register_ipc_clients = hda_register_clients,
- .unregister_ipc_clients = hda_unregister_clients,
-
- /* DAI drivers */
- .drv = skl_dai,
- .num_drv = SOF_SKL_NUM_DAIS,
-
- /* PM */
- .suspend = hda_dsp_suspend,
- .resume = hda_dsp_resume,
- .runtime_suspend = hda_dsp_runtime_suspend,
- .runtime_resume = hda_dsp_runtime_resume,
- .runtime_idle = hda_dsp_runtime_idle,
- .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
- .set_power_state = hda_dsp_set_power_state,
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- .dsp_arch_ops = &sof_xtensa_arch_ops,
+ return 0;
};
-EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_cnl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
const struct sof_intel_dsp_desc cnl_chip_info = {
/* Cannonlake */
@@ -357,12 +393,15 @@ const struct sof_intel_dsp_desc cnl_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = CNL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_1_8,
};
EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -383,11 +422,14 @@ const struct sof_intel_dsp_desc jsl_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_2_0,
};
EXPORT_SYMBOL_NS(jsl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
new file mode 100644
index 000000000000..b2326396c870
--- /dev/null
+++ b/sound/soc/sof/intel/hda-common-ops.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+
+/*
+ * common ops for SKL+ HDAudio platforms
+ */
+
+#include "../sof-priv.h"
+#include "hda.h"
+#include "../sof-audio.h"
+
+struct snd_sof_dsp_ops sof_hda_common_ops = {
+ /* probe/remove/shutdown */
+ .probe = hda_dsp_probe,
+ .remove = hda_dsp_remove,
+
+ /* Register IO */
+ .write = sof_io_write,
+ .read = sof_io_read,
+ .write64 = sof_io_write64,
+ .read64 = sof_io_read64,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
+ /* ipc */
+ .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
+ .get_window_offset = hda_dsp_ipc_get_window_offset,
+
+ .ipc_msg_data = hda_ipc_msg_data,
+ .set_stream_data_offset = hda_set_stream_data_offset,
+
+ /* machine driver */
+ .machine_select = hda_machine_select,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+ .set_mach_params = hda_set_mach_params,
+
+ /* debug */
+ .dbg_dump = hda_dsp_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+ /* stream callbacks */
+ .pcm_open = hda_dsp_pcm_open,
+ .pcm_close = hda_dsp_pcm_close,
+ .pcm_hw_params = hda_dsp_pcm_hw_params,
+ .pcm_hw_free = hda_dsp_stream_hw_free,
+ .pcm_trigger = hda_dsp_pcm_trigger,
+ .pcm_pointer = hda_dsp_pcm_pointer,
+ .pcm_ack = hda_dsp_pcm_ack,
+
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_raw,
+
+ /* pre/post fw run */
+ .pre_fw_run = hda_dsp_pre_fw_run,
+
+ /* firmware run */
+ .run = hda_dsp_cl_boot_firmware,
+
+ /* parse platform specific extended manifest */
+ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+
+ /* dsp core get/put */
+
+ /* trace callback */
+ .trace_init = hda_dsp_trace_init,
+ .trace_release = hda_dsp_trace_release,
+ .trace_trigger = hda_dsp_trace_trigger,
+
+ /* client ops */
+ .register_ipc_clients = hda_register_clients,
+ .unregister_ipc_clients = hda_unregister_clients,
+
+ /* DAI drivers */
+ .drv = skl_dai,
+ .num_drv = SOF_SKL_NUM_DAIS,
+
+ /* PM */
+ .suspend = hda_dsp_suspend,
+ .resume = hda_dsp_resume,
+ .runtime_suspend = hda_dsp_runtime_suspend,
+ .runtime_resume = hda_dsp_runtime_resume,
+ .runtime_idle = hda_dsp_runtime_idle,
+ .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
+ .set_power_state = hda_dsp_set_power_state,
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
+};
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index f9cb9f1f0237..9823230d2ef4 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -50,8 +50,8 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
}
static struct hdac_ext_stream *
- hda_link_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
+hda_link_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct sof_intel_hda_stream *hda_stream;
@@ -128,6 +128,40 @@ static struct hdac_ext_stream *
return res;
}
+static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
+ struct hdac_stream *hstream,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_soc_dai *codec_dai,
+ bool trigger_suspend_stop)
+{
+ struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ struct hdac_bus *bus = hstream->bus;
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_link *link;
+ int stream_tag;
+
+ link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
+ if (!link)
+ return -EINVAL;
+
+ if (trigger_suspend_stop)
+ snd_hdac_ext_link_stream_clear(hext_stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ }
+ snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+ snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ hext_stream->link_prepared = 0;
+
+ /* free the host DMA channel reserved by hostless streams */
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ hda_stream->host_reserved = 0;
+
+ return 0;
+}
+
static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
struct hda_pipe_params *params)
{
@@ -162,61 +196,28 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
return 0;
}
-static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
- struct snd_soc_dapm_widget *w,
- int channel, bool widget_setup)
-{
- struct snd_sof_dai_config_data data;
-
- data.dai_data = channel;
-
- /* set up/free DAI widget and send DAI_CONFIG IPC */
- if (widget_setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
-}
-
-static int hda_link_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_bus *bus = hstream->bus;
struct hdac_ext_stream *hext_stream;
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 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 sof_intel_hda_stream *hda_stream;
struct hda_pipe_params p_params = {0};
- struct snd_soc_dapm_widget *w;
+ struct hdac_bus *bus = hstream->bus;
struct hdac_ext_link *link;
- int stream_tag;
- int ret;
/* get stored dma data if resuming from system suspend */
- hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+ hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
if (!hext_stream) {
hext_stream = hda_link_stream_assign(bus, substream);
if (!hext_stream)
return -EBUSY;
- snd_soc_dai_set_dma_data(dai, substream, (void *)hext_stream);
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
}
- stream_tag = hdac_stream(hext_stream)->stream_tag;
-
- hda_stream = hstream_to_sof_hda_stream(hext_stream);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
-
- /* set up the DAI widget and send the DAI_CONFIG with the new tag */
- ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
- if (ret < 0)
- return ret;
-
link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
if (!link)
return -EINVAL;
@@ -239,26 +240,118 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
return hda_link_dma_params(hext_stream, &p_params);
}
-static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int hda_link_dma_prepare(struct snd_pcm_substream *substream)
{
- struct hdac_ext_stream *hext_stream =
- snd_soc_dai_get_dma_data(dai, substream);
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
int stream = substream->stream;
- if (hext_stream->link_prepared)
+ return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
+}
+
+static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 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 hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ int ret;
+
+ dev_dbg(cpu_dai->dev, "%s: cmd=%d\n", __func__, cmd);
+ if (!hext_stream)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_link_stream_start(hext_stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, true);
+ if (ret < 0)
+ return ret;
+
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_hdac_ext_link_stream_clear(hext_stream);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ 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 hdac_ext_stream *hext_stream;
+
+ hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ if (!hext_stream)
+ return 0;
+
+ return hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, false);
+}
+
+static int hda_dai_widget_update(struct snd_soc_dapm_widget *w,
+ int channel, bool widget_setup)
+{
+ struct snd_sof_dai_config_data data;
+
+ data.dai_data = channel;
+
+ /* set up/free DAI widget and send DAI_CONFIG IPC */
+ if (widget_setup)
+ return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
+
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+}
+
+static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *hext_stream;
+ struct snd_soc_dapm_widget *w;
+ int stream_tag;
+
+ hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!hext_stream)
+ return -EINVAL;
+
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
+
+ w = snd_soc_dai_get_widget(dai, substream->stream);
+
+ /* set up the DAI widget and send the DAI_CONFIG with the new tag */
+ return hda_dai_widget_update(w, stream_tag - 1, true);
+}
+
+static int hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *hext_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+ int ret;
+
+ if (hext_stream && hext_stream->link_prepared)
return 0;
- dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
+ ret = hda_link_dma_hw_params(substream, params);
+ if (ret < 0)
+ return ret;
- return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
- dai);
+ return hda_dai_hw_params_update(substream, params, dai);
}
-static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
+
+static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
@@ -276,132 +369,134 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
return ret;
}
-static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static int ipc3_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct hdac_ext_stream *hext_stream =
snd_soc_dai_get_dma_data(dai, substream);
- struct sof_intel_hda_stream *hda_stream;
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dapm_widget *w;
- struct hdac_ext_link *link;
- struct hdac_stream *hstream;
- struct hdac_bus *bus;
- int stream_tag;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int stream = substream->stream;
int ret;
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = asoc_substream_to_rtd(substream);
+ if (hext_stream && hext_stream->link_prepared)
+ return 0;
- link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
- if (!link)
- return -EINVAL;
+ dev_dbg(sdev->dev, "%s: prepare stream dir %d\n", __func__, substream->stream);
- hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ ret = hda_link_dma_prepare(substream);
+ if (ret < 0)
+ return ret;
+
+ return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai);
+}
- dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
+static int hda_dai_hw_free_ipc(int stream, /* direction */
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_widget *w;
+
+ w = snd_soc_dai_get_widget(dai, stream);
+
+ /* free the link DMA channel in the FW and the DAI widget */
+ return hda_dai_widget_update(w, DMA_CHAN_INVALID, false);
+}
+
+static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_widget *w;
+ int ret;
+
+ ret = hda_link_dma_trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
w = snd_soc_dai_get_widget(dai, substream->stream);
+ dev_dbg(dai->dev, "%s: cmd=%d\n", __func__, cmd);
switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_link_stream_start(hext_stream);
- break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- snd_hdac_ext_link_stream_clear(hext_stream);
-
/*
* free DAI widget during stop/suspend to keep widget use_count's balanced.
*/
- ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
+ ret = hda_dai_hw_free_ipc(substream->stream, dai);
if (ret < 0)
return ret;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(hext_stream)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
- }
-
- hext_stream->link_prepared = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_hdac_ext_link_stream_clear(hext_stream);
-
- ret = hda_link_dai_config_pause_push_ipc(w);
+ ret = hda_dai_config_pause_push_ipc(w);
if (ret < 0)
return ret;
break;
+
default:
- return -EINVAL;
+ break;
}
return 0;
}
-static int hda_link_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
- unsigned int stream_tag;
- struct sof_intel_hda_stream *hda_stream;
- struct hdac_bus *bus;
- struct hdac_ext_link *link;
- struct hdac_stream *hstream;
- struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *hext_stream;
- struct snd_soc_dapm_widget *w;
int ret;
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = asoc_substream_to_rtd(substream);
- hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+ ret = hda_link_dma_hw_free(substream);
+ if (ret < 0)
+ return ret;
- if (!hext_stream) {
- dev_dbg(dai->dev,
- "%s: hext_stream is not assigned\n", __func__);
- return -EINVAL;
- }
+ return hda_dai_hw_free_ipc(substream->stream, dai);
+}
- hda_stream = hstream_to_sof_hda_stream(hext_stream);
+static const struct snd_soc_dai_ops ipc3_hda_dai_ops = {
+ .hw_params = hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = ipc3_hda_dai_trigger,
+ .prepare = ipc3_hda_dai_prepare,
+};
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
+static int hda_dai_suspend(struct hdac_bus *bus)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *s;
+ int ret;
- /* free the link DMA channel in the FW and the DAI widget */
- ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
- if (ret < 0)
- return ret;
+ /* set internal flag for BE */
+ list_for_each_entry(s, &bus->stream_list, list) {
- link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
- if (!link)
- return -EINVAL;
+ hext_stream = stream_to_hdac_ext_stream(s);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(hext_stream)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ /*
+ * clear stream. This should already be taken care for running
+ * streams when the SUSPEND trigger is called. But paused
+ * streams do not get suspended, so this needs to be done
+ * explicitly during suspend.
+ */
+ if (hext_stream->link_substream) {
+ struct snd_soc_dai *cpu_dai;
+ struct snd_soc_dai *codec_dai;
+
+ 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);
+
+ ret = hda_link_dma_cleanup(hext_stream->link_substream, s,
+ cpu_dai, codec_dai, false);
+ if (ret < 0)
+ return ret;
+
+ /* for consistency with TRIGGER_SUSPEND we free DAI resources */
+ ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai);
+ if (ret < 0)
+ return ret;
+ }
}
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
- hext_stream->link_prepared = 0;
-
- /* free the host DMA channel reserved by hostless streams */
- hda_stream->host_reserved = 0;
-
return 0;
}
-
-static const struct snd_soc_dai_ops hda_link_dai_ops = {
- .hw_params = hda_link_hw_params,
- .hw_free = hda_link_hw_free,
- .trigger = hda_link_pcm_trigger,
- .prepare = hda_link_pcm_prepare,
-};
-
#endif
/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
@@ -414,10 +509,7 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
{
struct snd_soc_dapm_widget *w;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
+ w = snd_soc_dai_get_widget(dai, substream->stream);
if (setup)
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
@@ -478,8 +570,8 @@ static int ssp_dai_prepare(struct snd_pcm_substream *substream,
return ssp_dai_setup(substream, dai, true);
}
-static int ssp_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
{
if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
return 0;
@@ -507,15 +599,39 @@ static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
kfree(dma_data);
}
-static const struct snd_soc_dai_ops ssp_dai_ops = {
+static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
.startup = ssp_dai_startup,
.hw_params = ssp_dai_hw_params,
.prepare = ssp_dai_prepare,
- .trigger = ssp_dai_trigger,
+ .trigger = ipc3_ssp_dai_trigger,
.hw_free = ssp_dai_hw_free,
.shutdown = ssp_dai_shutdown,
};
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
+{
+ int i;
+
+ switch (sdev->pdata->ipc_type) {
+ case SOF_IPC:
+ for (i = 0; i < ops->num_drv; i++) {
+ if (strstr(ops->drv[i].name, "SSP")) {
+ ops->drv[i].ops = &ipc3_ssp_dai_ops;
+ continue;
+ }
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ if (strstr(ops->drv[i].name, "iDisp") ||
+ strstr(ops->drv[i].name, "Analog") ||
+ strstr(ops->drv[i].name, "Digital"))
+ ops->drv[i].ops = &ipc3_hda_dai_ops;
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
/*
* common dai driver for skl+ platforms.
* some products who use this DAI array only physically have a subset of
@@ -524,7 +640,6 @@ static const struct snd_soc_dai_ops ssp_dai_ops = {
struct snd_soc_dai_driver skl_dai[] = {
{
.name = "SSP0 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -536,7 +651,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP1 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -548,7 +662,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP2 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -560,7 +673,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP3 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -572,7 +684,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP4 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -584,7 +695,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP5 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -611,7 +721,6 @@ struct snd_soc_dai_driver skl_dai[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
{
.name = "iDisp1 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -619,7 +728,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp2 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -627,7 +735,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp3 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -635,7 +742,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp4 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -643,7 +749,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Analog CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -655,7 +760,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Digital CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -667,7 +771,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Alt Analog CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -679,3 +782,22 @@ struct snd_soc_dai_driver skl_dai[] = {
},
#endif
};
+
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
+{
+ /*
+ * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
+ * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
+ * 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)
+ int ret;
+
+ ret = hda_dai_suspend(sof_to_bus(sdev));
+ if (ret < 0)
+ return ret;
+#endif
+
+ return 0;
+}
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 8ddde60c56b3..000ea906670c 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -363,9 +363,8 @@ static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
pm_gate.flags = flags;
/* send pm_gate ipc to dsp */
- return sof_ipc_tx_message_no_pm(sdev->ipc, pm_gate.hdr.cmd,
- &pm_gate, sizeof(pm_gate), &reply,
- sizeof(reply));
+ return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
+ &reply, sizeof(reply));
}
static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
@@ -433,7 +432,7 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
* when the DSP enters D0I3 while the system is in S0
* for debug purpose.
*/
- if (!sdev->dtrace_is_supported ||
+ if (!sdev->fw_trace_is_supported ||
!hda_enable_trace_D0I3_S0 ||
sdev->system_suspend_target != SOF_SUSPEND_NONE)
flags = HDA_PM_NO_DMA_TRACE;
@@ -895,44 +894,14 @@ int hda_dsp_shutdown(struct snd_sof_dev *sdev)
int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- struct hdac_bus *bus = sof_to_bus(sdev);
- struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *hext_stream;
- struct hdac_ext_link *link;
- struct hdac_stream *s;
- const char *name;
- int stream_tag;
-
- /* set internal flag for BE */
- list_for_each_entry(s, &bus->stream_list, list) {
- hext_stream = stream_to_hdac_ext_stream(s);
-
- /*
- * clear stream. This should already be taken care for running
- * streams when the SUSPEND trigger is called. But paused
- * streams do not get suspended, so this needs to be done
- * explicitly during suspend.
- */
- if (hext_stream->link_substream) {
- rtd = asoc_substream_to_rtd(hext_stream->link_substream);
- name = asoc_rtd_to_codec(rtd, 0)->component->name;
- link = snd_hdac_ext_bus_get_link(bus, name);
- if (!link)
- return -EINVAL;
-
- hext_stream->link_prepared = 0;
+ int ret;
- if (hdac_stream(hext_stream)->direction ==
- SNDRV_PCM_STREAM_CAPTURE)
- continue;
+ /* make sure all DAI resources are freed */
+ ret = hda_dsp_dais_suspend(sdev);
+ if (ret < 0)
+ dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__);
- stream_tag = hdac_stream(hext_stream)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
- }
- }
-#endif
- return 0;
+ return ret;
}
void hda_dsp_d0i3_work(struct work_struct *work)
@@ -985,8 +954,7 @@ int hda_dsp_core_get(struct snd_sof_dev *sdev, int core)
return 0;
/* Now notify DSP for secondary cores */
- ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
- &pm_core_config, sizeof(pm_core_config),
+ ret = sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
if (ret < 0) {
dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n",
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 0395638c43ae..f08011249955 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -15,6 +15,7 @@
* Hardware interface for generic Intel audio DSP HDA IP
*/
+#include <sound/sof/ipc4/header.h>
#include "../ops.h"
#include "hda.h"
@@ -65,6 +66,22 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
+int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ /* send the message via mailbox */
+ if (msg_data->data_size)
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr,
+ msg_data->data_size);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension);
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
+ msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);
+
+ return 0;
+}
+
void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
@@ -100,6 +117,71 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
}
}
+irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context)
+{
+ struct sof_ipc4_msg notification_data = {{ 0 }};
+ struct snd_sof_dev *sdev = context;
+ bool ipc_irq = false;
+ u32 hipcie, hipct;
+
+ hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
+ if (hipcie & HDA_DSP_REG_HIPCIE_DONE) {
+ /* DSP received the message */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,
+ HDA_DSP_REG_HIPCCTL_DONE, 0);
+ hda_dsp_ipc_dsp_done(sdev);
+
+ ipc_irq = true;
+ }
+
+ hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
+ if (hipct & HDA_DSP_REG_HIPCT_BUSY) {
+ /* Message from DSP (reply or notification) */
+ u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
+ HDA_DSP_REG_HIPCTE);
+ u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;
+ u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;
+
+ /* mask BUSY interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL,
+ HDA_DSP_REG_HIPCCTL_BUSY, 0);
+
+ if (primary & SOF_IPC4_MSG_DIR_MASK) {
+ /* Reply received */
+ struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data;
+
+ data->primary = primary;
+ data->extension = extension;
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ snd_sof_ipc_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, data->primary);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ } else {
+ /* Notification received */
+
+ notification_data.primary = primary;
+ notification_data.extension = extension;
+ sdev->ipc->msg.rx_data = &notification_data;
+ snd_sof_ipc_msgs_rx(sdev);
+ sdev->ipc->msg.rx_data = NULL;
+ }
+
+ /* Let DSP know that we have finished processing the message */
+ hda_dsp_ipc_host_done(sdev);
+
+ ipc_irq = true;
+ }
+
+ if (!ipc_irq)
+ /* This interrupt is not shared so no need to return IRQ_NONE. */
+ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
+
+ return IRQ_HANDLED;
+}
+
/* IPC handler thread */
irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
{
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 2ac5d9d0719b..64290125d7cd 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -24,8 +24,6 @@
#include "../sof-priv.h"
#include "hda.h"
-#define HDA_CL_STREAM_FORMAT 0x40
-
static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -43,9 +41,9 @@ static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
}
}
-static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
- unsigned int size, struct snd_dma_buffer *dmab,
- int direction)
+struct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
+ unsigned int size, struct snd_dma_buffer *dmab,
+ int direction)
{
struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
@@ -101,14 +99,14 @@ out_put:
* status on core 1, so power up core 1 also momentarily, keep it in
* reset/stall and then turn it off
*/
-static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
+static int cl_dsp_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;
- unsigned int status;
+ unsigned int status, target_status;
+ u32 flags, ipc_hdr, j;
unsigned long mask;
char *dump_msg;
- u32 flags, j;
int ret;
/* step 1: power up corex */
@@ -121,10 +119,12 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
hda_ssp_set_cbp_cfp(sdev);
- /* step 2: purge FW request */
- snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req,
- chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW |
- ((stream_tag - 1) << 9)));
+ /* step 2: Send ROM_CONTROL command (stream_tag is ignored for IMR boot) */
+ ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL;
+ if (!imr_boot)
+ ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr);
/* step 3: unset core 0 reset state & unstall/run core 0 */
ret = hda_dsp_core_run(sdev, BIT(0));
@@ -171,11 +171,20 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
/* step 6: enable IPC interrupts */
hda_dsp_ipc_int_enable(sdev);
- /* step 7: wait for ROM init */
+ /*
+ * step 7:
+ * - Cold/Full boot: wait for ROM init to proceed to download the firmware
+ * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
+ */
+ if (imr_boot)
+ target_status = HDA_DSP_ROM_FW_ENTERED;
+ else
+ target_status = HDA_DSP_ROM_INIT;
+
ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS, status,
+ chip->rom_status_reg, status,
((status & HDA_DSP_ROM_STS_MASK)
- == HDA_DSP_ROM_INIT),
+ == target_status),
HDA_DSP_REG_POLL_INTERVAL_US,
chip->rom_init_timeout *
USEC_PER_MSEC);
@@ -190,8 +199,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev,
- "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
- __func__);
+ "%s: timeout with rom_status_reg (%#x) read\n",
+ __func__, chip->rom_status_reg);
err:
flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
@@ -236,8 +245,8 @@ static int cl_trigger(struct snd_sof_dev *sdev,
}
}
-static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
- struct hdac_ext_stream *hext_stream)
+int hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
+ struct hdac_ext_stream *hext_stream)
{
struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
@@ -268,8 +277,10 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
return ret;
}
-static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
+int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
unsigned int reg;
int ret, status;
@@ -280,7 +291,7 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_str
}
status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS, reg,
+ chip->rom_status_reg, reg,
((reg & HDA_DSP_ROM_STS_MASK)
== HDA_DSP_ROM_FW_ENTERED),
HDA_DSP_REG_POLL_INTERVAL_US,
@@ -293,8 +304,8 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_str
if (status < 0) {
dev_err(sdev->dev,
- "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n",
- __func__);
+ "%s: timeout with rom_status_reg (%#x) read\n",
+ __func__, chip->rom_status_reg);
}
ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
@@ -313,6 +324,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
struct hdac_ext_stream *iccmax_stream;
struct hdac_bus *bus = sof_to_bus(sdev);
struct firmware stripped_firmware;
+ struct snd_dma_buffer dmab_bdl;
int ret, ret1;
u8 original_gb;
@@ -327,8 +339,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset;
/* prepare capture stream for ICCMAX */
- iccmax_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
- &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE);
+ iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
+ &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE);
if (IS_ERR(iccmax_stream)) {
dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n");
return PTR_ERR(iccmax_stream);
@@ -340,7 +352,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
* Perform iccmax stream cleanup. This should be done even if firmware loading fails.
* If the cleanup also fails, we return the initial error
*/
- ret1 = cl_cleanup(sdev, &sdev->dmab_bdl, iccmax_stream);
+ ret1 = hda_cl_cleanup(sdev, &dmab_bdl, iccmax_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n");
@@ -357,32 +369,11 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
static int hda_dsp_boot_imr(struct snd_sof_dev *sdev)
{
- struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
- const struct sof_intel_dsp_desc *chip = hda->desc;
- unsigned long mask;
- u32 j;
int ret;
- /* power up & unstall/run the cores to run the firmware */
- ret = hda_dsp_enable_core(sdev, chip->init_core_mask);
- if (ret < 0) {
- dev_err(sdev->dev, "dsp core start failed %d\n", ret);
- return -EIO;
- }
-
- /* set enabled cores mask and increment ref count for cores in init_core_mask */
- sdev->enabled_cores_mask |= chip->init_core_mask;
- mask = sdev->enabled_cores_mask;
- for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES)
- sdev->dsp_core_ref_count[j]++;
-
- hda_ssp_set_cbp_cfp(sdev);
-
- /* enable IPC interrupts */
- hda_dsp_ipc_int_enable(sdev);
-
- /* process wakes */
- hda_sdw_process_wakeen(sdev);
+ ret = cl_dsp_init(sdev, 0, true);
+ if (!ret)
+ hda_sdw_process_wakeen(sdev);
return ret;
}
@@ -395,13 +386,17 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
const struct sof_intel_dsp_desc *chip_info;
struct hdac_ext_stream *hext_stream;
struct firmware stripped_firmware;
+ struct snd_dma_buffer dmab;
int ret, ret1, i;
- if ((sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) &&
- !(sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) &&
- !sdev->first_boot) {
+ if (hda->imrboot_supported && !sdev->first_boot) {
dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n");
- return hda_dsp_boot_imr(sdev);
+ hda->boot_iteration = 0;
+ ret = hda_dsp_boot_imr(sdev);
+ if (!ret)
+ return 0;
+
+ dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n");
}
chip_info = desc->chip_info;
@@ -418,14 +413,15 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
init_waitqueue_head(&sdev->boot_wait);
/* prepare DMA for code loader stream */
- hext_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
- &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK);
+ hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT,
+ stripped_firmware.size,
+ &dmab, SNDRV_PCM_STREAM_PLAYBACK);
if (IS_ERR(hext_stream)) {
dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
return PTR_ERR(hext_stream);
}
- memcpy(sdev->dmab.area, stripped_firmware.data,
+ memcpy(dmab.area, stripped_firmware.data,
stripped_firmware.size);
/* try ROM init a few times before giving up */
@@ -434,7 +430,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
"Attempting iteration %d of Core En/ROM load...\n", i);
hda->boot_iteration = i + 1;
- ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag);
+ ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag, false);
/* don't retry anymore if successful */
if (!ret)
@@ -473,7 +469,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
* Continue with code loading and firmware boot
*/
hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS;
- ret = cl_copy_fw(sdev, hext_stream);
+ ret = hda_cl_copy_fw(sdev, hext_stream);
if (!ret)
dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
else
@@ -486,7 +482,7 @@ cleanup:
* This should be done even if firmware loading fails.
* If the cleanup also fails, we return the initial error
*/
- ret1 = cl_cleanup(sdev, &sdev->dmab, hext_stream);
+ ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
@@ -522,12 +518,19 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev)
int ret;
if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
ret = hda_sdw_startup(sdev);
if (ret < 0) {
dev_err(sdev->dev,
"error: could not startup SoundWire links\n");
return ret;
}
+
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
+ sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT)
+ hdev->imrboot_supported = true;
}
hda_sdw_int_enable(sdev, true);
diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c
index 755ef1d835e0..cbb9bd7770e6 100644
--- a/sound/soc/sof/intel/hda-trace.c
+++ b/sound/soc/sof/intel/hda-trace.c
@@ -36,7 +36,7 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer
return ret;
}
-int hda_dsp_trace_init(struct snd_sof_dev *sdev,
+int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -57,7 +57,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev,
* initialize capture stream, set BDL address and return corresponding
* stream tag which will be sent to the firmware by IPC message.
*/
- ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb);
+ ret = hda_dsp_trace_prepare(sdev, dmab);
if (ret < 0) {
dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret);
hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE,
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 9c97c80a7f48..bc07df1fc39f 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -406,11 +406,13 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = {
static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level)
{
+ const struct sof_intel_dsp_desc *chip;
u32 status;
int i;
+ chip = get_chip_info(sdev->pdata);
status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS);
+ chip->rom_status_reg);
for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) {
if (status == hda_dsp_rom_msg[i].code) {
@@ -456,13 +458,15 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level,
u32 flags)
{
+ const struct sof_intel_dsp_desc *chip;
char msg[128];
int len = 0;
u32 value;
int i;
+ chip = get_chip_info(sdev->pdata);
for (i = 0; i < HDA_EXT_ROM_STATUS_SIZE; i++) {
- value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_STATUS + i * 0x4);
+ value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg + i * 0x4);
len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value);
}
@@ -493,6 +497,17 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
}
}
+static bool hda_check_ipc_irq(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->check_ipc_irq)
+ return chip->check_ipc_irq(sdev);
+
+ return false;
+}
+
void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
@@ -584,14 +599,13 @@ static int hda_init(struct snd_sof_dev *sdev)
static int check_dmic_num(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
struct nhlt_acpi_table *nhlt;
int dmic_num = 0;
- nhlt = intel_nhlt_init(sdev->dev);
- if (nhlt) {
+ nhlt = hdev->nhlt;
+ if (nhlt)
dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt);
- intel_nhlt_free(nhlt);
- }
/* allow for module parameter override */
if (dmic_num_override != -1) {
@@ -611,10 +625,11 @@ static int check_dmic_num(struct snd_sof_dev *sdev)
static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
struct nhlt_acpi_table *nhlt;
int ssp_mask = 0;
- nhlt = intel_nhlt_init(sdev->dev);
+ nhlt = hdev->nhlt;
if (!nhlt)
return ssp_mask;
@@ -623,7 +638,6 @@ static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev)
if (ssp_mask)
dev_info(sdev->dev, "NHLT_DEVICE_I2S detected, ssp_mask %#x\n", ssp_mask);
}
- intel_nhlt_free(nhlt);
return ssp_mask;
}
@@ -816,7 +830,7 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
if (hda_dsp_check_stream_irq(sdev))
hda_dsp_stream_threaded_handler(irq, sdev);
- if (hda_dsp_check_ipc_irq(sdev))
+ if (hda_check_ipc_irq(sdev))
sof_ops(sdev)->irq_thread(irq, sdev);
if (hda_dsp_check_sdw_irq(sdev))
@@ -984,6 +998,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ hdev->nhlt = intel_nhlt_init(sdev->dev);
+
return 0;
free_ipc_irq:
@@ -1009,6 +1025,10 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
const struct sof_intel_dsp_desc *chip = hda->desc;
struct hdac_bus *bus = sof_to_bus(sdev);
struct pci_dev *pci = to_pci_dev(sdev->dev);
+ struct nhlt_acpi_table *nhlt = hda->nhlt;
+
+ if (nhlt)
+ intel_nhlt_free(nhlt);
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
@@ -1274,7 +1294,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
mach->mach_params.platform = dev_name(sdev->dev);
- pdata->fw_filename = pdata->desc->default_fw_filename;
+ pdata->fw_filename = pdata->desc->default_fw_filename[pdata->ipc_type];
pdata->tplg_filename = mach->sof_tplg_filename;
/*
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 05e5e158614a..3e0f7b0c586a 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -210,7 +210,9 @@
#define HDA_DSP_ROM_USER_EXCEPTION 0xBEEF0000
#define HDA_DSP_ROM_UNEXPECTED_RESET 0xDECAF000
#define HDA_DSP_ROM_NULL_FW_ENTRY 0x4c4c4e55
-#define HDA_DSP_IPC_PURGE_FW 0x01004000
+
+#define HDA_DSP_ROM_IPC_CONTROL 0x01000000
+#define HDA_DSP_ROM_IPC_PURGE_FW 0x00004000
/* various timeout values */
#define HDA_DSP_PU_TIMEOUT 50
@@ -223,8 +225,8 @@
#define HDA_DSP_REG_POLL_INTERVAL_US 500 /* 0.5 msec */
#define HDA_DSP_REG_POLL_RETRY_COUNT 50
-#define HDA_DSP_ADSPIC_IPC 1
-#define HDA_DSP_ADSPIS_IPC 1
+#define HDA_DSP_ADSPIC_IPC BIT(0)
+#define HDA_DSP_ADSPIS_IPC BIT(0)
/* Intel HD Audio General DSP Registers */
#define HDA_DSP_GEN_BASE 0x0
@@ -268,8 +270,8 @@
/* HIPCTE */
#define HDA_DSP_REG_HIPCTE_MSG_MASK 0x3FFFFFFF
-#define HDA_DSP_ADSPIC_CL_DMA 0x2
-#define HDA_DSP_ADSPIS_CL_DMA 0x2
+#define HDA_DSP_ADSPIC_CL_DMA BIT(1)
+#define HDA_DSP_ADSPIS_CL_DMA BIT(1)
/* Delay before scheduling D0i3 entry */
#define BXT_D0I3_DELAY 5000
@@ -416,6 +418,8 @@ enum sof_hda_D0_substate {
/* represents DSP HDA controller frontend - i.e. host facing control */
struct sof_intel_hda_dev {
+ bool imrboot_supported;
+
int boot_iteration;
struct hda_bus hbus;
@@ -449,6 +453,9 @@ struct sof_intel_hda_dev {
/* FW clock config, 0:HPRO, 1:LPRO */
bool clk_config_lpro;
+
+ /* Intel NHLT information */
+ struct nhlt_acpi_table *nhlt;
};
static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s)
@@ -588,6 +595,13 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir);
*/
int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev);
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev);
+int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream);
+struct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
+ unsigned int size, struct snd_dma_buffer *dmab,
+ int direction);
+int hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
+ struct hdac_ext_stream *hext_stream);
+#define HDA_CL_STREAM_FORMAT 0x40
/* pre and post fw run ops */
int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev);
@@ -644,7 +658,7 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
/*
* Trace Control.
*/
-int hda_dsp_trace_init(struct snd_sof_dev *sdev,
+int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
struct sof_ipc_dma_trace_params_ext *dtrace_params);
int hda_dsp_trace_release(struct snd_sof_dev *sdev);
int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
@@ -683,18 +697,24 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)
/* common dai driver */
extern struct snd_soc_dai_driver skl_dai[];
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);
/*
* Platform Specific HW abstraction Ops.
*/
-extern const struct snd_sof_dsp_ops sof_apl_ops;
-extern const struct snd_sof_dsp_ops sof_cnl_ops;
-extern const struct snd_sof_dsp_ops sof_tgl_ops;
-extern const struct snd_sof_dsp_ops sof_icl_ops;
+extern struct snd_sof_dsp_ops sof_hda_common_ops;
+
+extern struct snd_sof_dsp_ops sof_apl_ops;
+int sof_apl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_cnl_ops;
+int sof_cnl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_tgl_ops;
+int sof_tgl_ops_init(struct snd_sof_dev *sdev);
+extern struct snd_sof_dsp_ops sof_icl_ops;
+int sof_icl_ops_init(struct snd_sof_dev *sdev);
extern const struct sof_intel_dsp_desc apl_chip_info;
extern const struct sof_intel_dsp_desc cnl_chip_info;
-extern const struct sof_intel_dsp_desc skl_chip_info;
extern const struct sof_intel_dsp_desc icl_chip_info;
extern const struct sof_intel_dsp_desc tgl_chip_info;
extern const struct sof_intel_dsp_desc tglh_chip_info;
@@ -742,4 +762,12 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f
extern int sof_hda_position_quirk;
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops);
+
+/* IPC4 */
+irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
+int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
+int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+
#endif
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index b44a649bfc0b..f19517dffd62 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -56,11 +56,18 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev)
int ret;
if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+
ret = hda_sdw_startup(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: could not startup SoundWire links\n");
return ret;
}
+
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) &&
+ sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT)
+ hdev->imrboot_supported = true;
}
hda_sdw_int_enable(sdev, true);
@@ -88,109 +95,44 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev)
}
/* Icelake ops */
-const struct snd_sof_dsp_ops sof_icl_ops = {
- /* probe/remove/shutdown */
- .probe = hda_dsp_probe,
- .remove = hda_dsp_remove,
- .shutdown = hda_dsp_shutdown,
-
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
+struct snd_sof_dsp_ops sof_icl_ops;
+EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
+int sof_icl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_icl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
+ /* probe/remove/shutdown */
+ sof_icl_ops.shutdown = hda_dsp_shutdown;
/* doorbell */
- .irq_thread = cnl_ipc_irq_thread,
+ sof_icl_ops.irq_thread = cnl_ipc_irq_thread;
/* ipc */
- .send_msg = cnl_ipc_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
- .get_window_offset = hda_dsp_ipc_get_window_offset,
-
- .ipc_msg_data = hda_ipc_msg_data,
- .set_stream_data_offset = hda_set_stream_data_offset,
-
- /* machine driver */
- .machine_select = hda_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = hda_set_mach_params,
+ sof_icl_ops.send_msg = cnl_ipc_send_msg;
/* debug */
- .debug_map = icl_dsp_debugfs,
- .debug_map_count = ARRAY_SIZE(icl_dsp_debugfs),
- .dbg_dump = hda_dsp_dump,
- .ipc_dump = cnl_ipc_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
- /* stream callbacks */
- .pcm_open = hda_dsp_pcm_open,
- .pcm_close = hda_dsp_pcm_close,
- .pcm_hw_params = hda_dsp_pcm_hw_params,
- .pcm_hw_free = hda_dsp_stream_hw_free,
- .pcm_trigger = hda_dsp_pcm_trigger,
- .pcm_pointer = hda_dsp_pcm_pointer,
- .pcm_ack = hda_dsp_pcm_ack,
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_raw,
+ sof_icl_ops.debug_map = icl_dsp_debugfs;
+ sof_icl_ops.debug_map_count = ARRAY_SIZE(icl_dsp_debugfs);
+ sof_icl_ops.ipc_dump = cnl_ipc_dump;
/* pre/post fw run */
- .pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = icl_dsp_post_fw_run,
+ sof_icl_ops.post_fw_run = icl_dsp_post_fw_run;
- /* parse platform specific extended manifest */
- .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+ /* firmware run */
+ sof_icl_ops.run = hda_dsp_cl_boot_firmware_iccmax;
+ sof_icl_ops.stall = icl_dsp_core_stall;
/* dsp core get/put */
- .core_get = hda_dsp_core_get,
+ sof_icl_ops.core_get = hda_dsp_core_get;
- /* firmware run */
- .run = hda_dsp_cl_boot_firmware_iccmax,
- .stall = icl_dsp_core_stall,
-
- /* trace callback */
- .trace_init = hda_dsp_trace_init,
- .trace_release = hda_dsp_trace_release,
- .trace_trigger = hda_dsp_trace_trigger,
-
- /* client ops */
- .register_ipc_clients = hda_register_clients,
- .unregister_ipc_clients = hda_unregister_clients,
-
- /* DAI drivers */
- .drv = skl_dai,
- .num_drv = SOF_SKL_NUM_DAIS,
-
- /* PM */
- .suspend = hda_dsp_suspend,
- .resume = hda_dsp_resume,
- .runtime_suspend = hda_dsp_runtime_suspend,
- .runtime_resume = hda_dsp_runtime_resume,
- .runtime_idle = hda_dsp_runtime_idle,
- .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
- .set_power_state = hda_dsp_set_power_state,
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- .dsp_arch_ops = &sof_xtensa_arch_ops,
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_icl_ops);
+
+ return 0;
};
-EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_icl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
const struct sof_intel_dsp_desc icl_chip_info = {
/* Icelake */
@@ -202,11 +144,14 @@ const struct sof_intel_dsp_desc icl_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_2_0,
};
EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
index a023b3cc0af4..2de3658eb817 100644
--- a/sound/soc/sof/intel/pci-apl.c
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -27,11 +27,23 @@ static const struct sof_dev_desc bxt_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &apl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-apl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/apl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-apl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-apl-nocodec.tplg",
.ops = &sof_apl_ops,
+ .ops_init = sof_apl_ops_init,
};
static const struct sof_dev_desc glk_desc = {
@@ -42,11 +54,23 @@ static const struct sof_dev_desc glk_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &apl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-glk.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/glk",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-glk.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-glk-nocodec.tplg",
.ops = &sof_apl_ops,
+ .ops_init = sof_apl_ops_init,
};
/* PCI IDs */
diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c
index 40cf1cd00042..87e587aef9c9 100644
--- a/sound/soc/sof/intel/pci-cnl.c
+++ b/sound/soc/sof/intel/pci-cnl.c
@@ -28,11 +28,23 @@ static const struct sof_dev_desc cnl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &cnl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-cnl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/cnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-cnl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
};
static const struct sof_dev_desc cfl_desc = {
@@ -44,11 +56,23 @@ static const struct sof_dev_desc cfl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &cnl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-cfl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/cnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-cfl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
};
static const struct sof_dev_desc cml_desc = {
@@ -60,11 +84,23 @@ static const struct sof_dev_desc cml_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &cnl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-cml.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/cnl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-cml.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
};
/* PCI IDs */
diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c
index 39c84121b313..1c7f16ce531e 100644
--- a/sound/soc/sof/intel/pci-icl.c
+++ b/sound/soc/sof/intel/pci-icl.c
@@ -28,11 +28,23 @@ static const struct sof_dev_desc icl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &icl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-icl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/icl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-icl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-icl-nocodec.tplg",
.ops = &sof_icl_ops,
+ .ops_init = sof_icl_ops_init,
};
static const struct sof_dev_desc jsl_desc = {
@@ -43,11 +55,23 @@ static const struct sof_dev_desc jsl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &jsl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-jsl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/jsl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-jsl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-jsl-nocodec.tplg",
.ops = &sof_cnl_ops,
+ .ops_init = sof_cnl_ops_init,
};
/* PCI IDs */
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index feaec251adc8..58a9bd92a237 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -28,11 +28,23 @@ static const struct sof_dev_desc tgl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &tgl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-tgl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/tgl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-tgl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
.ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
};
static const struct sof_dev_desc tglh_desc = {
@@ -44,11 +56,23 @@ static const struct sof_dev_desc tglh_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &tglh_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-tgl-h.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/tgl-h",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-tgl-h.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
.ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
};
static const struct sof_dev_desc ehl_desc = {
@@ -59,11 +83,23 @@ static const struct sof_dev_desc ehl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &ehl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-ehl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/ehl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-ehl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-ehl-nocodec.tplg",
.ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
};
static const struct sof_dev_desc adls_desc = {
@@ -75,11 +111,23 @@ static const struct sof_dev_desc adls_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &adls_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-adl-s.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/adl-s",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-adl-s.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-adl-nocodec.tplg",
.ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
};
static const struct sof_dev_desc adl_desc = {
@@ -91,11 +139,23 @@ static const struct sof_dev_desc adl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.chip_info = &tgl_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-adl.ri",
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/adl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-adl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
.nocodec_tplg_filename = "sof-adl-nocodec.tplg",
.ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
};
/* PCI IDs */
@@ -116,6 +176,12 @@ static const struct pci_device_id sof_pci_ids[] = {
.driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */
.driver_data = (unsigned long)&adl_desc},
+ { PCI_DEVICE(0x8086, 0x51c9), /* ADL-PS */
+ .driver_data = (unsigned long)&adl_desc},
+ { PCI_DEVICE(0x8086, 0x51ca), /* RPL-P */
+ .driver_data = (unsigned long)&adl_desc},
+ { PCI_DEVICE(0x8086, 0x51cb), /* RPL-P */
+ .driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x51cc), /* ADL-M */
.driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x54c8), /* ADL-N */
@@ -140,4 +206,3 @@ module_pci_driver(snd_sof_pci_intel_tgl_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON);
MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
-
diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c
index 6efef225973f..f0f6d9ba8803 100644
--- a/sound/soc/sof/intel/pci-tng.c
+++ b/sound/soc/sof/intel/pci-tng.c
@@ -75,7 +75,11 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev)
/* LPE base */
base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET;
- size = PCI_BAR_SIZE;
+ size = pci_resource_len(pci, desc->resindex_lpe_base);
+ if (size < PCI_BAR_SIZE) {
+ dev_err(sdev->dev, "error: I/O region is too small.\n");
+ return -ENODEV;
+ }
dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size);
@@ -132,7 +136,7 @@ irq:
return ret;
}
-const struct snd_sof_dsp_ops sof_tng_ops = {
+struct snd_sof_dsp_ops sof_tng_ops = {
/* device init */
.probe = tangier_pci_probe,
@@ -160,7 +164,6 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
/* ipc */
.send_msg = atom_send_msg,
- .fw_ready = sof_fw_ready,
.get_mailbox_offset = atom_get_mailbox_offset,
.get_window_offset = atom_get_window_offset,
@@ -183,9 +186,6 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
.pcm_open = sof_stream_pcm_open,
.pcm_close = sof_stream_pcm_close,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
@@ -206,6 +206,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
const struct sof_intel_dsp_desc tng_chip_info = {
.cores_num = 1,
.host_managed_cores_mask = 1,
+ .hw_ip_version = SOF_INTEL_TANGIER,
};
static const struct sof_dev_desc tng_desc = {
@@ -215,9 +216,17 @@ static const struct sof_dev_desc tng_desc = {
.resindex_imr_base = 0,
.irqindex_host_ipc = -1,
.chip_info = &tng_chip_info,
- .default_fw_path = "intel/sof",
- .default_tplg_path = "intel/sof-tplg",
- .default_fw_filename = "sof-byt.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-byt.ri",
+ },
.nocodec_tplg_filename = "sof-byt.tplg",
.ops = &sof_tng_ops,
};
diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h
index f36cd9d5eb94..1fd7b485d821 100644
--- a/sound/soc/sof/intel/shim.h
+++ b/sound/soc/sof/intel/shim.h
@@ -11,6 +11,17 @@
#ifndef __SOF_INTEL_SHIM_H
#define __SOF_INTEL_SHIM_H
+enum sof_intel_hw_ip_version {
+ SOF_INTEL_TANGIER,
+ SOF_INTEL_BAYTRAIL,
+ SOF_INTEL_BROADWELL,
+ SOF_INTEL_CAVS_1_5, /* SkyLake, KabyLake, AmberLake */
+ SOF_INTEL_CAVS_1_5_PLUS,/* ApolloLake, GeminiLake */
+ SOF_INTEL_CAVS_1_8, /* CannonLake, CometLake, CoffeeLake */
+ SOF_INTEL_CAVS_2_0, /* IceLake, JasperLake */
+ SOF_INTEL_CAVS_2_5, /* TigerLake, AlderLake */
+};
+
/*
* SHIM registers for BYT, BSW, CHT, BDW
*/
@@ -164,16 +175,19 @@ struct sof_intel_dsp_desc {
int ipc_ack;
int ipc_ack_mask;
int ipc_ctl;
+ int rom_status_reg;
int rom_init_timeout;
int ssp_count; /* ssp count of the platform */
int ssp_base_offset; /* base address of the SSPs */
u32 sdw_shim_base;
u32 sdw_alh_base;
u32 quirks;
+ enum sof_intel_hw_ip_version hw_ip_version;
bool (*check_sdw_irq)(struct snd_sof_dev *sdev);
+ bool (*check_ipc_irq)(struct snd_sof_dev *sdev);
};
-extern const struct snd_sof_dsp_ops sof_tng_ops;
+extern struct snd_sof_dsp_ops sof_tng_ops;
extern const struct sof_intel_dsp_desc tng_chip_info;
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index cb1c319d5bee..1ddc492f1b13 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -9,6 +9,8 @@
* Hardware interface for audio DSP on Tigerlake.
*/
+#include <sound/sof/ext_manifest4.h>
+#include "../ipc4-priv.h"
#include "../ops.h"
#include "hda.h"
#include "hda-ipc.h"
@@ -35,8 +37,7 @@ static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core)
return hda_dsp_enable_core(sdev, BIT(core));
/* notify DSP for secondary cores */
- return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
- &pm_core_config, sizeof(pm_core_config),
+ return sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
}
@@ -55,115 +56,68 @@ static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core)
return hda_dsp_core_reset_power_down(sdev, BIT(core));
/* notify DSP for secondary cores */
- return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
- &pm_core_config, sizeof(pm_core_config),
+ return sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
}
/* Tigerlake ops */
-const struct snd_sof_dsp_ops sof_tgl_ops = {
+struct snd_sof_dsp_ops sof_tgl_ops;
+EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+int sof_tgl_ops_init(struct snd_sof_dev *sdev)
+{
+ /* common defaults */
+ memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+
/* probe/remove/shutdown */
- .probe = hda_dsp_probe,
- .remove = hda_dsp_remove,
- .shutdown = hda_dsp_shutdown,
-
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
-
- /* doorbell */
- .irq_thread = cnl_ipc_irq_thread,
-
- /* ipc */
- .send_msg = cnl_ipc_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset,
- .get_window_offset = hda_dsp_ipc_get_window_offset,
-
- .ipc_msg_data = hda_ipc_msg_data,
- .set_stream_data_offset = hda_set_stream_data_offset,
-
- /* machine driver */
- .machine_select = hda_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = hda_set_mach_params,
+ sof_tgl_ops.shutdown = hda_dsp_shutdown;
+
+ if (sdev->pdata->ipc_type == SOF_IPC) {
+ /* doorbell */
+ sof_tgl_ops.irq_thread = cnl_ipc_irq_thread;
+
+ /* ipc */
+ sof_tgl_ops.send_msg = cnl_ipc_send_msg;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ /* doorbell */
+ sof_tgl_ops.irq_thread = cnl_ipc4_irq_thread;
+
+ /* ipc */
+ sof_tgl_ops.send_msg = cnl_ipc4_send_msg;
+ }
+
+ /* set DAI driver ops */
+ hda_set_dai_drv_ops(sdev, &sof_tgl_ops);
/* debug */
- .debug_map = tgl_dsp_debugfs,
- .debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs),
- .dbg_dump = hda_dsp_dump,
- .ipc_dump = cnl_ipc_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
- /* stream callbacks */
- .pcm_open = hda_dsp_pcm_open,
- .pcm_close = hda_dsp_pcm_close,
- .pcm_hw_params = hda_dsp_pcm_hw_params,
- .pcm_hw_free = hda_dsp_stream_hw_free,
- .pcm_trigger = hda_dsp_pcm_trigger,
- .pcm_pointer = hda_dsp_pcm_pointer,
- .pcm_ack = hda_dsp_pcm_ack,
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_raw,
+ sof_tgl_ops.debug_map = tgl_dsp_debugfs;
+ sof_tgl_ops.debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs);
+ sof_tgl_ops.ipc_dump = cnl_ipc_dump;
/* pre/post fw run */
- .pre_fw_run = hda_dsp_pre_fw_run,
- .post_fw_run = hda_dsp_post_fw_run,
+ sof_tgl_ops.post_fw_run = hda_dsp_post_fw_run;
- /* parse platform specific extended manifest */
- .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
+ /* firmware run */
+ sof_tgl_ops.run = hda_dsp_cl_boot_firmware_iccmax;
/* dsp core get/put */
- .core_get = tgl_dsp_core_get,
- .core_put = tgl_dsp_core_put,
+ sof_tgl_ops.core_get = tgl_dsp_core_get;
+ sof_tgl_ops.core_put = tgl_dsp_core_put;
- /* firmware run */
- .run = hda_dsp_cl_boot_firmware_iccmax,
-
- /* trace callback */
- .trace_init = hda_dsp_trace_init,
- .trace_release = hda_dsp_trace_release,
- .trace_trigger = hda_dsp_trace_trigger,
-
- /* client ops */
- .register_ipc_clients = hda_register_clients,
- .unregister_ipc_clients = hda_unregister_clients,
-
- /* DAI drivers */
- .drv = skl_dai,
- .num_drv = SOF_SKL_NUM_DAIS,
-
- /* PM */
- .suspend = hda_dsp_suspend,
- .resume = hda_dsp_resume,
- .runtime_suspend = hda_dsp_runtime_suspend,
- .runtime_resume = hda_dsp_runtime_resume,
- .runtime_idle = hda_dsp_runtime_idle,
- .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
- .set_power_state = hda_dsp_set_power_state,
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- .dsp_arch_ops = &sof_xtensa_arch_ops,
+ return 0;
};
-EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
+EXPORT_SYMBOL_NS(sof_tgl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
const struct sof_intel_dsp_desc tgl_chip_info = {
/* Tigerlake , Alderlake */
@@ -175,12 +129,15 @@ const struct sof_intel_dsp_desc tgl_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
};
EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -194,12 +151,15 @@ const struct sof_intel_dsp_desc tglh_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
};
EXPORT_SYMBOL_NS(tglh_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -213,12 +173,15 @@ const struct sof_intel_dsp_desc ehl_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
};
EXPORT_SYMBOL_NS(ehl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -232,11 +195,14 @@ const struct sof_intel_dsp_desc adls_chip_info = {
.ipc_ack = CNL_DSP_REG_HIPCIDA,
.ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
.ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS,
.rom_init_timeout = 300,
.ssp_count = ICL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
.sdw_shim_base = SDW_SHIM_BASE,
.sdw_alh_base = SDW_ALH_BASE,
.check_sdw_irq = hda_common_check_sdw_irq,
+ .check_ipc_irq = hda_dsp_check_ipc_irq,
+ .hw_ip_version = SOF_INTEL_CAVS_2_5,
};
EXPORT_SYMBOL_NS(adls_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 5f5753608c79..c5aef5fc056b 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -17,294 +17,35 @@
#include "sof-priv.h"
#include "sof-audio.h"
#include "ops.h"
-#include "ipc3-ops.h"
-typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
-
-static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf);
-static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf);
-
-/*
- * IPC message Tx/Rx message handling.
+/**
+ * sof_ipc_send_msg - generic function to prepare and send one IPC message
+ * @sdev: pointer to SOF core device struct
+ * @msg_data: pointer to a message to send
+ * @msg_bytes: number of bytes in the message
+ * @reply_bytes: number of bytes available for the reply.
+ * The buffer for the reply data is not passed to this
+ * function, the available size is an information for the
+ * reply handling functions.
+ *
+ * On success the function returns 0, otherwise negative error number.
+ *
+ * Note: higher level sdev->ipc->tx_mutex must be held to make sure that
+ * transfers are synchronized.
*/
-
-struct sof_ipc_ctrl_data_params {
- size_t msg_bytes;
- size_t hdr_bytes;
- size_t pl_size;
- size_t elems;
- u32 num_msg;
- u8 *src;
- u8 *dst;
-};
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
-static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
-{
- u8 *str;
- u8 *str2 = NULL;
- u32 glb;
- u32 type;
- bool vdbg = false;
-
- glb = cmd & SOF_GLB_TYPE_MASK;
- type = cmd & SOF_CMD_TYPE_MASK;
-
- switch (glb) {
- case SOF_IPC_GLB_REPLY:
- str = "GLB_REPLY"; break;
- case SOF_IPC_GLB_COMPOUND:
- str = "GLB_COMPOUND"; break;
- case SOF_IPC_GLB_TPLG_MSG:
- str = "GLB_TPLG_MSG";
- switch (type) {
- case SOF_IPC_TPLG_COMP_NEW:
- str2 = "COMP_NEW"; break;
- case SOF_IPC_TPLG_COMP_FREE:
- str2 = "COMP_FREE"; break;
- case SOF_IPC_TPLG_COMP_CONNECT:
- str2 = "COMP_CONNECT"; break;
- case SOF_IPC_TPLG_PIPE_NEW:
- str2 = "PIPE_NEW"; break;
- case SOF_IPC_TPLG_PIPE_FREE:
- str2 = "PIPE_FREE"; break;
- case SOF_IPC_TPLG_PIPE_CONNECT:
- str2 = "PIPE_CONNECT"; break;
- case SOF_IPC_TPLG_PIPE_COMPLETE:
- str2 = "PIPE_COMPLETE"; break;
- case SOF_IPC_TPLG_BUFFER_NEW:
- str2 = "BUFFER_NEW"; break;
- case SOF_IPC_TPLG_BUFFER_FREE:
- str2 = "BUFFER_FREE"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_PM_MSG:
- str = "GLB_PM_MSG";
- switch (type) {
- case SOF_IPC_PM_CTX_SAVE:
- str2 = "CTX_SAVE"; break;
- case SOF_IPC_PM_CTX_RESTORE:
- str2 = "CTX_RESTORE"; break;
- case SOF_IPC_PM_CTX_SIZE:
- str2 = "CTX_SIZE"; break;
- case SOF_IPC_PM_CLK_SET:
- str2 = "CLK_SET"; break;
- case SOF_IPC_PM_CLK_GET:
- str2 = "CLK_GET"; break;
- case SOF_IPC_PM_CLK_REQ:
- str2 = "CLK_REQ"; break;
- case SOF_IPC_PM_CORE_ENABLE:
- str2 = "CORE_ENABLE"; break;
- case SOF_IPC_PM_GATE:
- str2 = "GATE"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_COMP_MSG:
- str = "GLB_COMP_MSG";
- switch (type) {
- case SOF_IPC_COMP_SET_VALUE:
- str2 = "SET_VALUE"; break;
- case SOF_IPC_COMP_GET_VALUE:
- str2 = "GET_VALUE"; break;
- case SOF_IPC_COMP_SET_DATA:
- str2 = "SET_DATA"; break;
- case SOF_IPC_COMP_GET_DATA:
- str2 = "GET_DATA"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_STREAM_MSG:
- str = "GLB_STREAM_MSG";
- switch (type) {
- case SOF_IPC_STREAM_PCM_PARAMS:
- str2 = "PCM_PARAMS"; break;
- case SOF_IPC_STREAM_PCM_PARAMS_REPLY:
- str2 = "PCM_REPLY"; break;
- case SOF_IPC_STREAM_PCM_FREE:
- str2 = "PCM_FREE"; break;
- case SOF_IPC_STREAM_TRIG_START:
- str2 = "TRIG_START"; break;
- case SOF_IPC_STREAM_TRIG_STOP:
- str2 = "TRIG_STOP"; break;
- case SOF_IPC_STREAM_TRIG_PAUSE:
- str2 = "TRIG_PAUSE"; break;
- case SOF_IPC_STREAM_TRIG_RELEASE:
- str2 = "TRIG_RELEASE"; break;
- case SOF_IPC_STREAM_TRIG_DRAIN:
- str2 = "TRIG_DRAIN"; break;
- case SOF_IPC_STREAM_TRIG_XRUN:
- str2 = "TRIG_XRUN"; break;
- case SOF_IPC_STREAM_POSITION:
- vdbg = true;
- str2 = "POSITION"; break;
- case SOF_IPC_STREAM_VORBIS_PARAMS:
- str2 = "VORBIS_PARAMS"; break;
- case SOF_IPC_STREAM_VORBIS_FREE:
- str2 = "VORBIS_FREE"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_FW_READY:
- str = "FW_READY"; break;
- case SOF_IPC_GLB_DAI_MSG:
- str = "GLB_DAI_MSG";
- switch (type) {
- case SOF_IPC_DAI_CONFIG:
- str2 = "CONFIG"; break;
- case SOF_IPC_DAI_LOOPBACK:
- str2 = "LOOPBACK"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_TRACE_MSG:
- str = "GLB_TRACE_MSG";
- switch (type) {
- case SOF_IPC_TRACE_DMA_PARAMS:
- str2 = "DMA_PARAMS"; break;
- case SOF_IPC_TRACE_DMA_POSITION:
- str2 = "DMA_POSITION"; break;
- case SOF_IPC_TRACE_DMA_PARAMS_EXT:
- str2 = "DMA_PARAMS_EXT"; break;
- case SOF_IPC_TRACE_FILTER_UPDATE:
- str2 = "FILTER_UPDATE"; break;
- case SOF_IPC_TRACE_DMA_FREE:
- str2 = "DMA_FREE"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_TEST_MSG:
- str = "GLB_TEST_MSG";
- switch (type) {
- case SOF_IPC_TEST_IPC_FLOOD:
- str2 = "IPC_FLOOD"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_DEBUG:
- str = "GLB_DEBUG";
- switch (type) {
- case SOF_IPC_DEBUG_MEM_USAGE:
- str2 = "MEM_USAGE"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- case SOF_IPC_GLB_PROBE:
- str = "GLB_PROBE";
- switch (type) {
- case SOF_IPC_PROBE_INIT:
- str2 = "INIT"; break;
- case SOF_IPC_PROBE_DEINIT:
- str2 = "DEINIT"; break;
- case SOF_IPC_PROBE_DMA_ADD:
- str2 = "DMA_ADD"; break;
- case SOF_IPC_PROBE_DMA_INFO:
- str2 = "DMA_INFO"; break;
- case SOF_IPC_PROBE_DMA_REMOVE:
- str2 = "DMA_REMOVE"; break;
- case SOF_IPC_PROBE_POINT_ADD:
- str2 = "POINT_ADD"; break;
- case SOF_IPC_PROBE_POINT_INFO:
- str2 = "POINT_INFO"; break;
- case SOF_IPC_PROBE_POINT_REMOVE:
- str2 = "POINT_REMOVE"; break;
- default:
- str2 = "unknown type"; break;
- }
- break;
- default:
- str = "unknown GLB command"; break;
- }
-
- if (str2) {
- if (vdbg)
- dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
- else
- dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
- } else {
- dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str);
- }
-}
-#else
-static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
-{
- if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG)
- dev_dbg(dev, "%s: 0x%x\n", text, cmd);
-}
-#endif
-
-/* wait for IPC message reply */
-static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
- void *reply_data)
-{
- struct snd_sof_dev *sdev = ipc->sdev;
- struct sof_ipc_cmd_hdr *hdr = msg->msg_data;
- int ret;
-
- /* wait for DSP IPC completion */
- ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
- msecs_to_jiffies(sdev->ipc_timeout));
-
- if (ret == 0) {
- dev_err(sdev->dev,
- "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n",
- hdr->cmd, hdr->size, msg->reply_size);
- snd_sof_handle_fw_exception(ipc->sdev);
- ret = -ETIMEDOUT;
- } else {
- ret = msg->reply_error;
- if (ret < 0) {
- dev_err(sdev->dev,
- "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n",
- hdr->cmd, hdr->size, msg->reply_size, ret);
- } else {
- ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
- if (msg->reply_size)
- /* copy the data returned from DSP */
- memcpy(reply_data, msg->reply_data,
- msg->reply_size);
- }
-
- /* re-enable dumps after successful IPC tx */
- if (sdev->ipc_dump_printed) {
- sdev->dbg_dump_printed = false;
- sdev->ipc_dump_printed = false;
- }
- }
-
- return ret;
-}
-
-/* send IPC message from host to DSP */
-static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc,
- void *msg_data, size_t msg_bytes,
- void *reply_data, size_t reply_bytes)
+int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+ size_t reply_bytes)
{
- struct sof_ipc_cmd_hdr *hdr = msg_data;
- struct snd_sof_dev *sdev = ipc->sdev;
+ struct snd_sof_ipc *ipc = sdev->ipc;
struct snd_sof_ipc_msg *msg;
int ret;
- if (!msg_data || msg_bytes < sizeof(*hdr)) {
- dev_err_ratelimited(sdev->dev, "No IPC message to send\n");
- return -EINVAL;
- }
-
if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE)
return -ENODEV;
/*
- * The spin-lock is also still needed to protect message objects against
- * other atomic contexts.
+ * The spin-lock is needed to protect message objects against other
+ * atomic contexts.
*/
spin_lock_irq(&sdev->ipc_lock);
@@ -327,38 +68,19 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc,
spin_unlock_irq(&sdev->ipc_lock);
- if (ret) {
- dev_err_ratelimited(sdev->dev,
- "error: ipc tx failed with error %d\n",
- ret);
- return ret;
- }
-
- ipc_log_header(sdev->dev, "ipc tx", hdr->cmd);
-
- /* now wait for completion */
- return tx_wait_done(ipc, msg, reply_data);
+ return ret;
}
/* send IPC message from host to DSP */
-int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
- void *msg_data, size_t msg_bytes, void *reply_data,
- size_t reply_bytes)
+int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes)
{
- const struct sof_dsp_power_state target_state = {
- .state = SOF_DSP_PM_D0,
- };
- int ret;
-
- /* ensure the DSP is in D0 before sending a new IPC */
- ret = snd_sof_dsp_set_power_state(ipc->sdev, &target_state);
- if (ret < 0) {
- dev_err(ipc->sdev->dev, "error: resuming DSP %d\n", ret);
- return ret;
- }
+ if (msg_bytes > ipc->max_payload_size ||
+ reply_bytes > ipc->max_payload_size)
+ return -ENOBUFS;
- return sof_ipc_tx_message_no_pm(ipc, header, msg_data, msg_bytes,
- reply_data, reply_bytes);
+ return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
+ reply_bytes, false);
}
EXPORT_SYMBOL(sof_ipc_tx_message);
@@ -367,86 +89,32 @@ EXPORT_SYMBOL(sof_ipc_tx_message);
* This will be used for IPC's that can be handled by the DSP
* even in a low-power D0 substate.
*/
-int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
- void *msg_data, size_t msg_bytes,
+int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes)
{
- int ret;
-
- if (msg_bytes > SOF_IPC_MSG_MAX_SIZE ||
- reply_bytes > SOF_IPC_MSG_MAX_SIZE)
+ if (msg_bytes > ipc->max_payload_size ||
+ reply_bytes > ipc->max_payload_size)
return -ENOBUFS;
- /* Serialise IPC TX */
- mutex_lock(&ipc->tx_mutex);
-
- ret = sof_ipc_tx_message_unlocked(ipc, msg_data, msg_bytes,
- reply_data, reply_bytes);
-
- mutex_unlock(&ipc->tx_mutex);
-
- return ret;
+ return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
+ reply_bytes, true);
}
EXPORT_SYMBOL(sof_ipc_tx_message_no_pm);
/* Generic helper function to retrieve the reply */
void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev)
{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
* ipc belongs to none of the ipcs sent from driver.
* In this case, the driver must ignore the ipc.
*/
- if (!msg) {
+ if (!sdev->msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
- /* get the generic reply */
- snd_sof_dsp_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 if (!reply.hdr.size) {
- /* Reply should always be >= sizeof(struct sof_ipc_reply) */
- if (msg->reply_size)
- dev_err(sdev->dev,
- "empty reply received, expected %zu bytes\n",
- msg->reply_size);
- else
- dev_err(sdev->dev, "empty reply received\n");
-
- ret = -EINVAL;
- } else if (msg->reply_size > 0) {
- if (reply.hdr.size == msg->reply_size) {
- ret = 0;
- } else if (reply.hdr.size < msg->reply_size) {
- dev_dbg(sdev->dev,
- "reply size (%u) is less than expected (%zu)\n",
- reply.hdr.size, msg->reply_size);
-
- msg->reply_size = reply.hdr.size;
- ret = 0;
- } else {
- dev_err(sdev->dev,
- "reply size (%u) exceeds the buffer size (%zu)\n",
- reply.hdr.size, msg->reply_size);
- ret = -EINVAL;
- }
-
- /* get the full message if reply.hdr.size <= msg->reply_size */
- if (!ret)
- snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
+ sdev->msg->reply_error = sdev->ipc->ops->get_reply(sdev);
}
EXPORT_SYMBOL(snd_sof_ipc_get_reply);
@@ -468,550 +136,11 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
}
EXPORT_SYMBOL(snd_sof_ipc_reply);
-static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
-{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- struct sof_ipc_cmd_hdr *hdr = msg_buf;
- u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
-
- switch (msg_type) {
- case SOF_IPC_COMP_GET_VALUE:
- case SOF_IPC_COMP_GET_DATA:
- break;
- default:
- dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type);
- return;
- }
-
- if (tplg_ops->control->update)
- tplg_ops->control->update(sdev, msg_buf);
-}
-
-/* DSP firmware has sent host a message */
-void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
-{
- ipc_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;
- }
-
- if (hdr.size < sizeof(hdr)) {
- dev_err(sdev->dev, "The received message size is invalid\n");
- return;
- }
-
- ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
-
- cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
-
- /* check message type */
- switch (cmd) {
- case SOF_IPC_GLB_REPLY:
- dev_err(sdev->dev, "error: ipc reply unknown\n");
- break;
- case SOF_IPC_FW_READY:
- /* check for FW boot completion */
- if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
- err = sof_ops(sdev)->fw_ready(sdev, cmd);
- if (err < 0)
- sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
- else
- sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
-
- /* wake up firmware loader */
- wake_up(&sdev->boot_wait);
- }
- break;
- case SOF_IPC_GLB_COMPOUND:
- case SOF_IPC_GLB_TPLG_MSG:
- case SOF_IPC_GLB_PM_MSG:
- break;
- case SOF_IPC_GLB_COMP_MSG:
- rx_callback = ipc_comp_notification;
- break;
- case SOF_IPC_GLB_STREAM_MSG:
- rx_callback = ipc_stream_message;
- break;
- case SOF_IPC_GLB_TRACE_MSG:
- rx_callback = ipc_trace_message;
- break;
- default:
- dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
- break;
- }
-
- /* read the full message */
- msg_buf = kmalloc(hdr.size, GFP_KERNEL);
- if (!msg_buf)
- return;
-
- 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);
-
- ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
-}
-EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
-
-/*
- * IPC trace mechanism.
- */
-
-static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
-{
- struct sof_ipc_cmd_hdr *hdr = msg_buf;
- u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
-
- switch (msg_type) {
- case SOF_IPC_TRACE_DMA_POSITION:
- snd_sof_trace_update_pos(sdev, msg_buf);
- break;
- default:
- dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type);
- break;
- }
-}
-
-/*
- * IPC stream position.
- */
-
-static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
-{
- struct snd_soc_component *scomp = sdev->component;
- struct snd_sof_pcm_stream *stream;
- struct sof_ipc_stream_posn posn;
- struct snd_sof_pcm *spcm;
- int direction, ret;
-
- spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
- if (!spcm) {
- dev_err(sdev->dev,
- "error: period elapsed for unknown stream, msg_id %d\n",
- msg_id);
- return;
- }
-
- stream = &spcm->stream[direction];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
- if (ret < 0) {
- dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
- return;
- }
-
- dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
- posn.host_posn, posn.dai_posn, posn.wallclock);
-
- memcpy(&stream->posn, &posn, sizeof(posn));
-
- if (spcm->pcm.compress)
- snd_sof_compr_fragment_elapsed(stream->cstream);
- else if (stream->substream->runtime &&
- !stream->substream->runtime->no_period_wakeup)
- /* only inform ALSA for period_wakeup mode */
- snd_sof_pcm_period_elapsed(stream->substream);
-}
-
-/* DSP notifies host of an XRUN within FW */
-static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
-{
- struct snd_soc_component *scomp = sdev->component;
- struct snd_sof_pcm_stream *stream;
- struct sof_ipc_stream_posn posn;
- struct snd_sof_pcm *spcm;
- int direction, ret;
-
- spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
- if (!spcm) {
- dev_err(sdev->dev, "error: XRUN for unknown stream, msg_id %d\n",
- msg_id);
- return;
- }
-
- stream = &spcm->stream[direction];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
- if (ret < 0) {
- dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
- return;
- }
-
- dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n",
- posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
-
-#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
- /* stop PCM on XRUN - used for pipeline debug */
- memcpy(&stream->posn, &posn, sizeof(posn));
- snd_pcm_stop_xrun(stream->substream);
-#endif
-}
-
-/* stream notifications from DSP FW */
-static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
-{
- struct sof_ipc_cmd_hdr *hdr = msg_buf;
- u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
- u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
-
- switch (msg_type) {
- case SOF_IPC_STREAM_POSITION:
- ipc_period_elapsed(sdev, msg_id);
- break;
- case SOF_IPC_STREAM_TRIG_XRUN:
- ipc_xrun(sdev, msg_id);
- break;
- default:
- dev_err(sdev->dev, "error: unhandled stream message %#x\n",
- msg_id);
- break;
- }
-}
-
-/* get stream position IPC - use faster MMIO method if available on platform */
-int snd_sof_ipc_stream_posn(struct snd_soc_component *scomp,
- struct snd_sof_pcm *spcm, int direction,
- struct sof_ipc_stream_posn *posn)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_stream stream;
- int err;
-
- /* read position via slower IPC */
- stream.hdr.size = sizeof(stream);
- stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION;
- stream.comp_id = spcm->stream[direction].comp_id;
-
- /* send IPC to the DSP */
- err = sof_ipc_tx_message(sdev->ipc,
- stream.hdr.cmd, &stream, sizeof(stream), posn,
- sizeof(*posn));
- if (err < 0) {
- dev_err(sdev->dev, "error: failed to get stream %d position\n",
- stream.comp_id);
- return err;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(snd_sof_ipc_stream_posn);
-
-static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type,
- struct sof_ipc_ctrl_data *src,
- struct sof_ipc_ctrl_data *dst,
- struct sof_ipc_ctrl_data_params *sparams)
-{
- switch (ctrl_type) {
- case SOF_CTRL_TYPE_VALUE_CHAN_GET:
- case SOF_CTRL_TYPE_VALUE_CHAN_SET:
- sparams->src = (u8 *)src->chanv;
- sparams->dst = (u8 *)dst->chanv;
- break;
- case SOF_CTRL_TYPE_DATA_GET:
- case SOF_CTRL_TYPE_DATA_SET:
- sparams->src = (u8 *)src->data->data;
- sparams->dst = (u8 *)dst->data->data;
- break;
- default:
- return -EINVAL;
- }
-
- /* calculate payload size and number of messages */
- sparams->pl_size = SOF_IPC_MSG_MAX_SIZE - sparams->hdr_bytes;
- sparams->num_msg = DIV_ROUND_UP(sparams->msg_bytes, sparams->pl_size);
-
- return 0;
-}
-
-static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
- struct sof_ipc_ctrl_data *cdata,
- struct sof_ipc_ctrl_data_params *sparams,
- bool set)
-{
- struct sof_ipc_ctrl_data *partdata;
- size_t send_bytes;
- size_t offset = 0;
- size_t msg_bytes;
- size_t pl_size;
- int err;
- int i;
-
- /* allocate max ipc size because we have at least one */
- partdata = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- if (!partdata)
- return -ENOMEM;
-
- if (set)
- err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata,
- sparams);
- else
- err = sof_get_ctrl_copy_params(cdata->type, partdata, cdata,
- sparams);
- if (err < 0) {
- kfree(partdata);
- return err;
- }
-
- msg_bytes = sparams->msg_bytes;
- pl_size = sparams->pl_size;
-
- /* copy the header data */
- memcpy(partdata, cdata, sparams->hdr_bytes);
-
- /* Serialise IPC TX */
- mutex_lock(&sdev->ipc->tx_mutex);
-
- /* copy the payload data in a loop */
- for (i = 0; i < sparams->num_msg; i++) {
- send_bytes = min(msg_bytes, pl_size);
- partdata->num_elems = send_bytes;
- partdata->rhdr.hdr.size = sparams->hdr_bytes + send_bytes;
- partdata->msg_index = i;
- msg_bytes -= send_bytes;
- partdata->elems_remaining = msg_bytes;
-
- if (set)
- memcpy(sparams->dst, sparams->src + offset, send_bytes);
-
- err = sof_ipc_tx_message_unlocked(sdev->ipc,
- partdata,
- partdata->rhdr.hdr.size,
- partdata,
- partdata->rhdr.hdr.size);
- if (err < 0)
- break;
-
- if (!set)
- memcpy(sparams->dst + offset, sparams->src, send_bytes);
-
- offset += pl_size;
- }
-
- mutex_unlock(&sdev->ipc->tx_mutex);
-
- kfree(partdata);
- return err;
-}
-
-/*
- * IPC get()/set() for kcontrols.
- */
-int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set)
-{
- struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
- struct sof_ipc_fw_version *v = &ready->version;
- struct sof_ipc_ctrl_data_params sparams;
- enum sof_ipc_ctrl_type ctrl_type;
- struct snd_sof_widget *swidget;
- bool widget_found = false;
- size_t send_bytes;
- u32 ipc_cmd;
- int err;
-
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (swidget->comp_id == scontrol->comp_id) {
- widget_found = true;
- break;
- }
- }
-
- if (!widget_found) {
- dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id);
- return -EINVAL;
- }
-
- /*
- * Volatile controls should always be part of static pipelines and the widget use_count
- * would always be > 0 in this case. For the others, just return the cached value if the
- * widget is not set up.
- */
- if (!swidget->use_count)
- return 0;
-
- /* read or write firmware volume */
- if (scontrol->readback_offset != 0) {
- /* write/read value header via mmaped region */
- send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) *
- cdata->num_elems;
- if (set)
- err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM,
- scontrol->readback_offset,
- cdata->chanv, send_bytes);
-
- else
- err = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_IRAM,
- scontrol->readback_offset,
- cdata->chanv, send_bytes);
-
- if (err)
- dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n",
- set ? "write to" : "read from");
- return err;
- }
-
- /*
- * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
- * direction
- * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently
- * for ctrl_type
- */
- if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
- ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA;
- ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET;
- } else {
- ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE;
- ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET;
- }
-
- cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
- cdata->type = ctrl_type;
- cdata->comp_id = scontrol->comp_id;
- cdata->msg_index = 0;
-
- /* calculate header and data size */
- switch (cdata->type) {
- case SOF_CTRL_TYPE_VALUE_CHAN_GET:
- case SOF_CTRL_TYPE_VALUE_CHAN_SET:
- sparams.msg_bytes = scontrol->num_channels *
- sizeof(struct sof_ipc_ctrl_value_chan);
- sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
- sparams.elems = scontrol->num_channels;
- break;
- case SOF_CTRL_TYPE_DATA_GET:
- case SOF_CTRL_TYPE_DATA_SET:
- sparams.msg_bytes = cdata->data->size;
- sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data) +
- sizeof(struct sof_abi_hdr);
- sparams.elems = cdata->data->size;
- break;
- default:
- return -EINVAL;
- }
-
- cdata->rhdr.hdr.size = sparams.msg_bytes + sparams.hdr_bytes;
- cdata->num_elems = sparams.elems;
- cdata->elems_remaining = 0;
-
- /* send normal size ipc in one part */
- if (cdata->rhdr.hdr.size <= SOF_IPC_MSG_MAX_SIZE) {
- err = sof_ipc_tx_message(sdev->ipc, cdata->rhdr.hdr.cmd, cdata,
- cdata->rhdr.hdr.size, cdata,
- cdata->rhdr.hdr.size);
-
- if (err < 0)
- dev_err(sdev->dev, "error: set/get ctrl ipc comp %d\n",
- cdata->comp_id);
-
- return err;
- }
-
- /* data is bigger than max ipc size, chop into smaller pieces */
- dev_dbg(sdev->dev, "large ipc size %u, control size %u\n",
- cdata->rhdr.hdr.size, scontrol->size);
-
- /* large messages is only supported from ABI 3.3.0 onwards */
- if (v->abi_version < SOF_ABI_VER(3, 3, 0)) {
- dev_err(sdev->dev, "error: incompatible FW ABI version\n");
- return -EINVAL;
- }
-
- err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, set);
-
- if (err < 0)
- dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n",
- cdata->comp_id);
-
- return err;
-}
-EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data);
-
-int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
-{
- struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
- struct sof_ipc_fw_version *v = &ready->version;
-
- dev_info(sdev->dev,
- "Firmware info: version %d:%d:%d-%s\n", v->major, v->minor,
- v->micro, v->tag);
- dev_info(sdev->dev,
- "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
- SOF_ABI_VERSION_MAJOR(v->abi_version),
- SOF_ABI_VERSION_MINOR(v->abi_version),
- SOF_ABI_VERSION_PATCH(v->abi_version),
- SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
-
- if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) {
- dev_err(sdev->dev, "error: incompatible FW ABI version\n");
- return -EINVAL;
- }
-
- if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) {
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
- dev_warn(sdev->dev, "warn: FW ABI is more recent than kernel\n");
- } else {
- dev_err(sdev->dev, "error: FW ABI is more recent than kernel\n");
- return -EINVAL;
- }
- }
-
- if (ready->flags & SOF_IPC_INFO_BUILD) {
- dev_info(sdev->dev,
- "Firmware debug build %d on %s-%s - options:\n"
- " GDB: %s\n"
- " lock debug: %s\n"
- " lock vdebug: %s\n",
- v->build, v->date, v->time,
- (ready->flags & SOF_IPC_INFO_GDB) ?
- "enabled" : "disabled",
- (ready->flags & SOF_IPC_INFO_LOCKS) ?
- "enabled" : "disabled",
- (ready->flags & SOF_IPC_INFO_LOCKSV) ?
- "enabled" : "disabled");
- }
-
- /* copy the fw_version into debugfs at first boot */
- memcpy(&sdev->fw_version, v, sizeof(*v));
-
- return 0;
-}
-EXPORT_SYMBOL(snd_sof_ipc_valid);
-
-int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg;
-
- msg = &sdev->ipc->msg;
-
- msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- if (!msg->reply_data)
- return -ENOMEM;
-
- return 0;
-}
-
struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc *ipc;
struct snd_sof_ipc_msg *msg;
+ const struct sof_ipc_ops *ops;
ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
if (!ipc)
@@ -1031,11 +160,33 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
* versions, this will need to be modified to use the selected version at runtime.
*/
ipc->ops = &ipc3_ops;
+ ops = ipc->ops;
/* check for mandatory ops */
- if (!ipc->ops->pcm || !ipc->ops->tplg || !ipc->ops->tplg->widget ||
- !ipc->ops->tplg->control) {
- dev_err(sdev->dev, "Invalid IPC ops\n");
+ if (!ops->tx_msg || !ops->rx_msg || !ops->set_get_data || !ops->get_reply) {
+ dev_err(sdev->dev, "Missing IPC message handling ops\n");
+ return NULL;
+ }
+
+ if (!ops->fw_loader || !ops->fw_loader->validate ||
+ !ops->fw_loader->parse_ext_manifest) {
+ dev_err(sdev->dev, "Missing IPC firmware loading ops\n");
+ return NULL;
+ }
+
+ if (!ops->pcm) {
+ dev_err(sdev->dev, "Missing IPC PCM ops\n");
+ return NULL;
+ }
+
+ if (!ops->tplg || !ops->tplg->widget || !ops->tplg->control) {
+ dev_err(sdev->dev, "Missing IPC topology ops\n");
+ return NULL;
+ }
+
+ if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend ||
+ !ops->fw_tracing->resume)) {
+ dev_err(sdev->dev, "Missing firmware tracing ops\n");
return NULL;
}
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index cdd5ad860a94..3fdc0d854e65 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -9,26 +9,85 @@
#include "sof-priv.h"
#include "sof-audio.h"
-#include "ipc3-ops.h"
+#include "ipc3-priv.h"
-static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
+/* IPC set()/get() for kcontrols. */
+static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
{
- if (value >= size)
- return volume_map[size - 1];
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ const struct sof_ipc_ops *iops = sdev->ipc->ops;
+ enum sof_ipc_ctrl_type ctrl_type;
+ struct snd_sof_widget *swidget;
+ bool widget_found = false;
+ u32 ipc_cmd, msg_bytes;
- return volume_map[value];
-}
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->comp_id == scontrol->comp_id) {
+ widget_found = true;
+ break;
+ }
+ }
-static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
-{
- int i;
+ if (!widget_found) {
+ dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__,
+ scontrol->comp_id);
+ return -EINVAL;
+ }
+
+ /*
+ * Volatile controls should always be part of static pipelines and the widget use_count
+ * would always be > 0 in this case. For the others, just return the cached value if the
+ * widget is not set up.
+ */
+ if (!swidget->use_count)
+ return 0;
+
+ /*
+ * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
+ * direction
+ * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently
+ * for ctrl_type
+ */
+ if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
+ ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA;
+ ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET;
+ } else {
+ ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE;
+ ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET;
+ }
+
+ cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
+ cdata->type = ctrl_type;
+ cdata->comp_id = scontrol->comp_id;
+ cdata->msg_index = 0;
+
+ /* calculate header and data size */
+ switch (cdata->type) {
+ case SOF_CTRL_TYPE_VALUE_CHAN_GET:
+ case SOF_CTRL_TYPE_VALUE_CHAN_SET:
+ cdata->num_elems = scontrol->num_channels;
- for (i = 0; i < size; i++) {
- if (volume_map[i] >= value)
- return i;
+ msg_bytes = scontrol->num_channels *
+ sizeof(struct sof_ipc_ctrl_value_chan);
+ msg_bytes += sizeof(struct sof_ipc_ctrl_data);
+ break;
+ case SOF_CTRL_TYPE_DATA_GET:
+ case SOF_CTRL_TYPE_DATA_SET:
+ cdata->num_elems = cdata->data->size;
+
+ msg_bytes = cdata->data->size;
+ msg_bytes += sizeof(struct sof_ipc_ctrl_data) +
+ sizeof(struct sof_abi_hdr);
+ break;
+ default:
+ return -EINVAL;
}
- return i - 1;
+ cdata->rhdr.hdr.size = msg_bytes;
+ cdata->elems_remaining = 0;
+
+ return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
}
static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
@@ -49,7 +108,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
/* refresh the component data from DSP */
scontrol->comp_data_dirty = false;
- ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
if (ret < 0) {
dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
@@ -97,7 +156,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -145,7 +204,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -193,7 +252,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
/* notify DSP of enum updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set enum updates for %s\n",
@@ -265,7 +324,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- return snd_sof_ipc_set_get_comp_data(scontrol, true);
+ return sof_ipc3_set_get_kcontrol_data(scontrol, true);
return 0;
}
@@ -379,7 +438,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- return snd_sof_ipc_set_get_comp_data(scontrol, true);
+ return sof_ipc3_set_get_kcontrol_data(scontrol, true);
return 0;
}
@@ -409,7 +468,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
cdata->data->abi = SOF_ABI_VERSION;
/* get all the component data from DSP */
- ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
if (ret < 0)
return ret;
@@ -578,6 +637,60 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_
snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
}
+static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget)
+{
+ struct snd_sof_control *scontrol;
+ int ret;
+
+ /* set up all controls for the widget */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ if (scontrol->comp_id == swidget->comp_id) {
+ /* set kcontrol data in DSP */
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "kcontrol %d set up failed for widget %s\n",
+ scontrol->comp_id, swidget->widget->name);
+ return ret;
+ }
+
+ /*
+ * Read back the data from the DSP for static widgets.
+ * This is particularly useful for binary kcontrols
+ * associated with static pipeline widgets to initialize
+ * the data size to match that in the DSP.
+ */
+ if (swidget->dynamic_pipeline_widget)
+ continue;
+
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ if (ret < 0)
+ dev_warn(sdev->dev,
+ "kcontrol %d read failed for widget %s\n",
+ scontrol->comp_id, swidget->widget->name);
+ }
+
+ return 0;
+}
+
+static int
+sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)
+{
+ int i;
+
+ /* init the volume table */
+ scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
+ if (!scontrol->volume_table)
+ return -ENOMEM;
+
+ /* populate the volume table */
+ for (i = 0; i < size ; i++)
+ scontrol->volume_table[i] = vol_compute_gain(i, tlv);
+
+ return 0;
+}
+
const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
.volume_put = sof_ipc3_volume_put,
.volume_get = sof_ipc3_volume_get,
@@ -591,4 +704,6 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
.bytes_ext_get = sof_ipc3_bytes_ext_get,
.bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get,
.update = sof_ipc3_control_update,
+ .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup,
+ .set_up_volume_table = sof_ipc3_set_up_volume_table,
};
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
new file mode 100644
index 000000000000..b4e1343f9138
--- /dev/null
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+
+#include <linux/debugfs.h>
+#include <linux/sched/signal.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ops.h"
+#include "sof-utils.h"
+#include "ipc3-priv.h"
+
+#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
+#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
+
+enum sof_dtrace_state {
+ SOF_DTRACE_DISABLED,
+ SOF_DTRACE_STOPPED,
+ SOF_DTRACE_ENABLED,
+};
+
+struct sof_dtrace_priv {
+ struct snd_dma_buffer dmatb;
+ struct snd_dma_buffer dmatp;
+ int dma_trace_pages;
+ wait_queue_head_t trace_sleep;
+ u32 host_offset;
+ bool dtrace_error;
+ bool dtrace_draining;
+ enum sof_dtrace_state dtrace_state;
+};
+
+static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value,
+ struct sof_ipc_trace_filter_elem *elem_list,
+ int capacity, int *counter)
+{
+ if (*counter >= capacity)
+ return -ENOMEM;
+
+ elem_list[*counter].key = key;
+ elem_list[*counter].value = value;
+ ++*counter;
+
+ return 0;
+}
+
+static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
+ struct sof_ipc_trace_filter_elem *elem,
+ int capacity, int *counter)
+{
+ int log_level, pipe_id, comp_id, read, ret;
+ int len = strlen(line);
+ int cnt = *counter;
+ u32 uuid_id;
+
+ /* ignore empty content */
+ ret = sscanf(line, " %n", &read);
+ if (!ret && read == len)
+ return len;
+
+ ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
+ if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
+ dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line);
+ return -EINVAL;
+ }
+
+ if (uuid_id > 0) {
+ ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
+ uuid_id, elem, capacity, &cnt);
+ if (ret)
+ return ret;
+ }
+ if (pipe_id >= 0) {
+ ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
+ pipe_id, elem, capacity, &cnt);
+ if (ret)
+ return ret;
+ }
+ if (comp_id >= 0) {
+ ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
+ comp_id, elem, capacity, &cnt);
+ if (ret)
+ return ret;
+ }
+
+ ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
+ SOF_IPC_TRACE_FILTER_ELEM_FIN,
+ log_level, elem, capacity, &cnt);
+ if (ret)
+ return ret;
+
+ /* update counter only when parsing whole entry passed */
+ *counter = cnt;
+
+ return len;
+}
+
+static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
+ int *out_elem_cnt,
+ struct sof_ipc_trace_filter_elem **out)
+{
+ static const char entry_delimiter[] = ";";
+ char *entry = string;
+ int capacity = 0;
+ int entry_len;
+ int cnt = 0;
+
+ /*
+ * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
+ * IPC elements, depending on content. Calculate IPC elements capacity
+ * for the input string where each element is set.
+ */
+ while (entry) {
+ capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
+ entry = strchr(entry + 1, entry_delimiter[0]);
+ }
+ *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
+ if (!*out)
+ return -ENOMEM;
+
+ /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
+ while ((entry = strsep(&string, entry_delimiter))) {
+ entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
+ if (entry_len < 0) {
+ dev_err(sdev->dev,
+ "Parsing filter entry '%s' failed with %d\n",
+ entry, entry_len);
+ return -EINVAL;
+ }
+ }
+
+ *out_elem_cnt = cnt;
+
+ return 0;
+}
+
+static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
+ struct sof_ipc_trace_filter_elem *elems)
+{
+ struct sof_ipc_trace_filter *msg;
+ struct sof_ipc_reply reply;
+ size_t size;
+ int ret;
+
+ size = struct_size(msg, elems, num_elems);
+ if (size > SOF_IPC_MSG_MAX_SIZE)
+ return -EINVAL;
+
+ msg = kmalloc(size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->hdr.size = size;
+ msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
+ msg->elem_cnt = num_elems;
+ memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
+
+ ret = pm_runtime_get_sync(sdev->dev);
+ if (ret < 0 && ret != -EACCES) {
+ pm_runtime_put_noidle(sdev->dev);
+ dev_err(sdev->dev, "enabling device failed: %d\n", ret);
+ goto error;
+ }
+ ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
+ pm_runtime_mark_last_busy(sdev->dev);
+ pm_runtime_put_autosuspend(sdev->dev);
+
+error:
+ kfree(msg);
+ return ret ? ret : reply.error;
+}
+
+static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct snd_sof_dfsentry *dfse = file->private_data;
+ struct sof_ipc_trace_filter_elem *elems = NULL;
+ struct snd_sof_dev *sdev = dfse->sdev;
+ loff_t pos = 0;
+ int num_elems;
+ char *string;
+ int ret;
+
+ if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
+ dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
+ TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
+ return -EINVAL;
+ }
+
+ string = kmalloc(count + 1, GFP_KERNEL);
+ if (!string)
+ return -ENOMEM;
+
+ /* assert null termination */
+ string[count] = 0;
+ ret = simple_write_to_buffer(string, count, &pos, from, count);
+ if (ret < 0)
+ goto error;
+
+ ret = trace_filter_parse(sdev, string, &num_elems, &elems);
+ if (ret < 0)
+ goto error;
+
+ if (num_elems) {
+ ret = ipc3_trace_update_filter(sdev, num_elems, elems);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Filter update failed: %d\n", ret);
+ goto error;
+ }
+ }
+ ret = count;
+error:
+ kfree(string);
+ kfree(elems);
+ return ret;
+}
+
+static const struct file_operations sof_dfs_trace_filter_fops = {
+ .open = simple_open,
+ .write = dfsentry_trace_filter_write,
+ .llseek = default_llseek,
+};
+
+static int debugfs_create_trace_filter(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_dfsentry *dfse;
+
+ dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+ if (!dfse)
+ return -ENOMEM;
+
+ dfse->sdev = sdev;
+ dfse->type = SOF_DFSENTRY_TYPE_BUF;
+
+ debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
+ &sof_dfs_trace_filter_fops);
+ /* add to dfsentry list */
+ list_add(&dfse->list, &sdev->dfsentry_list);
+
+ return 0;
+}
+
+static size_t sof_dtrace_avail(struct snd_sof_dev *sdev,
+ loff_t pos, size_t buffer_size)
+{
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+ loff_t host_offset = READ_ONCE(priv->host_offset);
+
+ /*
+ * If host offset is less than local pos, it means write pointer of
+ * host DMA buffer has been wrapped. We should output the trace data
+ * at the end of host DMA buffer at first.
+ */
+ if (host_offset < pos)
+ return buffer_size - pos;
+
+ /* If there is available trace data now, it is unnecessary to wait. */
+ if (host_offset > pos)
+ return host_offset - pos;
+
+ return 0;
+}
+
+static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos,
+ size_t buffer_size)
+{
+ size_t ret = sof_dtrace_avail(sdev, pos, buffer_size);
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+ wait_queue_entry_t wait;
+
+ /* data immediately available */
+ if (ret)
+ return ret;
+
+ if (priv->dtrace_state != SOF_DTRACE_ENABLED && priv->dtrace_draining) {
+ /*
+ * tracing has ended and all traces have been
+ * read by client, return EOF
+ */
+ priv->dtrace_draining = false;
+ return 0;
+ }
+
+ /* wait for available trace data from FW */
+ init_waitqueue_entry(&wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&priv->trace_sleep, &wait);
+
+ if (!signal_pending(current)) {
+ /* set timeout to max value, no error code */
+ schedule_timeout(MAX_SCHEDULE_TIMEOUT);
+ }
+ remove_wait_queue(&priv->trace_sleep, &wait);
+
+ return sof_dtrace_avail(sdev, pos, buffer_size);
+}
+
+static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct snd_sof_dfsentry *dfse = file->private_data;
+ struct snd_sof_dev *sdev = dfse->sdev;
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+ unsigned long rem;
+ loff_t lpos = *ppos;
+ size_t avail, buffer_size = dfse->size;
+ u64 lpos_64;
+
+ /* make sure we know about any failures on the DSP side */
+ priv->dtrace_error = false;
+
+ /* check pos and count */
+ if (lpos < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+
+ /* check for buffer wrap and count overflow */
+ lpos_64 = lpos;
+ lpos = do_div(lpos_64, buffer_size);
+
+ /* get available count based on current host offset */
+ avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size);
+ if (priv->dtrace_error) {
+ dev_err(sdev->dev, "trace IO error\n");
+ return -EIO;
+ }
+
+ /* make sure count is <= avail */
+ if (count > avail)
+ count = avail;
+
+ /*
+ * make sure that all trace data is available for the CPU as the trace
+ * data buffer might be allocated from non consistent memory.
+ * Note: snd_dma_buffer_sync() is called for normal audio playback and
+ * capture streams also.
+ */
+ snd_dma_buffer_sync(&priv->dmatb, SNDRV_DMA_SYNC_CPU);
+ /* copy available trace data to debugfs */
+ rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
+ if (rem)
+ return -EFAULT;
+
+ *ppos += count;
+
+ /* move debugfs reading position */
+ return count;
+}
+
+static int dfsentry_dtrace_release(struct inode *inode, struct file *file)
+{
+ struct snd_sof_dfsentry *dfse = inode->i_private;
+ struct snd_sof_dev *sdev = dfse->sdev;
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+ /* avoid duplicate traces at next open */
+ if (priv->dtrace_state != SOF_DTRACE_ENABLED)
+ priv->host_offset = 0;
+
+ return 0;
+}
+
+static const struct file_operations sof_dfs_dtrace_fops = {
+ .open = simple_open,
+ .read = dfsentry_dtrace_read,
+ .llseek = default_llseek,
+ .release = dfsentry_dtrace_release,
+};
+
+static int debugfs_create_dtrace(struct snd_sof_dev *sdev)
+{
+ struct sof_dtrace_priv *priv;
+ struct snd_sof_dfsentry *dfse;
+ int ret;
+
+ if (!sdev)
+ return -EINVAL;
+
+ priv = sdev->fw_trace_data;
+
+ ret = debugfs_create_trace_filter(sdev);
+ if (ret < 0)
+ dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret);
+
+ dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+ if (!dfse)
+ return -ENOMEM;
+
+ dfse->type = SOF_DFSENTRY_TYPE_BUF;
+ dfse->buf = priv->dmatb.area;
+ dfse->size = priv->dmatb.bytes;
+ dfse->sdev = sdev;
+
+ debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
+ &sof_dfs_dtrace_fops);
+
+ return 0;
+}
+
+static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
+{
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+ struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+ struct sof_ipc_fw_version *v = &ready->version;
+ struct sof_ipc_dma_trace_params_ext params;
+ struct sof_ipc_reply ipc_reply;
+ int ret;
+
+ if (!sdev->fw_trace_is_supported)
+ return 0;
+
+ if (priv->dtrace_state == SOF_DTRACE_ENABLED || !priv->dma_trace_pages)
+ return -EINVAL;
+
+ if (priv->dtrace_state == SOF_DTRACE_STOPPED)
+ goto start;
+
+ /* set IPC parameters */
+ params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
+ /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
+ if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
+ params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
+ params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
+ params.timestamp_ns = ktime_get(); /* in nanosecond */
+ } else {
+ params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
+ params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
+ }
+ params.buffer.phy_addr = priv->dmatp.addr;
+ params.buffer.size = priv->dmatb.bytes;
+ params.buffer.pages = priv->dma_trace_pages;
+ params.stream_tag = 0;
+
+ priv->host_offset = 0;
+ priv->dtrace_draining = false;
+
+ ret = sof_dtrace_host_init(sdev, &priv->dmatb, &params);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag);
+
+ /* send IPC to the DSP */
+ ret = sof_ipc_tx_message(sdev->ipc, &params, sizeof(params), &ipc_reply, sizeof(ipc_reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
+ goto trace_release;
+ }
+
+start:
+ ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_START);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret);
+ goto trace_release;
+ }
+
+ priv->dtrace_state = SOF_DTRACE_ENABLED;
+
+ return 0;
+
+trace_release:
+ sof_dtrace_host_release(sdev);
+ return ret;
+}
+
+static int ipc3_dtrace_init(struct snd_sof_dev *sdev)
+{
+ struct sof_dtrace_priv *priv;
+ int ret;
+
+ /* dtrace is only supported with SOF_IPC */
+ if (sdev->pdata->ipc_type != SOF_IPC)
+ return -EOPNOTSUPP;
+
+ if (sdev->fw_trace_data) {
+ dev_err(sdev->dev, "fw_trace_data has been already allocated\n");
+ return -EBUSY;
+ }
+
+ priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ sdev->fw_trace_data = priv;
+
+ /* set false before start initialization */
+ priv->dtrace_state = SOF_DTRACE_DISABLED;
+
+ /* allocate trace page table buffer */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
+ PAGE_SIZE, &priv->dmatp);
+ if (ret < 0) {
+ dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret);
+ return ret;
+ }
+
+ /* allocate trace data buffer */
+ ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
+ DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
+ &priv->dmatb);
+ if (ret < 0) {
+ dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret);
+ goto page_err;
+ }
+
+ /* create compressed page table for audio firmware */
+ ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb,
+ priv->dmatp.area, priv->dmatb.bytes);
+ if (ret < 0)
+ goto table_err;
+
+ priv->dma_trace_pages = ret;
+ dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", __func__,
+ priv->dma_trace_pages);
+
+ if (sdev->first_boot) {
+ ret = debugfs_create_dtrace(sdev);
+ if (ret < 0)
+ goto table_err;
+ }
+
+ init_waitqueue_head(&priv->trace_sleep);
+
+ ret = ipc3_dtrace_enable(sdev);
+ if (ret < 0)
+ goto table_err;
+
+ return 0;
+table_err:
+ priv->dma_trace_pages = 0;
+ snd_dma_free_pages(&priv->dmatb);
+page_err:
+ snd_dma_free_pages(&priv->dmatp);
+ return ret;
+}
+
+int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_posn *posn)
+{
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+ if (!sdev->fw_trace_is_supported)
+ return 0;
+
+ if (priv->dtrace_state == SOF_DTRACE_ENABLED &&
+ priv->host_offset != posn->host_offset) {
+ priv->host_offset = posn->host_offset;
+ wake_up(&priv->trace_sleep);
+ }
+
+ if (posn->overflow != 0)
+ dev_err(sdev->dev,
+ "DSP trace buffer overflow %u bytes. Total messages %d\n",
+ posn->overflow, posn->messages);
+
+ return 0;
+}
+
+/* an error has occurred within the DSP that prevents further trace */
+static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev)
+{
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+ if (priv->dtrace_state == SOF_DTRACE_ENABLED) {
+ priv->dtrace_error = true;
+ wake_up(&priv->trace_sleep);
+ }
+}
+
+static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
+{
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+ struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+ struct sof_ipc_fw_version *v = &ready->version;
+ struct sof_ipc_cmd_hdr hdr;
+ struct sof_ipc_reply ipc_reply;
+ int ret;
+
+ if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED)
+ return;
+
+ ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0)
+ dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret);
+ priv->dtrace_state = SOF_DTRACE_STOPPED;
+
+ /*
+ * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from
+ * ABI 3.20.0 onwards
+ */
+ if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) {
+ hdr.size = sizeof(hdr);
+ hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
+
+ ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size,
+ &ipc_reply, sizeof(ipc_reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
+ }
+
+ if (only_stop)
+ goto out;
+
+ ret = sof_dtrace_host_release(sdev);
+ if (ret < 0)
+ dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
+
+ priv->dtrace_state = SOF_DTRACE_DISABLED;
+
+out:
+ priv->dtrace_draining = true;
+ wake_up(&priv->trace_sleep);
+}
+
+static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
+{
+ ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0);
+}
+
+static int ipc3_dtrace_resume(struct snd_sof_dev *sdev)
+{
+ return ipc3_dtrace_enable(sdev);
+}
+
+static void ipc3_dtrace_free(struct snd_sof_dev *sdev)
+{
+ struct sof_dtrace_priv *priv = sdev->fw_trace_data;
+
+ /* release trace */
+ ipc3_dtrace_release(sdev, false);
+
+ if (priv->dma_trace_pages) {
+ snd_dma_free_pages(&priv->dmatb);
+ snd_dma_free_pages(&priv->dmatp);
+ priv->dma_trace_pages = 0;
+ }
+}
+
+const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = {
+ .init = ipc3_dtrace_init,
+ .free = ipc3_dtrace_free,
+ .fw_crashed = ipc3_dtrace_fw_crashed,
+ .suspend = ipc3_dtrace_suspend,
+ .resume = ipc3_dtrace_resume,
+};
diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c
new file mode 100644
index 000000000000..f3c741b49519
--- /dev/null
+++ b/sound/soc/sof/ipc3-loader.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <linux/firmware.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ipc3-priv.h"
+#include "ops.h"
+
+static int ipc3_fw_ext_man_get_version(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_fw_version *v =
+ container_of(hdr, struct sof_ext_man_fw_version, hdr);
+
+ memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
+ sdev->fw_ready.flags = v->flags;
+
+ /* log ABI versions and check FW compatibility */
+ return sof_ipc3_validate_fw_version(sdev);
+}
+
+static int ipc3_fw_ext_man_get_windows(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_window *w;
+
+ w = container_of(hdr, struct sof_ext_man_window, hdr);
+
+ return sof_ipc3_get_ext_windows(sdev, &w->ipc_window.ext_hdr);
+}
+
+static int ipc3_fw_ext_man_get_cc_info(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_cc_version *cc;
+
+ cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
+
+ return sof_ipc3_get_cc_info(sdev, &cc->cc_version.ext_hdr);
+}
+
+static int ipc3_fw_ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct ext_man_dbg_abi *dbg_abi =
+ container_of(hdr, struct ext_man_dbg_abi, hdr);
+
+ if (sdev->first_boot)
+ dev_dbg(sdev->dev,
+ "Firmware: DBG_ABI %d:%d:%d\n",
+ SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
+ SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
+ SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
+
+ return 0;
+}
+
+static int ipc3_fw_ext_man_get_config_data(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_config_data *config =
+ container_of(hdr, struct sof_ext_man_config_data, hdr);
+ const struct sof_config_elem *elem;
+ int elems_counter;
+ int elems_size;
+ int ret = 0;
+ int i;
+
+ /* calculate elements counter */
+ elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
+ elems_counter = elems_size / sizeof(struct sof_config_elem);
+
+ dev_dbg(sdev->dev, "%s can hold up to %d config elements\n",
+ __func__, elems_counter);
+
+ for (i = 0; i < elems_counter; ++i) {
+ elem = &config->elems[i];
+ dev_dbg(sdev->dev, "%s get index %d token %d val %d\n",
+ __func__, i, elem->token, elem->value);
+ switch (elem->token) {
+ case SOF_EXT_MAN_CONFIG_EMPTY:
+ /* unused memory space is zero filled - mapped to EMPTY elements */
+ break;
+ case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
+ /* TODO: use ipc msg size from config data */
+ break;
+ case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
+ if (sdev->first_boot && elem->value)
+ ret = snd_sof_dbg_memory_info_init(sdev);
+ break;
+ default:
+ dev_info(sdev->dev,
+ "Unknown firmware configuration token %d value %d",
+ elem->token, elem->value);
+ break;
+ }
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "%s: processing failed for token %d value %#x, %d\n",
+ __func__, elem->token, elem->value, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t ipc3_fw_ext_man_size(const struct firmware *fw)
+{
+ const struct sof_ext_man_header *head;
+
+ head = (struct sof_ext_man_header *)fw->data;
+
+ /*
+ * assert fw size is big enough to contain extended manifest header,
+ * it prevents from reading unallocated memory from `head` in following
+ * step.
+ */
+ if (fw->size < sizeof(*head))
+ return -EINVAL;
+
+ /*
+ * When fw points to extended manifest,
+ * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
+ */
+ if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
+ return head->full_size;
+
+ /* otherwise given fw don't have an extended manifest */
+ return 0;
+}
+
+static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ const struct firmware *fw = plat_data->fw;
+ const struct sof_ext_man_elem_header *elem_hdr;
+ const struct sof_ext_man_header *head;
+ ssize_t ext_man_size;
+ ssize_t remaining;
+ uintptr_t iptr;
+ int ret = 0;
+
+ head = (struct sof_ext_man_header *)fw->data;
+ remaining = head->full_size - head->header_size;
+ ext_man_size = ipc3_fw_ext_man_size(fw);
+
+ /* Assert firmware starts with extended manifest */
+ if (ext_man_size <= 0)
+ return ext_man_size;
+
+ /* incompatible version */
+ if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
+ head->header_version)) {
+ dev_err(sdev->dev,
+ "extended manifest version %#x differ from used %#x\n",
+ head->header_version, SOF_EXT_MAN_VERSION);
+ return -EINVAL;
+ }
+
+ /* get first extended manifest element header */
+ iptr = (uintptr_t)fw->data + head->header_size;
+
+ while (remaining > sizeof(*elem_hdr)) {
+ elem_hdr = (struct sof_ext_man_elem_header *)iptr;
+
+ dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n",
+ elem_hdr->type, elem_hdr->size);
+
+ if (elem_hdr->size < sizeof(*elem_hdr) ||
+ elem_hdr->size > remaining) {
+ dev_err(sdev->dev,
+ "invalid sof_ext_man header size, type %d size %#x\n",
+ elem_hdr->type, elem_hdr->size);
+ return -EINVAL;
+ }
+
+ /* process structure data */
+ switch (elem_hdr->type) {
+ case SOF_EXT_MAN_ELEM_FW_VERSION:
+ ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_WINDOW:
+ ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_CC_VERSION:
+ ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_DBG_ABI:
+ ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_CONFIG_DATA:
+ ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
+ ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
+ break;
+ default:
+ dev_info(sdev->dev,
+ "unknown sof_ext_man header type %d size %#x\n",
+ elem_hdr->type, elem_hdr->size);
+ break;
+ }
+
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "failed to parse sof_ext_man header type %d size %#x\n",
+ elem_hdr->type, elem_hdr->size);
+ return ret;
+ }
+
+ remaining -= elem_hdr->size;
+ iptr += elem_hdr->size;
+ }
+
+ if (remaining) {
+ dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
+ return -EINVAL;
+ }
+
+ return ext_man_size;
+}
+
+/* generic module parser for mmaped DSPs */
+static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev,
+ struct snd_sof_mod_hdr *module)
+{
+ struct snd_sof_blk_hdr *block;
+ int count, ret;
+ u32 offset;
+ size_t remaining;
+
+ dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n",
+ module->size, module->num_blocks, module->type);
+
+ block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
+
+ /* module->size doesn't include header size */
+ remaining = module->size;
+ for (count = 0; count < module->num_blocks; count++) {
+ /* check for wrap */
+ if (remaining < sizeof(*block)) {
+ dev_err(sdev->dev, "not enough data remaining\n");
+ return -EINVAL;
+ }
+
+ /* minus header size of block */
+ remaining -= sizeof(*block);
+
+ if (block->size == 0) {
+ dev_warn(sdev->dev,
+ "warning: block %d size zero\n", count);
+ dev_warn(sdev->dev, " type %#x offset %#x\n",
+ block->type, block->offset);
+ continue;
+ }
+
+ switch (block->type) {
+ case SOF_FW_BLK_TYPE_RSRVD0:
+ case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
+ continue; /* not handled atm */
+ case SOF_FW_BLK_TYPE_IRAM:
+ case SOF_FW_BLK_TYPE_DRAM:
+ case SOF_FW_BLK_TYPE_SRAM:
+ offset = block->offset;
+ break;
+ default:
+ dev_err(sdev->dev, "%s: bad type %#x for block %#x\n",
+ __func__, block->type, count);
+ return -EINVAL;
+ }
+
+ dev_dbg(sdev->dev, "block %d type %#x size %#x ==> offset %#x\n",
+ count, block->type, block->size, offset);
+
+ /* checking block->size to avoid unaligned access */
+ if (block->size % sizeof(u32)) {
+ dev_err(sdev->dev, "%s: invalid block size %#x\n",
+ __func__, block->size);
+ return -EINVAL;
+ }
+ ret = snd_sof_dsp_block_write(sdev, block->type, offset,
+ block + 1, block->size);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: write to block type %#x failed\n",
+ __func__, block->type);
+ return ret;
+ }
+
+ if (remaining < block->size) {
+ dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
+ return -EINVAL;
+ }
+
+ /* minus body size of block */
+ remaining -= block->size;
+ /* next block */
+ block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
+ + block->size);
+ }
+
+ return 0;
+}
+
+static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ const struct firmware *fw = plat_data->fw;
+ struct snd_sof_fw_header *header;
+ struct snd_sof_mod_hdr *module;
+ int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr);
+ size_t remaining;
+ int ret, count;
+
+ if (!plat_data->fw)
+ return -EINVAL;
+
+ header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
+ load_module = sof_ops(sdev)->load_module;
+ if (!load_module) {
+ dev_dbg(sdev->dev, "%s: Using generic module loading\n", __func__);
+ load_module = sof_ipc3_parse_module_memcpy;
+ } else {
+ dev_dbg(sdev->dev, "%s: Using custom module loading\n", __func__);
+ }
+
+ /* parse each module */
+ module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset +
+ sizeof(*header));
+ remaining = fw->size - sizeof(*header) - plat_data->fw_offset;
+ /* check for wrap */
+ if (remaining > fw->size) {
+ dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__);
+ return -EINVAL;
+ }
+
+ for (count = 0; count < header->num_modules; count++) {
+ /* check for wrap */
+ if (remaining < sizeof(*module)) {
+ dev_err(sdev->dev, "%s: not enough data for a module\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* minus header size of module */
+ remaining -= sizeof(*module);
+
+ /* module */
+ ret = load_module(sdev, module);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: invalid module %d\n", __func__, count);
+ return ret;
+ }
+
+ if (remaining < module->size) {
+ dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
+ return -EINVAL;
+ }
+
+ /* minus body size of module */
+ remaining -= module->size;
+ module = (struct snd_sof_mod_hdr *)((u8 *)module +
+ sizeof(*module) + module->size);
+ }
+
+ return 0;
+}
+
+static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ const struct firmware *fw = plat_data->fw;
+ struct snd_sof_fw_header *header;
+ size_t fw_size = fw->size - plat_data->fw_offset;
+
+ if (fw->size <= plat_data->fw_offset) {
+ dev_err(sdev->dev,
+ "firmware size must be greater than firmware offset\n");
+ return -EINVAL;
+ }
+
+ /* Read the header information from the data pointer */
+ header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
+
+ /* verify FW sig */
+ if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
+ dev_err(sdev->dev, "invalid firmware signature\n");
+ return -EINVAL;
+ }
+
+ /* check size is valid */
+ if (fw_size != header->file_size + sizeof(*header)) {
+ dev_err(sdev->dev,
+ "invalid filesize mismatch got 0x%zx expected 0x%zx\n",
+ fw_size, header->file_size + sizeof(*header));
+ return -EINVAL;
+ }
+
+ dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
+ header->file_size, header->num_modules,
+ header->abi, sizeof(*header));
+
+ return 0;
+}
+
+const struct sof_ipc_fw_loader_ops ipc3_loader_ops = {
+ .validate = sof_ipc3_validate_firmware,
+ .parse_ext_manifest = sof_ipc3_fw_parse_ext_man,
+ .load_fw_to_dsp = sof_ipc3_load_fw_to_dsp,
+};
diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h
deleted file mode 100644
index a4784626a3d7..000000000000
--- a/sound/soc/sof/ipc3-ops.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
-/*
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * Copyright(c) 2021 Intel Corporation. All rights reserved.
- *
- * Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
- */
-
-#ifndef __SOUND_SOC_SOF_IPC3_OPS_H
-#define __SOUND_SOC_SOF_IPC3_OPS_H
-
-#include "sof-priv.h"
-
-extern const struct sof_ipc_tplg_ops ipc3_tplg_ops;
-extern const struct sof_ipc_ops ipc3_ops;
-extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
-extern const struct sof_ipc_pcm_ops ipc3_pcm_ops;
-
-#endif
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index 58b75943cf6d..c8774a891d6f 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -8,7 +8,7 @@
//
#include <sound/pcm_params.h>
-#include "ipc3-ops.h"
+#include "ipc3-priv.h"
#include "ops.h"
#include "sof-priv.h"
#include "sof-audio.h"
@@ -34,8 +34,7 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
stream.comp_id = spcm->stream[substream->stream].comp_id;
/* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
- sizeof(stream), &reply, sizeof(reply));
+ return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
}
static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
@@ -119,7 +118,7 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
/* send hw_params IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+ ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
&ipc_params_reply, sizeof(ipc_params_reply));
if (ret < 0) {
dev_err(component->dev, "HW params ipc failed for stream %d\n",
@@ -175,8 +174,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
}
/* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
- sizeof(stream), &reply, sizeof(reply));
+ return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
}
static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h
new file mode 100644
index 000000000000..f5044202f3c5
--- /dev/null
+++ b/sound/soc/sof/ipc3-priv.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __SOUND_SOC_SOF_IPC3_PRIV_H
+#define __SOUND_SOC_SOF_IPC3_PRIV_H
+
+#include "sof-priv.h"
+
+/* IPC3 specific ops */
+extern const struct sof_ipc_pcm_ops ipc3_pcm_ops;
+extern const struct sof_ipc_tplg_ops ipc3_tplg_ops;
+extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
+extern const struct sof_ipc_fw_loader_ops ipc3_loader_ops;
+extern const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops;
+
+/* helpers for fw_ready and ext_manifest parsing */
+int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev,
+ const struct sof_ipc_ext_data_hdr *ext_hdr);
+int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev,
+ const struct sof_ipc_ext_data_hdr *ext_hdr);
+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);
+
+/* dtrace platform callback wrappers */
+static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmatb,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params)
+{
+ struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
+
+ if (dsp_ops->trace_init)
+ return dsp_ops->trace_init(sdev, dmatb, dtrace_params);
+
+ return 0;
+}
+
+static inline int sof_dtrace_host_release(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
+
+ if (dsp_ops->trace_release)
+ return dsp_ops->trace_release(sdev);
+
+ return 0;
+}
+
+static inline int sof_dtrace_host_trigger(struct snd_sof_dev *sdev, int cmd)
+{
+ struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
+
+ if (dsp_ops->trace_trigger)
+ return dsp_ops->trace_trigger(sdev, cmd);
+
+ return 0;
+}
+
+#endif
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 2f8450a8c0a1..043554d7cb4a 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -11,7 +11,7 @@
#include <sound/pcm_params.h>
#include "sof-priv.h"
#include "sof-audio.h"
-#include "ipc3-ops.h"
+#include "ipc3-priv.h"
#include "ops.h"
/* Full volume for default values */
@@ -20,7 +20,8 @@
struct sof_widget_data {
int ctrl_type;
int ipc_cmd;
- struct sof_abi_hdr *pdata;
+ void *pdata;
+ size_t pdata_size;
struct snd_sof_control *control;
};
@@ -784,16 +785,26 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
}
cdata = wdata[i].control->ipc_control_data;
- wdata[i].pdata = cdata->data;
- if (!wdata[i].pdata)
- return -EINVAL;
- /* make sure data is valid - data can be updated at runtime */
- if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES &&
- wdata[i].pdata->magic != SOF_ABI_MAGIC)
- return -EINVAL;
+ if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES) {
+ /* make sure data is valid - data can be updated at runtime */
+ if (cdata->data->magic != SOF_ABI_MAGIC)
+ return -EINVAL;
+
+ wdata[i].pdata = cdata->data->data;
+ wdata[i].pdata_size = cdata->data->size;
+ } else {
+ /* points to the control data union */
+ wdata[i].pdata = cdata->chanv;
+ /*
+ * wdata[i].control->size is calculated with struct_size
+ * and includes the size of struct sof_ipc_ctrl_data
+ */
+ wdata[i].pdata_size = wdata[i].control->size -
+ sizeof(struct sof_ipc_ctrl_data);
+ }
- *size += wdata[i].pdata->size;
+ *size += wdata[i].pdata_size;
/* get data type */
switch (cdata->cmd) {
@@ -876,10 +887,12 @@ static int sof_process_load(struct snd_soc_component *scomp,
*/
if (ipc_data_size) {
for (i = 0; i < widget->num_kcontrols; i++) {
- memcpy(&process->data[offset],
- wdata[i].pdata->data,
- wdata[i].pdata->size);
- offset += wdata[i].pdata->size;
+ if (!wdata[i].pdata_size)
+ continue;
+
+ memcpy(&process->data[offset], wdata[i].pdata,
+ wdata[i].pdata_size);
+ offset += wdata[i].pdata_size;
}
}
@@ -1551,8 +1564,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
sroute->sink_widget->widget->name);
/* send ipc */
- ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect),
- &reply, sizeof(reply));
+ ret = sof_ipc_tx_message(sdev->ipc, &connect, sizeof(connect), &reply, sizeof(reply));
if (ret < 0)
dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__,
sroute->src_widget->widget->name, sroute->sink_widget->widget->name);
@@ -1592,6 +1604,7 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
if (scontrol->priv_size > 0) {
memcpy(cdata->data, scontrol->priv, scontrol->priv_size);
kfree(scontrol->priv);
+ scontrol->priv = NULL;
if (cdata->data->magic != SOF_ABI_MAGIC) {
dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic);
@@ -1697,7 +1710,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro
fcomp.id = scontrol->comp_id;
/* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0);
+ return sof_ipc_tx_message(sdev->ipc, &fcomp, sizeof(fcomp), NULL, 0);
}
/* send pcm params ipc */
@@ -1749,7 +1762,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in
}
/* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+ ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
&ipc_params_reply, sizeof(ipc_params_reply));
if (ret < 0)
dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,
@@ -1773,8 +1786,7 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c
stream.comp_id = swidget->comp_id;
/* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
- sizeof(stream), &reply, sizeof(reply));
+ ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
@@ -1902,8 +1914,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w
ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;
ready.comp_id = swidget->comp_id;
- ret = sof_ipc_tx_message(sdev->ipc, ready.hdr.cmd, &ready, sizeof(ready), &reply,
- sizeof(reply));
+ ret = sof_ipc_tx_message(sdev->ipc, &ready, sizeof(ready), &reply, sizeof(reply));
if (ret < 0)
return ret;
@@ -1939,7 +1950,7 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
break;
}
- ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+ ret = sof_ipc_tx_message(sdev->ipc, &ipc_free, sizeof(ipc_free),
&reply, sizeof(reply));
if (ret < 0)
dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name);
@@ -2003,7 +2014,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
/* only send the IPC if the widget is set up in the DSP */
if (swidget->use_count > 0) {
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ ret = sof_ipc_tx_message(sdev->ipc, config, config->hdr.size,
&reply, sizeof(reply));
if (ret < 0)
dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);
@@ -2028,7 +2039,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_dai_private_data *dai_data = dai->private;
struct sof_ipc_comp *comp = &dai_data->comp_dai->comp;
- ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai,
+ ret = sof_ipc_tx_message(sdev->ipc, dai_data->comp_dai,
comp->hdr.size, &reply, sizeof(reply));
break;
}
@@ -2037,8 +2048,8 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc_pipe_new *pipeline;
pipeline = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
- sizeof(*pipeline), &reply, sizeof(reply));
+ ret = sof_ipc_tx_message(sdev->ipc, pipeline, sizeof(*pipeline),
+ &reply, sizeof(reply));
break;
}
default:
@@ -2046,7 +2057,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc_cmd_hdr *hdr;
hdr = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
+ ret = sof_ipc_tx_message(sdev->ipc, swidget->private, hdr->size,
&reply, sizeof(reply));
break;
}
@@ -2249,6 +2260,18 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
list_for_each_entry(sroute, &sdev->route_list, list)
sroute->setup = false;
+ /*
+ * before suspending, make sure the refcounts are all zeroed out. There's no way
+ * to recover at this point but this will help root cause bad sequences leading to
+ * more issues on resume
+ */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->use_count != 0) {
+ dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n",
+ __func__, swidget->widget->name, swidget->use_count);
+ }
+ }
+
return 0;
}
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 03e914b62728..dff5feaad370 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -7,8 +7,1035 @@
//
//
+#include <sound/sof/stream.h>
+#include <sound/sof/control.h>
#include "sof-priv.h"
-#include "ipc3-ops.h"
+#include "sof-audio.h"
+#include "ipc3-priv.h"
+#include "ops.h"
+
+typedef void (*ipc3_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
+static void ipc3_log_header(struct device *dev, u8 *text, u32 cmd)
+{
+ u8 *str;
+ u8 *str2 = NULL;
+ u32 glb;
+ u32 type;
+ bool vdbg = false;
+
+ glb = cmd & SOF_GLB_TYPE_MASK;
+ type = cmd & SOF_CMD_TYPE_MASK;
+
+ switch (glb) {
+ case SOF_IPC_GLB_REPLY:
+ str = "GLB_REPLY"; break;
+ case SOF_IPC_GLB_COMPOUND:
+ str = "GLB_COMPOUND"; break;
+ case SOF_IPC_GLB_TPLG_MSG:
+ str = "GLB_TPLG_MSG";
+ switch (type) {
+ case SOF_IPC_TPLG_COMP_NEW:
+ str2 = "COMP_NEW"; break;
+ case SOF_IPC_TPLG_COMP_FREE:
+ str2 = "COMP_FREE"; break;
+ case SOF_IPC_TPLG_COMP_CONNECT:
+ str2 = "COMP_CONNECT"; break;
+ case SOF_IPC_TPLG_PIPE_NEW:
+ str2 = "PIPE_NEW"; break;
+ case SOF_IPC_TPLG_PIPE_FREE:
+ str2 = "PIPE_FREE"; break;
+ case SOF_IPC_TPLG_PIPE_CONNECT:
+ str2 = "PIPE_CONNECT"; break;
+ case SOF_IPC_TPLG_PIPE_COMPLETE:
+ str2 = "PIPE_COMPLETE"; break;
+ case SOF_IPC_TPLG_BUFFER_NEW:
+ str2 = "BUFFER_NEW"; break;
+ case SOF_IPC_TPLG_BUFFER_FREE:
+ str2 = "BUFFER_FREE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_PM_MSG:
+ str = "GLB_PM_MSG";
+ switch (type) {
+ case SOF_IPC_PM_CTX_SAVE:
+ str2 = "CTX_SAVE"; break;
+ case SOF_IPC_PM_CTX_RESTORE:
+ str2 = "CTX_RESTORE"; break;
+ case SOF_IPC_PM_CTX_SIZE:
+ str2 = "CTX_SIZE"; break;
+ case SOF_IPC_PM_CLK_SET:
+ str2 = "CLK_SET"; break;
+ case SOF_IPC_PM_CLK_GET:
+ str2 = "CLK_GET"; break;
+ case SOF_IPC_PM_CLK_REQ:
+ str2 = "CLK_REQ"; break;
+ case SOF_IPC_PM_CORE_ENABLE:
+ str2 = "CORE_ENABLE"; break;
+ case SOF_IPC_PM_GATE:
+ str2 = "GATE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_COMP_MSG:
+ str = "GLB_COMP_MSG";
+ switch (type) {
+ case SOF_IPC_COMP_SET_VALUE:
+ str2 = "SET_VALUE"; break;
+ case SOF_IPC_COMP_GET_VALUE:
+ str2 = "GET_VALUE"; break;
+ case SOF_IPC_COMP_SET_DATA:
+ str2 = "SET_DATA"; break;
+ case SOF_IPC_COMP_GET_DATA:
+ str2 = "GET_DATA"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_STREAM_MSG:
+ str = "GLB_STREAM_MSG";
+ switch (type) {
+ case SOF_IPC_STREAM_PCM_PARAMS:
+ str2 = "PCM_PARAMS"; break;
+ case SOF_IPC_STREAM_PCM_PARAMS_REPLY:
+ str2 = "PCM_REPLY"; break;
+ case SOF_IPC_STREAM_PCM_FREE:
+ str2 = "PCM_FREE"; break;
+ case SOF_IPC_STREAM_TRIG_START:
+ str2 = "TRIG_START"; break;
+ case SOF_IPC_STREAM_TRIG_STOP:
+ str2 = "TRIG_STOP"; break;
+ case SOF_IPC_STREAM_TRIG_PAUSE:
+ str2 = "TRIG_PAUSE"; break;
+ case SOF_IPC_STREAM_TRIG_RELEASE:
+ str2 = "TRIG_RELEASE"; break;
+ case SOF_IPC_STREAM_TRIG_DRAIN:
+ str2 = "TRIG_DRAIN"; break;
+ case SOF_IPC_STREAM_TRIG_XRUN:
+ str2 = "TRIG_XRUN"; break;
+ case SOF_IPC_STREAM_POSITION:
+ vdbg = true;
+ str2 = "POSITION"; break;
+ case SOF_IPC_STREAM_VORBIS_PARAMS:
+ str2 = "VORBIS_PARAMS"; break;
+ case SOF_IPC_STREAM_VORBIS_FREE:
+ str2 = "VORBIS_FREE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_FW_READY:
+ str = "FW_READY"; break;
+ case SOF_IPC_GLB_DAI_MSG:
+ str = "GLB_DAI_MSG";
+ switch (type) {
+ case SOF_IPC_DAI_CONFIG:
+ str2 = "CONFIG"; break;
+ case SOF_IPC_DAI_LOOPBACK:
+ str2 = "LOOPBACK"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_TRACE_MSG:
+ str = "GLB_TRACE_MSG";
+ switch (type) {
+ case SOF_IPC_TRACE_DMA_PARAMS:
+ str2 = "DMA_PARAMS"; break;
+ case SOF_IPC_TRACE_DMA_POSITION:
+ str2 = "DMA_POSITION"; break;
+ case SOF_IPC_TRACE_DMA_PARAMS_EXT:
+ str2 = "DMA_PARAMS_EXT"; break;
+ case SOF_IPC_TRACE_FILTER_UPDATE:
+ str2 = "FILTER_UPDATE"; break;
+ case SOF_IPC_TRACE_DMA_FREE:
+ str2 = "DMA_FREE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_TEST_MSG:
+ str = "GLB_TEST_MSG";
+ switch (type) {
+ case SOF_IPC_TEST_IPC_FLOOD:
+ str2 = "IPC_FLOOD"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_DEBUG:
+ str = "GLB_DEBUG";
+ switch (type) {
+ case SOF_IPC_DEBUG_MEM_USAGE:
+ str2 = "MEM_USAGE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ case SOF_IPC_GLB_PROBE:
+ str = "GLB_PROBE";
+ switch (type) {
+ case SOF_IPC_PROBE_INIT:
+ str2 = "INIT"; break;
+ case SOF_IPC_PROBE_DEINIT:
+ str2 = "DEINIT"; break;
+ case SOF_IPC_PROBE_DMA_ADD:
+ str2 = "DMA_ADD"; break;
+ case SOF_IPC_PROBE_DMA_INFO:
+ str2 = "DMA_INFO"; break;
+ case SOF_IPC_PROBE_DMA_REMOVE:
+ str2 = "DMA_REMOVE"; break;
+ case SOF_IPC_PROBE_POINT_ADD:
+ str2 = "POINT_ADD"; break;
+ case SOF_IPC_PROBE_POINT_INFO:
+ str2 = "POINT_INFO"; break;
+ case SOF_IPC_PROBE_POINT_REMOVE:
+ str2 = "POINT_REMOVE"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
+ default:
+ str = "unknown GLB command"; break;
+ }
+
+ if (str2) {
+ if (vdbg)
+ dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
+ else
+ dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
+ } else {
+ dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str);
+ }
+}
+#else
+static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd)
+{
+ if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG)
+ dev_dbg(dev, "%s: 0x%x\n", text, cmd);
+}
+#endif
+
+static int sof_ipc3_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc_reply *reply;
+ int ret = 0;
+
+ /* get the generic reply */
+ reply = msg->reply_data;
+ snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, reply, sizeof(*reply));
+
+ if (reply->error < 0)
+ return reply->error;
+
+ if (!reply->hdr.size) {
+ /* Reply should always be >= sizeof(struct sof_ipc_reply) */
+ if (msg->reply_size)
+ dev_err(sdev->dev,
+ "empty reply received, expected %zu bytes\n",
+ msg->reply_size);
+ else
+ dev_err(sdev->dev, "empty reply received\n");
+
+ return -EINVAL;
+ }
+
+ if (msg->reply_size > 0) {
+ if (reply->hdr.size == msg->reply_size) {
+ ret = 0;
+ } else if (reply->hdr.size < msg->reply_size) {
+ dev_dbg(sdev->dev,
+ "reply size (%u) is less than expected (%zu)\n",
+ reply->hdr.size, msg->reply_size);
+
+ msg->reply_size = reply->hdr.size;
+ ret = 0;
+ } else {
+ dev_err(sdev->dev,
+ "reply size (%u) exceeds the buffer size (%zu)\n",
+ reply->hdr.size, msg->reply_size);
+ ret = -EINVAL;
+ }
+
+ /*
+ * get the full message if reply->hdr.size <= msg->reply_size
+ * and the reply->hdr.size > sizeof(struct sof_ipc_reply)
+ */
+ if (!ret && msg->reply_size > sizeof(*reply))
+ snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset,
+ msg->reply_data, msg->reply_size);
+ }
+
+ return ret;
+}
+
+/* wait for IPC message reply */
+static int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data)
+{
+ struct snd_sof_ipc_msg *msg = &ipc->msg;
+ struct sof_ipc_cmd_hdr *hdr = msg->msg_data;
+ struct snd_sof_dev *sdev = ipc->sdev;
+ int ret;
+
+ /* wait for DSP IPC completion */
+ ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
+ msecs_to_jiffies(sdev->ipc_timeout));
+
+ if (ret == 0) {
+ dev_err(sdev->dev,
+ "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n",
+ hdr->cmd, hdr->size, msg->reply_size);
+ snd_sof_handle_fw_exception(ipc->sdev);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = msg->reply_error;
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n",
+ hdr->cmd, hdr->size, msg->reply_size, ret);
+ } else {
+ ipc3_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
+ if (msg->reply_size)
+ /* copy the data returned from DSP */
+ memcpy(reply_data, msg->reply_data,
+ msg->reply_size);
+ }
+
+ /* re-enable dumps after successful IPC tx */
+ if (sdev->ipc_dump_printed) {
+ sdev->dbg_dump_printed = false;
+ sdev->ipc_dump_printed = false;
+ }
+ }
+
+ return ret;
+}
+
+/* send IPC message from host to DSP */
+static int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc,
+ void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes)
+{
+ struct sof_ipc_cmd_hdr *hdr = msg_data;
+ struct snd_sof_dev *sdev = ipc->sdev;
+ int ret;
+
+ ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);
+
+ if (ret) {
+ dev_err_ratelimited(sdev->dev,
+ "%s: ipc message send for %#x failed: %d\n",
+ __func__, hdr->cmd, ret);
+ return ret;
+ }
+
+ ipc3_log_header(sdev->dev, "ipc tx", hdr->cmd);
+
+ /* now wait for completion */
+ return ipc3_wait_tx_done(ipc, reply_data);
+}
+
+static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes, bool no_pm)
+{
+ struct snd_sof_ipc *ipc = sdev->ipc;
+ int ret;
+
+ if (!msg_data || msg_bytes < sizeof(struct sof_ipc_cmd_hdr)) {
+ dev_err_ratelimited(sdev->dev, "No IPC message to send\n");
+ return -EINVAL;
+ }
+
+ if (!no_pm) {
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ /* ensure the DSP is in D0 before sending a new IPC */
+ ret = snd_sof_dsp_set_power_state(sdev, &target_state);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: resuming DSP failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ /* Serialise IPC TX */
+ mutex_lock(&ipc->tx_mutex);
+
+ ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
+
+ mutex_unlock(&ipc->tx_mutex);
+
+ return ret;
+}
+
+static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t data_bytes,
+ bool set)
+{
+ size_t msg_bytes, hdr_bytes, payload_size, send_bytes;
+ struct sof_ipc_ctrl_data *cdata = data;
+ struct sof_ipc_ctrl_data *cdata_chunk;
+ struct snd_sof_ipc *ipc = sdev->ipc;
+ size_t offset = 0;
+ u8 *src, *dst;
+ u32 num_msg;
+ int ret = 0;
+ int i;
+
+ if (!cdata || data_bytes < sizeof(*cdata))
+ return -EINVAL;
+
+ if ((cdata->rhdr.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_COMP_MSG) {
+ dev_err(sdev->dev, "%s: Not supported message type of %#x\n",
+ __func__, cdata->rhdr.hdr.cmd);
+ return -EINVAL;
+ }
+
+ /* send normal size ipc in one part */
+ if (cdata->rhdr.hdr.size <= ipc->max_payload_size)
+ return sof_ipc3_tx_msg(sdev, cdata, cdata->rhdr.hdr.size,
+ cdata, cdata->rhdr.hdr.size, false);
+
+ cdata_chunk = kzalloc(ipc->max_payload_size, GFP_KERNEL);
+ if (!cdata_chunk)
+ return -ENOMEM;
+
+ switch (cdata->type) {
+ case SOF_CTRL_TYPE_VALUE_CHAN_GET:
+ case SOF_CTRL_TYPE_VALUE_CHAN_SET:
+ hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
+ if (set) {
+ src = (u8 *)cdata->chanv;
+ dst = (u8 *)cdata_chunk->chanv;
+ } else {
+ src = (u8 *)cdata_chunk->chanv;
+ dst = (u8 *)cdata->chanv;
+ }
+ break;
+ case SOF_CTRL_TYPE_DATA_GET:
+ case SOF_CTRL_TYPE_DATA_SET:
+ hdr_bytes = sizeof(struct sof_ipc_ctrl_data) + sizeof(struct sof_abi_hdr);
+ if (set) {
+ src = (u8 *)cdata->data->data;
+ dst = (u8 *)cdata_chunk->data->data;
+ } else {
+ src = (u8 *)cdata_chunk->data->data;
+ dst = (u8 *)cdata->data->data;
+ }
+ break;
+ default:
+ kfree(cdata_chunk);
+ return -EINVAL;
+ }
+
+ msg_bytes = cdata->rhdr.hdr.size - hdr_bytes;
+ payload_size = ipc->max_payload_size - hdr_bytes;
+ num_msg = DIV_ROUND_UP(msg_bytes, payload_size);
+
+ /* copy the header data */
+ memcpy(cdata_chunk, cdata, hdr_bytes);
+
+ /* Serialise IPC TX */
+ mutex_lock(&sdev->ipc->tx_mutex);
+
+ /* copy the payload data in a loop */
+ for (i = 0; i < num_msg; i++) {
+ send_bytes = min(msg_bytes, payload_size);
+ cdata_chunk->num_elems = send_bytes;
+ cdata_chunk->rhdr.hdr.size = hdr_bytes + send_bytes;
+ cdata_chunk->msg_index = i;
+ msg_bytes -= send_bytes;
+ cdata_chunk->elems_remaining = msg_bytes;
+
+ if (set)
+ memcpy(dst, src + offset, send_bytes);
+
+ ret = ipc3_tx_msg_unlocked(sdev->ipc,
+ cdata_chunk, cdata_chunk->rhdr.hdr.size,
+ cdata_chunk, cdata_chunk->rhdr.hdr.size);
+ if (ret < 0)
+ break;
+
+ if (!set)
+ memcpy(dst + offset, src, send_bytes);
+
+ offset += payload_size;
+ }
+
+ mutex_unlock(&sdev->ipc->tx_mutex);
+
+ kfree(cdata_chunk);
+
+ return ret;
+}
+
+int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev,
+ const struct sof_ipc_ext_data_hdr *ext_hdr)
+{
+ const struct sof_ipc_window *w =
+ container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
+
+ if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
+ return -EINVAL;
+
+ if (sdev->info_window) {
+ if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) {
+ dev_err(sdev->dev, "mismatch between window descriptor from extended manifest and mailbox");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ /* keep a local copy of the data */
+ sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, GFP_KERNEL);
+ if (!sdev->info_window)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev,
+ const struct sof_ipc_ext_data_hdr *ext_hdr)
+{
+ int ret;
+
+ const struct sof_ipc_cc_version *cc =
+ container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
+
+ if (sdev->cc_version) {
+ if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
+ dev_err(sdev->dev,
+ "Receive diverged cc_version descriptions");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ dev_dbg(sdev->dev,
+ "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
+ cc->name, cc->major, cc->minor, cc->micro, cc->desc, cc->optim);
+
+ /* create read-only cc_version debugfs to store compiler version info */
+ /* use local copy of the cc_version to prevent data corruption */
+ if (sdev->first_boot) {
+ sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
+ GFP_KERNEL);
+
+ if (!sdev->cc_version)
+ return -ENOMEM;
+
+ memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
+ ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
+ cc->ext_hdr.hdr.size,
+ "cc_version", 0444);
+
+ /* errors are only due to memory allocation, not debugfs */
+ if (ret < 0) {
+ dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* parse the extended FW boot data structures from FW boot message */
+static int ipc3_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
+{
+ struct sof_ipc_ext_data_hdr *ext_hdr;
+ void *ext_data;
+ int ret = 0;
+
+ ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!ext_data)
+ return -ENOMEM;
+
+ /* get first header */
+ snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
+ sizeof(*ext_hdr));
+ ext_hdr = ext_data;
+
+ while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
+ /* read in ext structure */
+ snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM,
+ offset + sizeof(*ext_hdr),
+ (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
+ ext_hdr->hdr.size - sizeof(*ext_hdr));
+
+ dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
+ ext_hdr->type, ext_hdr->hdr.size);
+
+ /* process structure data */
+ switch (ext_hdr->type) {
+ case SOF_IPC_EXT_WINDOW:
+ ret = sof_ipc3_get_ext_windows(sdev, ext_hdr);
+ break;
+ case SOF_IPC_EXT_CC_INFO:
+ ret = sof_ipc3_get_cc_info(sdev, ext_hdr);
+ break;
+ case SOF_IPC_EXT_UNUSED:
+ case SOF_IPC_EXT_PROBE_INFO:
+ case SOF_IPC_EXT_USER_ABI_INFO:
+ /* They are supported but we don't do anything here */
+ break;
+ default:
+ dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n",
+ ext_hdr->type, ext_hdr->hdr.size);
+ ret = 0;
+ break;
+ }
+
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to parse ext data type %d\n",
+ ext_hdr->type);
+ break;
+ }
+
+ /* move to next header */
+ offset += ext_hdr->hdr.size;
+ snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
+ sizeof(*ext_hdr));
+ ext_hdr = ext_data;
+ }
+
+ kfree(ext_data);
+ return ret;
+}
+
+static void ipc3_get_windows(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc_window_elem *elem;
+ u32 outbox_offset = 0;
+ u32 stream_offset = 0;
+ u32 inbox_offset = 0;
+ u32 outbox_size = 0;
+ u32 stream_size = 0;
+ u32 inbox_size = 0;
+ u32 debug_size = 0;
+ u32 debug_offset = 0;
+ int window_offset;
+ int i;
+
+ if (!sdev->info_window) {
+ dev_err(sdev->dev, "%s: No window info present\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < sdev->info_window->num_windows; i++) {
+ elem = &sdev->info_window->window[i];
+
+ window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
+ if (window_offset < 0) {
+ dev_warn(sdev->dev, "No offset for window %d\n", elem->id);
+ continue;
+ }
+
+ switch (elem->type) {
+ case SOF_IPC_REGION_UPBOX:
+ inbox_offset = window_offset + elem->offset;
+ inbox_size = elem->size;
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ inbox_offset,
+ elem->size, "inbox",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ case SOF_IPC_REGION_DOWNBOX:
+ outbox_offset = window_offset + elem->offset;
+ outbox_size = elem->size;
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ outbox_offset,
+ elem->size, "outbox",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ case SOF_IPC_REGION_TRACE:
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "etrace",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ case SOF_IPC_REGION_DEBUG:
+ debug_offset = window_offset + elem->offset;
+ debug_size = elem->size;
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "debug",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ case SOF_IPC_REGION_STREAM:
+ stream_offset = window_offset + elem->offset;
+ stream_size = elem->size;
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ stream_offset,
+ elem->size, "stream",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ case SOF_IPC_REGION_REGS:
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "regs",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ case SOF_IPC_REGION_EXCEPTION:
+ sdev->dsp_oops_offset = window_offset + elem->offset;
+ snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
+ window_offset + elem->offset,
+ elem->size, "exception",
+ SOF_DEBUGFS_ACCESS_D0_ONLY);
+ break;
+ default:
+ dev_err(sdev->dev, "%s: Illegal window info: %u\n",
+ __func__, elem->type);
+ return;
+ }
+ }
+
+ if (outbox_size == 0 || inbox_size == 0) {
+ dev_err(sdev->dev, "%s: Illegal mailbox window\n", __func__);
+ return;
+ }
+
+ sdev->dsp_box.offset = inbox_offset;
+ sdev->dsp_box.size = inbox_size;
+
+ sdev->host_box.offset = outbox_offset;
+ sdev->host_box.size = outbox_size;
+
+ sdev->stream_box.offset = stream_offset;
+ sdev->stream_box.size = stream_size;
+
+ sdev->debug_box.offset = debug_offset;
+ sdev->debug_box.size = debug_size;
+
+ dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
+ inbox_offset, inbox_size);
+ dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
+ outbox_offset, outbox_size);
+ dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
+ stream_offset, stream_size);
+ dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n",
+ debug_offset, debug_size);
+}
+
+static int ipc3_init_reply_data_buffer(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
+
+ msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!msg->reply_data)
+ return -ENOMEM;
+
+ sdev->ipc->max_payload_size = SOF_IPC_MSG_MAX_SIZE;
+
+ return 0;
+}
+
+int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+ struct sof_ipc_fw_version *v = &ready->version;
+
+ dev_info(sdev->dev,
+ "Firmware info: version %d:%d:%d-%s\n", v->major, v->minor,
+ v->micro, v->tag);
+ dev_info(sdev->dev,
+ "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
+ SOF_ABI_VERSION_MAJOR(v->abi_version),
+ SOF_ABI_VERSION_MINOR(v->abi_version),
+ SOF_ABI_VERSION_PATCH(v->abi_version),
+ SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
+
+ if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) {
+ dev_err(sdev->dev, "incompatible FW ABI version\n");
+ return -EINVAL;
+ }
+
+ if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) {
+ if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
+ dev_warn(sdev->dev, "FW ABI is more recent than kernel\n");
+ } else {
+ dev_err(sdev->dev, "FW ABI is more recent than kernel\n");
+ return -EINVAL;
+ }
+ }
+
+ if (ready->flags & SOF_IPC_INFO_BUILD) {
+ dev_info(sdev->dev,
+ "Firmware debug build %d on %s-%s - options:\n"
+ " GDB: %s\n"
+ " lock debug: %s\n"
+ " lock vdebug: %s\n",
+ v->build, v->date, v->time,
+ (ready->flags & SOF_IPC_INFO_GDB) ?
+ "enabled" : "disabled",
+ (ready->flags & SOF_IPC_INFO_LOCKS) ?
+ "enabled" : "disabled",
+ (ready->flags & SOF_IPC_INFO_LOCKSV) ?
+ "enabled" : "disabled");
+ }
+
+ /* copy the fw_version into debugfs at first boot */
+ memcpy(&sdev->fw_version, v, sizeof(*v));
+
+ return 0;
+}
+
+static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd)
+{
+ struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
+ int offset;
+ int ret;
+
+ /* mailbox must be on 4k boundary */
+ offset = snd_sof_dsp_get_mailbox_offset(sdev);
+ if (offset < 0) {
+ dev_err(sdev->dev, "%s: no mailbox offset\n", __func__);
+ return offset;
+ }
+
+ dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset);
+
+ /* no need to re-check version/ABI for subsequent boots */
+ if (!sdev->first_boot)
+ return 0;
+
+ /*
+ * copy data from the DSP FW ready offset
+ * Subsequent error handling is not needed for BLK_TYPE_SRAM
+ */
+ ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
+ sizeof(*fw_ready));
+ if (ret) {
+ dev_err(sdev->dev,
+ "Unable to read fw_ready, read from TYPE_SRAM failed\n");
+ return ret;
+ }
+
+ /* make sure ABI version is compatible */
+ ret = sof_ipc3_validate_fw_version(sdev);
+ if (ret < 0)
+ return ret;
+
+ /* now check for extended data */
+ ipc3_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
+
+ ipc3_get_windows(sdev);
+
+ return ipc3_init_reply_data_buffer(sdev);
+}
+
+/* IPC stream position. */
+static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
+{
+ struct snd_soc_component *scomp = sdev->component;
+ struct snd_sof_pcm_stream *stream;
+ struct sof_ipc_stream_posn posn;
+ struct snd_sof_pcm *spcm;
+ int direction, ret;
+
+ spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
+ if (!spcm) {
+ dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n",
+ msg_id);
+ return;
+ }
+
+ stream = &spcm->stream[direction];
+ ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ if (ret < 0) {
+ dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
+ return;
+ }
+
+ dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
+ posn.host_posn, posn.dai_posn, posn.wallclock);
+
+ memcpy(&stream->posn, &posn, sizeof(posn));
+
+ if (spcm->pcm.compress)
+ snd_sof_compr_fragment_elapsed(stream->cstream);
+ else if (stream->substream->runtime &&
+ !stream->substream->runtime->no_period_wakeup)
+ /* only inform ALSA for period_wakeup mode */
+ snd_sof_pcm_period_elapsed(stream->substream);
+}
+
+/* DSP notifies host of an XRUN within FW */
+static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id)
+{
+ struct snd_soc_component *scomp = sdev->component;
+ struct snd_sof_pcm_stream *stream;
+ struct sof_ipc_stream_posn posn;
+ struct snd_sof_pcm *spcm;
+ int direction, ret;
+
+ spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
+ if (!spcm) {
+ dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n",
+ msg_id);
+ return;
+ }
+
+ stream = &spcm->stream[direction];
+ ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ if (ret < 0) {
+ dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
+ return;
+ }
+
+ dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n",
+ posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
+
+#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
+ /* stop PCM on XRUN - used for pipeline debug */
+ memcpy(&stream->posn, &posn, sizeof(posn));
+ snd_pcm_stop_xrun(stream->substream);
+#endif
+}
+
+/* stream notifications from firmware */
+static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
+{
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+ u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
+
+ switch (msg_type) {
+ case SOF_IPC_STREAM_POSITION:
+ ipc3_period_elapsed(sdev, msg_id);
+ break;
+ case SOF_IPC_STREAM_TRIG_XRUN:
+ ipc3_xrun(sdev, msg_id);
+ break;
+ default:
+ dev_err(sdev->dev, "unhandled stream message %#x\n",
+ msg_id);
+ break;
+ }
+}
+
+/* component notifications from firmware */
+static void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
+{
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+
+ switch (msg_type) {
+ case SOF_IPC_COMP_GET_VALUE:
+ case SOF_IPC_COMP_GET_DATA:
+ break;
+ default:
+ dev_err(sdev->dev, "unhandled component message %#x\n", msg_type);
+ return;
+ }
+
+ if (tplg_ops->control->update)
+ tplg_ops->control->update(sdev, msg_buf);
+}
+
+static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
+{
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+
+ switch (msg_type) {
+ case SOF_IPC_TRACE_DMA_POSITION:
+ ipc3_dtrace_posn_update(sdev, msg_buf);
+ break;
+ default:
+ dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type);
+ break;
+ }
+}
+
+/* DSP firmware has sent host a message */
+static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
+{
+ 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;
+ }
+
+ if (hdr.size < sizeof(hdr)) {
+ dev_err(sdev->dev, "The received message size is invalid\n");
+ return;
+ }
+
+ ipc3_log_header(sdev->dev, "ipc rx", hdr.cmd);
+
+ cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
+
+ /* check message type */
+ switch (cmd) {
+ case SOF_IPC_GLB_REPLY:
+ dev_err(sdev->dev, "ipc reply unknown\n");
+ break;
+ case SOF_IPC_FW_READY:
+ /* check for FW boot completion */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
+ err = ipc3_fw_ready(sdev, cmd);
+ if (err < 0)
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
+ else
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
+
+ /* wake up firmware loader */
+ wake_up(&sdev->boot_wait);
+ }
+ break;
+ case SOF_IPC_GLB_COMPOUND:
+ case SOF_IPC_GLB_TPLG_MSG:
+ case SOF_IPC_GLB_PM_MSG:
+ break;
+ case SOF_IPC_GLB_COMP_MSG:
+ rx_callback = ipc3_comp_notification;
+ break;
+ case SOF_IPC_GLB_STREAM_MSG:
+ rx_callback = ipc3_stream_message;
+ break;
+ case SOF_IPC_GLB_TRACE_MSG:
+ rx_callback = ipc3_trace_message;
+ break;
+ default:
+ dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
+ break;
+ }
+
+ /* read the full message */
+ msg_buf = kmalloc(hdr.size, GFP_KERNEL);
+ if (!msg_buf)
+ return;
+
+ 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);
+
+ ipc3_log_header(sdev->dev, "ipc rx done", hdr.cmd);
+}
static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
{
@@ -19,8 +1046,8 @@ static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
struct sof_ipc_reply reply;
/* send ctx save ipc to dsp */
- return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx,
- sizeof(pm_ctx), &reply, sizeof(reply));
+ return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx),
+ &reply, sizeof(reply), false);
}
static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev)
@@ -42,4 +1069,11 @@ const struct sof_ipc_ops ipc3_ops = {
.tplg = &ipc3_tplg_ops,
.pm = &ipc3_pm_ops,
.pcm = &ipc3_pcm_ops,
+ .fw_loader = &ipc3_loader_ops,
+ .fw_tracing = &ipc3_dtrace_ops,
+
+ .tx_msg = sof_ipc3_tx_msg,
+ .rx_msg = sof_ipc3_rx_msg,
+ .set_get_data = sof_ipc3_set_get_data,
+ .get_reply = sof_ipc3_get_reply,
};
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
new file mode 100644
index 000000000000..9fadae8fd011
--- /dev/null
+++ b/sound/soc/sof/ipc4-loader.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <linux/firmware.h>
+#include <sound/sof/ext_manifest4.h>
+#include <sound/sof/ipc4/header.h>
+#include "ipc4-priv.h"
+#include "sof-audio.h"
+#include "sof-priv.h"
+#include "ops.h"
+
+static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct sof_man4_fw_binary_header *fw_header;
+ const struct firmware *fw = plat_data->fw;
+ struct sof_ext_manifest4_hdr *ext_man_hdr;
+ struct sof_man4_module_config *fm_config;
+ struct sof_ipc4_fw_module *fw_module;
+ struct sof_man4_module *fm_entry;
+ ssize_t remaining;
+ u32 fw_hdr_offset;
+ int i;
+
+ if (!ipc4_data) {
+ dev_err(sdev->dev, "%s: ipc4_data is not available\n", __func__);
+ return -EINVAL;
+ }
+
+ remaining = fw->size;
+ if (remaining <= sizeof(*ext_man_hdr)) {
+ dev_err(sdev->dev, "Firmware size is too small: %zu\n", remaining);
+ return -EINVAL;
+ }
+
+ ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data;
+
+ fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset;
+ if (!fw_hdr_offset)
+ return -EINVAL;
+
+ if (remaining <= ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header)) {
+ dev_err(sdev->dev, "Invalid firmware size %zu, should be at least %zu\n",
+ remaining, ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header));
+ return -EINVAL;
+ }
+
+ fw_header = (struct sof_man4_fw_binary_header *)
+ (fw->data + ext_man_hdr->len + fw_hdr_offset);
+ remaining -= (ext_man_hdr->len + fw_hdr_offset);
+
+ if (remaining <= fw_header->len) {
+ dev_err(sdev->dev, "Invalid fw_header->len %u\n", fw_header->len);
+ return -EINVAL;
+ }
+
+ dev_info(sdev->dev, "Loaded firmware version: %u.%u.%u.%u\n",
+ fw_header->major_version, fw_header->minor_version,
+ fw_header->hotfix_version, fw_header->build_version);
+ dev_dbg(sdev->dev, "Firmware name: %s, header length: %u, module count: %u\n",
+ fw_header->name, fw_header->len, fw_header->num_module_entries);
+
+ ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev,
+ fw_header->num_module_entries,
+ sizeof(*fw_module), GFP_KERNEL);
+ if (!ipc4_data->fw_modules)
+ return -ENOMEM;
+
+ ipc4_data->num_fw_modules = fw_header->num_module_entries;
+ fw_module = ipc4_data->fw_modules;
+
+ fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len);
+ remaining -= fw_header->len;
+
+ if (remaining < fw_header->num_module_entries * sizeof(*fm_entry)) {
+ dev_err(sdev->dev, "Invalid num_module_entries %u\n",
+ fw_header->num_module_entries);
+ return -EINVAL;
+ }
+
+ fm_config = (struct sof_man4_module_config *)
+ (fm_entry + fw_header->num_module_entries);
+ remaining -= (fw_header->num_module_entries * sizeof(*fm_entry));
+ for (i = 0; i < fw_header->num_module_entries; i++) {
+ memcpy(&fw_module->man4_module_entry, fm_entry, sizeof(*fm_entry));
+
+ if (fm_entry->cfg_count) {
+ if (remaining < (fm_entry->cfg_offset + fm_entry->cfg_count) *
+ sizeof(*fm_config)) {
+ dev_err(sdev->dev, "Invalid module cfg_offset %u\n",
+ fm_entry->cfg_offset);
+ return -EINVAL;
+ }
+
+ /* a module's config is always the same size */
+ fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes;
+
+ 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);
+ } else {
+ fw_module->bss_size = 0;
+
+ dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name,
+ &fm_entry->uuid);
+ }
+
+ fw_module->man4_module_entry.id = i;
+ ida_init(&fw_module->m_ida);
+ fw_module->private = NULL;
+
+ fw_module++;
+ fm_entry++;
+ }
+
+ return ext_man_hdr->len;
+}
+
+static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ u32 fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset;
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct sof_man4_fw_binary_header *fw_header;
+ const struct firmware *fw = plat_data->fw;
+ struct sof_ext_manifest4_hdr *ext_man_hdr;
+
+ ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data;
+ fw_header = (struct sof_man4_fw_binary_header *)
+ (fw->data + ext_man_hdr->len + fw_hdr_offset);
+
+ /* TODO: Add firmware verification code here */
+
+ dev_dbg(sdev->dev, "Validated firmware version: %u.%u.%u.%u\n",
+ fw_header->major_version, fw_header->minor_version,
+ fw_header->hotfix_version, fw_header->build_version);
+
+ return 0;
+}
+
+static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
+{
+ const struct sof_ipc_ops *iops = sdev->ipc->ops;
+ struct sof_ipc4_fw_version *fw_ver;
+ struct sof_ipc4_tuple *tuple;
+ struct sof_ipc4_msg msg;
+ size_t offset = 0;
+ int ret;
+
+ /* Get the firmware configuration */
+ msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
+ msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_FW_CONFIG);
+
+ msg.data_size = sdev->ipc->max_payload_size;
+ msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL);
+ if (!msg.data_ptr)
+ return -ENOMEM;
+
+ ret = iops->set_get_data(sdev, &msg, msg.data_size, false);
+ if (ret)
+ goto out;
+
+ while (offset < msg.data_size) {
+ tuple = (struct sof_ipc4_tuple *)((u8 *)msg.data_ptr + offset);
+
+ switch (tuple->type) {
+ case SOF_IPC4_FW_CFG_FW_VERSION:
+ fw_ver = (struct sof_ipc4_fw_version *)tuple->value;
+
+ dev_info(sdev->dev,
+ "Booted firmware version: %u.%u.%u.%u\n",
+ fw_ver->major, fw_ver->minor, fw_ver->hotfix,
+ fw_ver->build);
+ break;
+ case SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES:
+ dev_vdbg(sdev->dev, "DL mailbox size: %u\n", *tuple->value);
+ break;
+ case SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES:
+ dev_vdbg(sdev->dev, "UL mailbox size: %u\n", *tuple->value);
+ break;
+ case SOF_IPC4_FW_CFG_TRACE_LOG_BYTES:
+ dev_vdbg(sdev->dev, "Trace log size: %u\n", *tuple->value);
+ break;
+ default:
+ break;
+ }
+
+ offset += sizeof(*tuple) + tuple->size;
+ }
+
+out:
+ kfree(msg.data_ptr);
+
+ return ret;
+}
+
+const struct sof_ipc_fw_loader_ops ipc4_loader_ops = {
+ .validate = sof_ipc4_validate_firmware,
+ .parse_ext_manifest = sof_ipc4_fw_parse_ext_man,
+ .query_fw_configuration = sof_ipc4_query_fw_configuration,
+};
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
new file mode 100644
index 000000000000..2b71d5675933
--- /dev/null
+++ b/sound/soc/sof/ipc4-priv.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __SOUND_SOC_SOF_IPC4_PRIV_H
+#define __SOUND_SOC_SOF_IPC4_PRIV_H
+
+#include <linux/idr.h>
+#include <sound/sof/ext_manifest4.h>
+#include "sof-priv.h"
+
+/**
+ * struct sof_ipc4_fw_data - IPC4-specific data
+ * @manifest_fw_hdr_offset: FW header offset in the manifest
+ * @num_fw_modules : Number of modules in base FW
+ * @fw_modules: Array of base FW modules
+ */
+struct sof_ipc4_fw_data {
+ u32 manifest_fw_hdr_offset;
+ int num_fw_modules;
+ void *fw_modules;
+};
+
+/**
+ * struct sof_ipc4_fw_module - IPC4 module info
+ * @sof_man4_module : Module info
+ * @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;
+ struct ida m_ida;
+ u32 bss_size;
+ void *private;
+};
+
+extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
+
+#endif
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
new file mode 100644
index 000000000000..658802c86685
--- /dev/null
+++ b/sound/soc/sof/ipc4.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Authors: Rander Wang <rander.wang@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+#include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#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;
+} ipc4_status[] = {
+ {0, "The operation was successful"},
+ {1, "Invalid parameter specified"},
+ {2, "Unknown message type specified"},
+ {3, "Not enough space in the IPC reply buffer to complete the request"},
+ {4, "The system or resource is busy"},
+ {5, "Replaced ADSP IPC PENDING (unused)"},
+ {6, "Unknown error while processing the request"},
+ {7, "Unsupported operation requested"},
+ {8, "Reserved (ADSP_STAGE_UNINITIALIZED removed)"},
+ {9, "Specified resource not found"},
+ {10, "A resource's ID requested to be created is already assigned"},
+ {11, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
+ {12, "Required resource is in invalid state"},
+ {13, "Requested power transition failed to complete"},
+ {14, "Manifest of the library being loaded is invalid"},
+ {15, "Requested service or data is unavailable on the target platform"},
+ {42, "Library target address is out of storage memory range"},
+ {43, "Reserved"},
+ {44, "Image verification by CSE failed"},
+ {100, "General module management error"},
+ {101, "Module loading failed"},
+ {102, "Integrity check of the loaded module content failed"},
+ {103, "Attempt to unload code of the module in use"},
+ {104, "Other failure of module instance initialization request"},
+ {105, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
+ {106, "Reserved (ADSP_IPC_CONFIG_GET_ERROR removed)"},
+ {107, "Reserved (ADSP_IPC_CONFIG_SET_ERROR removed)"},
+ {108, "Reserved (ADSP_IPC_LARGE_CONFIG_GET_ERROR removed)"},
+ {109, "Reserved (ADSP_IPC_LARGE_CONFIG_SET_ERROR removed)"},
+ {110, "Invalid (out of range) module ID provided"},
+ {111, "Invalid module instance ID provided"},
+ {112, "Invalid queue (pin) ID provided"},
+ {113, "Invalid destination queue (pin) ID provided"},
+ {114, "Reserved (ADSP_IPC_BIND_UNBIND_DST_SINK_UNSUPPORTED removed)"},
+ {115, "Reserved (ADSP_IPC_UNLOAD_INST_EXISTS removed)"},
+ {116, "Invalid target code ID provided"},
+ {117, "Injection DMA buffer is too small for probing the input pin"},
+ {118, "Extraction DMA buffer is too small for probing the output pin"},
+ {120, "Invalid ID of configuration item provided in TLV list"},
+ {121, "Invalid length of configuration item provided in TLV list"},
+ {122, "Invalid structure of configuration item provided"},
+ {140, "Initialization of DMA Gateway failed"},
+ {141, "Invalid ID of gateway provided"},
+ {142, "Setting state of DMA Gateway failed"},
+ {143, "DMA_CONTROL message targeting gateway not allocated yet"},
+ {150, "Attempt to configure SCLK while I2S port is running"},
+ {151, "Attempt to configure MCLK while I2S port is running"},
+ {152, "Attempt to stop SCLK that is not running"},
+ {153, "Attempt to stop MCLK that is not running"},
+ {160, "Reserved (ADSP_IPC_PIPELINE_NOT_INITIALIZED removed)"},
+ {161, "Reserved (ADSP_IPC_PIPELINE_NOT_EXIST removed)"},
+ {162, "Reserved (ADSP_IPC_PIPELINE_SAVE_FAILED removed)"},
+ {163, "Reserved (ADSP_IPC_PIPELINE_RESTORE_FAILED removed)"},
+ {165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"},
+};
+
+static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status)
+{
+ int i, ret;
+
+ status &= SOF_IPC4_REPLY_STATUS;
+
+ if (!status)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ipc4_status); i++) {
+ if (ipc4_status[i].status == status) {
+ dev_err(sdev->dev, "FW reported error: %u - %s\n",
+ status, ipc4_status[i].msg);
+ goto to_errno;
+ }
+ }
+
+ if (i == ARRAY_SIZE(ipc4_status))
+ dev_err(sdev->dev, "FW reported error: %u - Unknown\n", status);
+
+to_errno:
+ switch (status) {
+ case 8:
+ case 11:
+ case 105 ... 109:
+ case 114 ... 115:
+ case 160 ... 163:
+ case 165:
+ ret = -ENOENT;
+ break;
+ case 4:
+ case 150:
+ case 151:
+ ret = -EBUSY;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
+#define DBG_IPC4_MSG_TYPE_ENTRY(type) [SOF_IPC4_##type] = #type
+static const char * const ipc4_dbg_mod_msg_type[] = {
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_INIT_INSTANCE),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_GET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_SET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_GET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_SET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_BIND),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_UNBIND),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_DX),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_D0IX),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_ENTER_MODULE_RESTORE),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_EXIT_MODULE_RESTORE),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_DELETE_INSTANCE),
+};
+
+static const char * const ipc4_dbg_glb_msg_type[] = {
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_BOOT_CONFIG),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_ROM_CONTROL),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_IPCGATEWAY_CMD),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_PERF_MEASUREMENTS_CMD),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_CHAIN_DMA),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_MULTIPLE_MODULES),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_UNLOAD_MULTIPLE_MODULES),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_CREATE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_DELETE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_SET_PIPELINE_STATE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_STATE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_CONTEXT_SIZE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_SAVE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_RESTORE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION),
+};
+
+#define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type) [SOF_IPC4_NOTIFY_##type] = #type
+static const char * const ipc4_dbg_notification_type[] = {
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PHRASE_DETECTED),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(RESOURCE_EVENT),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(LOG_BUFFER_STATUS),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(TIMESTAMP_CAPTURED),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_READY),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_AUD_CLASS_RESULT),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(EXCEPTION_CAUGHT),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(MODULE_NOTIFICATION),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PROBE_DATA_AVAILABLE),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(ASYNC_MSG_SRVC_MESSAGE),
+};
+
+static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
+ bool data_size_valid)
+{
+ u32 val, type;
+ const u8 *str2 = NULL;
+ const u8 *str = NULL;
+
+ val = msg->primary & SOF_IPC4_MSG_TARGET_MASK;
+ type = SOF_IPC4_MSG_TYPE_GET(msg->primary);
+
+ if (val == SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) {
+ /* Module message */
+ if (type < SOF_IPC4_MOD_TYPE_LAST)
+ str = ipc4_dbg_mod_msg_type[type];
+ if (!str)
+ str = "Unknown Module message type";
+ } else {
+ /* Global FW message */
+ if (type < SOF_IPC4_GLB_TYPE_LAST)
+ str = ipc4_dbg_glb_msg_type[type];
+ if (!str)
+ str = "Unknown Global message type";
+
+ if (type == SOF_IPC4_GLB_NOTIFICATION) {
+ /* Notification message */
+ u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
+
+ if (notif < SOF_IPC4_NOTIFY_TYPE_LAST)
+ str2 = ipc4_dbg_notification_type[notif];
+ if (!str2)
+ str2 = "Unknown Global notification";
+ }
+ }
+
+ if (str2) {
+ if (data_size_valid && msg->data_size)
+ dev_dbg(dev, "%s: %#x|%#x: %s|%s [data size: %zu]\n",
+ text, msg->primary, msg->extension, str, str2,
+ msg->data_size);
+ else
+ dev_dbg(dev, "%s: %#x|%#x: %s|%s\n", text, msg->primary,
+ msg->extension, str, str2);
+ } else {
+ if (data_size_valid && msg->data_size)
+ dev_dbg(dev, "%s: %#x|%#x: %s [data size: %zu]\n",
+ text, msg->primary, msg->extension, str,
+ msg->data_size);
+ else
+ dev_dbg(dev, "%s: %#x|%#x: %s\n", text, msg->primary,
+ msg->extension, str);
+ }
+}
+#else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */
+static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
+ bool data_size_valid)
+{
+ if (data_size_valid && msg->data_size)
+ dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text,
+ msg->primary, msg->extension, msg->data_size);
+ else
+ dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension);
+}
+#endif
+
+static int sof_ipc4_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc4_msg *ipc4_reply;
+ int ret;
+
+ /* get the generic reply */
+ ipc4_reply = msg->reply_data;
+
+ sof_ipc4_log_header(sdev->dev, "ipc tx reply", ipc4_reply, false);
+
+ ret = sof_ipc4_check_reply_status(sdev, ipc4_reply->primary);
+ if (ret)
+ return ret;
+
+ /* No other information is expected for non large config get replies */
+ if (!msg->reply_size || !SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_reply->primary) ||
+ (SOF_IPC4_MSG_TYPE_GET(ipc4_reply->primary) != SOF_IPC4_MOD_LARGE_CONFIG_GET))
+ return 0;
+
+ /* Read the requested payload */
+ snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, ipc4_reply->data_ptr,
+ msg->reply_size);
+
+ return 0;
+}
+
+/* wait for IPC message reply */
+static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data)
+{
+ struct snd_sof_ipc_msg *msg = &ipc->msg;
+ struct sof_ipc4_msg *ipc4_msg = msg->msg_data;
+ struct snd_sof_dev *sdev = ipc->sdev;
+ int ret;
+
+ /* wait for DSP IPC completion */
+ ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
+ msecs_to_jiffies(sdev->ipc_timeout));
+ if (ret == 0) {
+ dev_err(sdev->dev, "ipc timed out for %#x|%#x\n",
+ ipc4_msg->primary, ipc4_msg->extension);
+ return -ETIMEDOUT;
+ }
+
+ if (msg->reply_error) {
+ dev_err(sdev->dev, "ipc error for msg %#x|%#x\n",
+ ipc4_msg->primary, ipc4_msg->extension);
+ ret = msg->reply_error;
+ } else {
+ if (reply_data) {
+ struct sof_ipc4_msg *ipc4_reply = msg->reply_data;
+ struct sof_ipc4_msg *ipc4_reply_data = reply_data;
+
+ /* Copy the header */
+ ipc4_reply_data->header_u64 = ipc4_reply->header_u64;
+ if (msg->reply_size && ipc4_reply_data->data_ptr) {
+ /* copy the payload returned from DSP */
+ memcpy(ipc4_reply_data->data_ptr, ipc4_reply->data_ptr,
+ msg->reply_size);
+ ipc4_reply_data->data_size = msg->reply_size;
+ }
+ }
+
+ ret = 0;
+ sof_ipc4_log_header(sdev->dev, "ipc tx done ", ipc4_msg, true);
+ }
+
+ /* re-enable dumps after successful IPC tx */
+ if (sdev->ipc_dump_printed) {
+ sdev->dbg_dump_printed = false;
+ sdev->ipc_dump_printed = false;
+ }
+
+ return ret;
+}
+
+static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc,
+ void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes)
+{
+ struct sof_ipc4_msg *ipc4_msg = msg_data;
+ struct snd_sof_dev *sdev = ipc->sdev;
+ int ret;
+
+ if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size)
+ return -EINVAL;
+
+ ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);
+ if (ret) {
+ dev_err_ratelimited(sdev->dev,
+ "%s: ipc message send for %#x|%#x failed: %d\n",
+ __func__, ipc4_msg->primary, ipc4_msg->extension, ret);
+ return ret;
+ }
+
+ sof_ipc4_log_header(sdev->dev, "ipc tx ", msg_data, true);
+
+ /* now wait for completion */
+ return ipc4_wait_tx_done(ipc, reply_data);
+}
+
+static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+ 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)
+ return -EINVAL;
+
+ /* Serialise IPC TX */
+ mutex_lock(&ipc->tx_mutex);
+
+ ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
+
+ mutex_unlock(&ipc->tx_mutex);
+
+#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;
+
+ if (msg)
+ sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
+#endif
+
+ return ret;
+}
+
+static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
+ size_t payload_bytes, bool set)
+{
+ size_t payload_limit = sdev->ipc->max_payload_size;
+ struct sof_ipc4_msg *ipc4_msg = data;
+ struct sof_ipc4_msg tx = {{ 0 }};
+ struct sof_ipc4_msg rx = {{ 0 }};
+ size_t remaining = payload_bytes;
+ size_t offset = 0;
+ size_t chunk_size;
+ int ret;
+
+ if (!data)
+ return -EINVAL;
+
+ if ((ipc4_msg->primary & SOF_IPC4_MSG_TARGET_MASK) !=
+ SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG))
+ return -EINVAL;
+
+ ipc4_msg->primary &= ~SOF_IPC4_MSG_TYPE_MASK;
+ tx.primary = ipc4_msg->primary;
+ tx.extension = ipc4_msg->extension;
+
+ if (set)
+ tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+ else
+ tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_GET);
+
+ tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(payload_bytes);
+
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+
+ /* Serialise IPC TX */
+ mutex_lock(&sdev->ipc->tx_mutex);
+
+ do {
+ size_t tx_size, rx_size;
+
+ if (remaining > payload_limit) {
+ chunk_size = payload_limit;
+ } else {
+ chunk_size = remaining;
+ if (set)
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
+ }
+
+ if (offset) {
+ tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK;
+ tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(offset);
+ }
+
+ if (set) {
+ tx.data_size = chunk_size;
+ tx.data_ptr = ipc4_msg->data_ptr + offset;
+
+ tx_size = chunk_size;
+ rx_size = 0;
+ } else {
+ rx.primary = 0;
+ rx.extension = 0;
+ rx.data_size = chunk_size;
+ rx.data_ptr = ipc4_msg->data_ptr + offset;
+
+ tx_size = 0;
+ rx_size = chunk_size;
+ }
+
+ /* Send the message for the current chunk */
+ ret = ipc4_tx_msg_unlocked(sdev->ipc, &tx, tx_size, &rx, rx_size);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "%s: large config %s failed at offset %zu: %d\n",
+ __func__, set ? "set" : "get", offset, ret);
+ goto out;
+ }
+
+ if (!set && rx.extension & SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK) {
+ /* Verify the firmware reported total payload size */
+ rx_size = rx.extension & SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+
+ if (rx_size > payload_bytes) {
+ dev_err(sdev->dev,
+ "%s: Receive buffer (%zu) is too small for %zu\n",
+ __func__, payload_bytes, rx_size);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (rx_size < chunk_size) {
+ chunk_size = rx_size;
+ remaining = rx_size;
+ } else if (rx_size < payload_bytes) {
+ remaining = rx_size;
+ }
+ }
+
+ offset += chunk_size;
+ remaining -= chunk_size;
+ } while (remaining);
+
+ /* Adjust the received data size if needed */
+ if (!set && payload_bytes != offset)
+ ipc4_msg->data_size = offset;
+
+ sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
+
+out:
+ mutex_unlock(&sdev->ipc->tx_mutex);
+
+ return ret;
+}
+
+static int sof_ipc4_init_msg_memory(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_msg *ipc4_msg;
+ struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
+
+ /* TODO: get max_payload_size from firmware */
+ sdev->ipc->max_payload_size = SOF_IPC4_MSG_MAX_SIZE;
+
+ /* Allocate memory for the ipc4 container and the maximum payload */
+ msg->reply_data = devm_kzalloc(sdev->dev, sdev->ipc->max_payload_size +
+ sizeof(struct sof_ipc4_msg), GFP_KERNEL);
+ if (!msg->reply_data)
+ return -ENOMEM;
+
+ ipc4_msg = msg->reply_data;
+ ipc4_msg->data_ptr = msg->reply_data + sizeof(struct sof_ipc4_msg);
+
+ return 0;
+}
+
+static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg)
+{
+ int inbox_offset, inbox_size, outbox_offset, outbox_size;
+
+ /* no need to re-check version/ABI for subsequent boots */
+ if (!sdev->first_boot)
+ return 0;
+
+ /* Set up the windows for IPC communication */
+ inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev);
+ if (inbox_offset < 0) {
+ dev_err(sdev->dev, "%s: No mailbox offset\n", __func__);
+ return inbox_offset;
+ }
+ inbox_size = SOF_IPC4_MSG_MAX_SIZE;
+ outbox_offset = snd_sof_dsp_get_window_offset(sdev, 1);
+ outbox_size = SOF_IPC4_MSG_MAX_SIZE;
+
+ sdev->dsp_box.offset = inbox_offset;
+ sdev->dsp_box.size = inbox_size;
+ sdev->host_box.offset = outbox_offset;
+ sdev->host_box.size = outbox_size;
+
+ dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n",
+ inbox_offset, inbox_size);
+ dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n",
+ outbox_offset, outbox_size);
+
+ return sof_ipc4_init_msg_memory(sdev);
+}
+
+static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data;
+ size_t data_size = 0;
+ int err;
+
+ if (!ipc4_msg || !SOF_IPC4_MSG_IS_NOTIFICATION(ipc4_msg->primary))
+ return;
+
+ ipc4_msg->data_ptr = NULL;
+ ipc4_msg->data_size = 0;
+
+ sof_ipc4_log_header(sdev->dev, "ipc rx ", ipc4_msg, false);
+
+ switch (SOF_IPC4_NOTIFICATION_TYPE_GET(ipc4_msg->primary)) {
+ case SOF_IPC4_NOTIFY_FW_READY:
+ /* check for FW boot completion */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
+ err = ipc4_fw_ready(sdev, ipc4_msg);
+ if (err < 0)
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
+ else
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
+
+ /* wake up firmware loader */
+ wake_up(&sdev->boot_wait);
+ }
+
+ break;
+ case SOF_IPC4_NOTIFY_RESOURCE_EVENT:
+ data_size = sizeof(struct sof_ipc4_notify_resource_data);
+ break;
+ default:
+ dev_dbg(sdev->dev, "%s: Unhandled DSP message: %#x|%#x\n", __func__,
+ ipc4_msg->primary, ipc4_msg->extension);
+ break;
+ }
+
+ if (data_size) {
+ ipc4_msg->data_ptr = kmalloc(data_size, GFP_KERNEL);
+ if (!ipc4_msg->data_ptr)
+ return;
+
+ ipc4_msg->data_size = data_size;
+ snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size);
+ }
+
+ sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true);
+
+ if (data_size) {
+ kfree(ipc4_msg->data_ptr);
+ ipc4_msg->data_ptr = NULL;
+ ipc4_msg->data_size = 0;
+ }
+}
+
+const struct sof_ipc_ops ipc4_ops = {
+ .tx_msg = sof_ipc4_tx_msg,
+ .rx_msg = sof_ipc4_rx_msg,
+ .set_get_data = sof_ipc4_set_get_data,
+ .get_reply = sof_ipc4_get_reply,
+ .fw_loader = &ipc4_loader_ops,
+};
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 697f03565a70..5f51d936b306 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -11,690 +11,9 @@
//
#include <linux/firmware.h>
-#include <sound/sof.h>
-#include <sound/sof/ext_manifest.h>
#include "sof-priv.h"
#include "ops.h"
-static int get_ext_windows(struct snd_sof_dev *sdev,
- const struct sof_ipc_ext_data_hdr *ext_hdr)
-{
- const struct sof_ipc_window *w =
- container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
-
- if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
- return -EINVAL;
-
- if (sdev->info_window) {
- if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) {
- dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox");
- return -EINVAL;
- }
- return 0;
- }
-
- /* keep a local copy of the data */
- sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size,
- GFP_KERNEL);
- if (!sdev->info_window)
- return -ENOMEM;
-
- return 0;
-}
-
-static int get_cc_info(struct snd_sof_dev *sdev,
- const struct sof_ipc_ext_data_hdr *ext_hdr)
-{
- int ret;
-
- const struct sof_ipc_cc_version *cc =
- container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
-
- if (sdev->cc_version) {
- if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
- dev_err(sdev->dev, "error: receive diverged cc_version descriptions");
- return -EINVAL;
- }
- return 0;
- }
-
- dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
- cc->name, cc->major, cc->minor, cc->micro, cc->desc,
- cc->optim);
-
- /* create read-only cc_version debugfs to store compiler version info */
- /* use local copy of the cc_version to prevent data corruption */
- if (sdev->first_boot) {
- sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
- GFP_KERNEL);
-
- if (!sdev->cc_version)
- return -ENOMEM;
-
- memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
- ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
- cc->ext_hdr.hdr.size,
- "cc_version", 0444);
-
- /* errors are only due to memory allocation, not debugfs */
- if (ret < 0) {
- dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
- return ret;
- }
- }
-
- return 0;
-}
-
-/* parse the extended FW boot data structures from FW boot message */
-static int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
-{
- struct sof_ipc_ext_data_hdr *ext_hdr;
- void *ext_data;
- int ret = 0;
-
- ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!ext_data)
- return -ENOMEM;
-
- /* get first header */
- snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
- sizeof(*ext_hdr));
- ext_hdr = ext_data;
-
- while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
- /* read in ext structure */
- snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM,
- offset + sizeof(*ext_hdr),
- (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
- ext_hdr->hdr.size - sizeof(*ext_hdr));
-
- dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
- ext_hdr->type, ext_hdr->hdr.size);
-
- /* process structure data */
- switch (ext_hdr->type) {
- case SOF_IPC_EXT_WINDOW:
- ret = get_ext_windows(sdev, ext_hdr);
- break;
- case SOF_IPC_EXT_CC_INFO:
- ret = get_cc_info(sdev, ext_hdr);
- break;
- case SOF_IPC_EXT_UNUSED:
- case SOF_IPC_EXT_PROBE_INFO:
- case SOF_IPC_EXT_USER_ABI_INFO:
- /* They are supported but we don't do anything here */
- break;
- default:
- dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n",
- ext_hdr->type, ext_hdr->hdr.size);
- ret = 0;
- break;
- }
-
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
- ext_hdr->type);
- break;
- }
-
- /* move to next header */
- offset += ext_hdr->hdr.size;
- snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
- sizeof(*ext_hdr));
- ext_hdr = ext_data;
- }
-
- kfree(ext_data);
- return ret;
-}
-
-static int ext_man_get_fw_version(struct snd_sof_dev *sdev,
- const struct sof_ext_man_elem_header *hdr)
-{
- const struct sof_ext_man_fw_version *v =
- container_of(hdr, struct sof_ext_man_fw_version, hdr);
-
- memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
- sdev->fw_ready.flags = v->flags;
-
- /* log ABI versions and check FW compatibility */
- return snd_sof_ipc_valid(sdev);
-}
-
-static int ext_man_get_windows(struct snd_sof_dev *sdev,
- const struct sof_ext_man_elem_header *hdr)
-{
- const struct sof_ext_man_window *w;
-
- w = container_of(hdr, struct sof_ext_man_window, hdr);
-
- return get_ext_windows(sdev, &w->ipc_window.ext_hdr);
-}
-
-static int ext_man_get_cc_info(struct snd_sof_dev *sdev,
- const struct sof_ext_man_elem_header *hdr)
-{
- const struct sof_ext_man_cc_version *cc;
-
- cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
-
- return get_cc_info(sdev, &cc->cc_version.ext_hdr);
-}
-
-static int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
- const struct sof_ext_man_elem_header *hdr)
-{
- const struct ext_man_dbg_abi *dbg_abi =
- container_of(hdr, struct ext_man_dbg_abi, hdr);
-
- if (sdev->first_boot)
- dev_dbg(sdev->dev,
- "Firmware: DBG_ABI %d:%d:%d\n",
- SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
- SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
- SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
-
- return 0;
-}
-
-static int ext_man_get_config_data(struct snd_sof_dev *sdev,
- const struct sof_ext_man_elem_header *hdr)
-{
- const struct sof_ext_man_config_data *config =
- container_of(hdr, struct sof_ext_man_config_data, hdr);
- const struct sof_config_elem *elem;
- int elems_counter;
- int elems_size;
- int ret = 0;
- int i;
-
- /* calculate elements counter */
- elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
- elems_counter = elems_size / sizeof(struct sof_config_elem);
-
- dev_dbg(sdev->dev, "%s can hold up to %d config elements\n",
- __func__, elems_counter);
-
- for (i = 0; i < elems_counter; ++i) {
- elem = &config->elems[i];
- dev_dbg(sdev->dev, "%s get index %d token %d val %d\n",
- __func__, i, elem->token, elem->value);
- switch (elem->token) {
- case SOF_EXT_MAN_CONFIG_EMPTY:
- /* unused memory space is zero filled - mapped to EMPTY elements */
- break;
- case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
- /* TODO: use ipc msg size from config data */
- break;
- case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
- if (sdev->first_boot && elem->value)
- ret = snd_sof_dbg_memory_info_init(sdev);
- break;
- default:
- dev_info(sdev->dev, "Unknown firmware configuration token %d value %d",
- elem->token, elem->value);
- break;
- }
- if (ret < 0) {
- dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n",
- elem->token, elem->value, ret);
- return ret;
- }
- }
-
- return 0;
-}
-
-static ssize_t snd_sof_ext_man_size(const struct firmware *fw)
-{
- const struct sof_ext_man_header *head;
-
- head = (struct sof_ext_man_header *)fw->data;
-
- /*
- * assert fw size is big enough to contain extended manifest header,
- * it prevents from reading unallocated memory from `head` in following
- * step.
- */
- if (fw->size < sizeof(*head))
- return -EINVAL;
-
- /*
- * When fw points to extended manifest,
- * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
- */
- if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
- return head->full_size;
-
- /* otherwise given fw don't have an extended manifest */
- return 0;
-}
-
-/* parse extended FW manifest data structures */
-static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev,
- const struct firmware *fw)
-{
- const struct sof_ext_man_elem_header *elem_hdr;
- const struct sof_ext_man_header *head;
- ssize_t ext_man_size;
- ssize_t remaining;
- uintptr_t iptr;
- int ret = 0;
-
- head = (struct sof_ext_man_header *)fw->data;
- remaining = head->full_size - head->header_size;
- ext_man_size = snd_sof_ext_man_size(fw);
-
- /* Assert firmware starts with extended manifest */
- if (ext_man_size <= 0)
- return ext_man_size;
-
- /* incompatible version */
- if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
- head->header_version)) {
- dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n",
- head->header_version, SOF_EXT_MAN_VERSION);
- return -EINVAL;
- }
-
- /* get first extended manifest element header */
- iptr = (uintptr_t)fw->data + head->header_size;
-
- while (remaining > sizeof(*elem_hdr)) {
- elem_hdr = (struct sof_ext_man_elem_header *)iptr;
-
- dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n",
- elem_hdr->type, elem_hdr->size);
-
- if (elem_hdr->size < sizeof(*elem_hdr) ||
- elem_hdr->size > remaining) {
- dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n",
- elem_hdr->type, elem_hdr->size);
- return -EINVAL;
- }
-
- /* process structure data */
- switch (elem_hdr->type) {
- case SOF_EXT_MAN_ELEM_FW_VERSION:
- ret = ext_man_get_fw_version(sdev, elem_hdr);
- break;
- case SOF_EXT_MAN_ELEM_WINDOW:
- ret = ext_man_get_windows(sdev, elem_hdr);
- break;
- case SOF_EXT_MAN_ELEM_CC_VERSION:
- ret = ext_man_get_cc_info(sdev, elem_hdr);
- break;
- case SOF_EXT_MAN_ELEM_DBG_ABI:
- ret = ext_man_get_dbg_abi_info(sdev, elem_hdr);
- break;
- case SOF_EXT_MAN_ELEM_CONFIG_DATA:
- ret = ext_man_get_config_data(sdev, elem_hdr);
- break;
- case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
- ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
- break;
- default:
- dev_info(sdev->dev, "unknown sof_ext_man header type %d size 0x%X\n",
- elem_hdr->type, elem_hdr->size);
- break;
- }
-
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n",
- elem_hdr->type, elem_hdr->size);
- return ret;
- }
-
- remaining -= elem_hdr->size;
- iptr += elem_hdr->size;
- }
-
- if (remaining) {
- dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
- return -EINVAL;
- }
-
- return ext_man_size;
-}
-
-/*
- * IPC Firmware ready.
- */
-static void sof_get_windows(struct snd_sof_dev *sdev)
-{
- struct sof_ipc_window_elem *elem;
- u32 outbox_offset = 0;
- u32 stream_offset = 0;
- u32 inbox_offset = 0;
- u32 outbox_size = 0;
- u32 stream_size = 0;
- u32 inbox_size = 0;
- u32 debug_size = 0;
- u32 debug_offset = 0;
- int window_offset;
- int i;
-
- if (!sdev->info_window) {
- dev_err(sdev->dev, "error: have no window info\n");
- return;
- }
-
- for (i = 0; i < sdev->info_window->num_windows; i++) {
- elem = &sdev->info_window->window[i];
-
- window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
- if (window_offset < 0) {
- dev_warn(sdev->dev, "warn: no offset for window %d\n",
- elem->id);
- continue;
- }
-
- switch (elem->type) {
- case SOF_IPC_REGION_UPBOX:
- inbox_offset = window_offset + elem->offset;
- inbox_size = elem->size;
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- inbox_offset,
- elem->size, "inbox",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- case SOF_IPC_REGION_DOWNBOX:
- outbox_offset = window_offset + elem->offset;
- outbox_size = elem->size;
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- outbox_offset,
- elem->size, "outbox",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- case SOF_IPC_REGION_TRACE:
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- window_offset + elem->offset,
- elem->size, "etrace",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- case SOF_IPC_REGION_DEBUG:
- debug_offset = window_offset + elem->offset;
- debug_size = elem->size;
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- window_offset + elem->offset,
- elem->size, "debug",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- case SOF_IPC_REGION_STREAM:
- stream_offset = window_offset + elem->offset;
- stream_size = elem->size;
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- stream_offset,
- elem->size, "stream",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- case SOF_IPC_REGION_REGS:
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- window_offset + elem->offset,
- elem->size, "regs",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- case SOF_IPC_REGION_EXCEPTION:
- sdev->dsp_oops_offset = window_offset + elem->offset;
- snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
- window_offset + elem->offset,
- elem->size, "exception",
- SOF_DEBUGFS_ACCESS_D0_ONLY);
- break;
- default:
- dev_err(sdev->dev, "error: get illegal window info\n");
- return;
- }
- }
-
- if (outbox_size == 0 || inbox_size == 0) {
- dev_err(sdev->dev, "error: get illegal mailbox window\n");
- return;
- }
-
- sdev->dsp_box.offset = inbox_offset;
- sdev->dsp_box.size = inbox_size;
-
- sdev->host_box.offset = outbox_offset;
- sdev->host_box.size = outbox_size;
-
- sdev->stream_box.offset = stream_offset;
- sdev->stream_box.size = stream_size;
-
- sdev->debug_box.offset = debug_offset;
- sdev->debug_box.size = debug_size;
-
- dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
- inbox_offset, inbox_size);
- dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
- outbox_offset, outbox_size);
- dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
- stream_offset, stream_size);
- dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n",
- debug_offset, debug_size);
-}
-
-/* check for ABI compatibility and create memory windows on first boot */
-int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
-{
- struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
- int offset;
- int ret;
-
- /* mailbox must be on 4k boundary */
- offset = snd_sof_dsp_get_mailbox_offset(sdev);
- if (offset < 0) {
- dev_err(sdev->dev, "error: have no mailbox offset\n");
- return offset;
- }
-
- dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
- msg_id, offset);
-
- /* no need to re-check version/ABI for subsequent boots */
- if (!sdev->first_boot)
- return 0;
-
- /*
- * copy data from the DSP FW ready offset
- * Subsequent error handling is not needed for BLK_TYPE_SRAM
- */
- ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
- sizeof(*fw_ready));
- if (ret) {
- dev_err(sdev->dev,
- "error: unable to read fw_ready, read from TYPE_SRAM failed\n");
- return ret;
- }
-
- /* make sure ABI version is compatible */
- ret = snd_sof_ipc_valid(sdev);
- if (ret < 0)
- return ret;
-
- /* now check for extended data */
- snd_sof_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
-
- sof_get_windows(sdev);
-
- return sof_ipc_init_msg_memory(sdev);
-}
-EXPORT_SYMBOL(sof_fw_ready);
-
-/* generic module parser for mmaped DSPs */
-int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
- struct snd_sof_mod_hdr *module)
-{
- struct snd_sof_blk_hdr *block;
- int count, ret;
- u32 offset;
- size_t remaining;
-
- dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
- module->size, module->num_blocks, module->type);
-
- block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
-
- /* module->size doesn't include header size */
- remaining = module->size;
- for (count = 0; count < module->num_blocks; count++) {
- /* check for wrap */
- if (remaining < sizeof(*block)) {
- dev_err(sdev->dev, "error: not enough data remaining\n");
- return -EINVAL;
- }
-
- /* minus header size of block */
- remaining -= sizeof(*block);
-
- if (block->size == 0) {
- dev_warn(sdev->dev,
- "warning: block %d size zero\n", count);
- dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
- block->type, block->offset);
- continue;
- }
-
- switch (block->type) {
- case SOF_FW_BLK_TYPE_RSRVD0:
- case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
- continue; /* not handled atm */
- case SOF_FW_BLK_TYPE_IRAM:
- case SOF_FW_BLK_TYPE_DRAM:
- case SOF_FW_BLK_TYPE_SRAM:
- offset = block->offset;
- break;
- default:
- dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
- block->type, count);
- return -EINVAL;
- }
-
- dev_dbg(sdev->dev,
- "block %d type 0x%x size 0x%x ==> offset 0x%x\n",
- count, block->type, block->size, offset);
-
- /* checking block->size to avoid unaligned access */
- if (block->size % sizeof(u32)) {
- dev_err(sdev->dev, "error: invalid block size 0x%x\n",
- block->size);
- return -EINVAL;
- }
- ret = snd_sof_dsp_block_write(sdev, block->type, offset,
- block + 1, block->size);
- if (ret < 0) {
- dev_err(sdev->dev, "error: write to block type 0x%x failed\n",
- block->type);
- return ret;
- }
-
- if (remaining < block->size) {
- dev_err(sdev->dev, "error: not enough data remaining\n");
- return -EINVAL;
- }
-
- /* minus body size of block */
- remaining -= block->size;
- /* next block */
- block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
- + block->size);
- }
-
- return 0;
-}
-EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
-
-static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw,
- size_t fw_offset)
-{
- struct snd_sof_fw_header *header;
- size_t fw_size = fw->size - fw_offset;
-
- if (fw->size <= fw_offset) {
- dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
- return -EINVAL;
- }
-
- /* Read the header information from the data pointer */
- header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
-
- /* verify FW sig */
- if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
- dev_err(sdev->dev, "error: invalid firmware signature\n");
- return -EINVAL;
- }
-
- /* check size is valid */
- if (fw_size != header->file_size + sizeof(*header)) {
- dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
- fw_size, header->file_size + sizeof(*header));
- return -EINVAL;
- }
-
- dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
- header->file_size, header->num_modules,
- header->abi, sizeof(*header));
-
- return 0;
-}
-
-static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw,
- size_t fw_offset)
-{
- struct snd_sof_fw_header *header;
- struct snd_sof_mod_hdr *module;
- int (*load_module)(struct snd_sof_dev *sof_dev,
- struct snd_sof_mod_hdr *hdr);
- int ret, count;
- size_t remaining;
-
- header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
- load_module = sof_ops(sdev)->load_module;
- if (!load_module)
- return -EINVAL;
-
- /* parse each module */
- module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset +
- sizeof(*header));
- remaining = fw->size - sizeof(*header) - fw_offset;
- /* check for wrap */
- if (remaining > fw->size) {
- dev_err(sdev->dev, "error: fw size smaller than header size\n");
- return -EINVAL;
- }
-
- for (count = 0; count < header->num_modules; count++) {
- /* check for wrap */
- if (remaining < sizeof(*module)) {
- dev_err(sdev->dev, "error: not enough data remaining\n");
- return -EINVAL;
- }
-
- /* minus header size of module */
- remaining -= sizeof(*module);
-
- /* module */
- ret = load_module(sdev, module);
- if (ret < 0) {
- dev_err(sdev->dev, "error: invalid module %d\n", count);
- return ret;
- }
-
- if (remaining < module->size) {
- dev_err(sdev->dev, "error: not enough data remaining\n");
- return -EINVAL;
- }
-
- /* minus body size of module */
- remaining -= module->size;
- module = (struct snd_sof_mod_hdr *)((u8 *)module
- + sizeof(*module) + module->size);
- }
-
- return 0;
-}
-
int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
@@ -726,7 +45,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
}
/* check for extended manifest */
- ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw);
+ ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev);
if (ext_man_size > 0) {
/* when no error occurred, drop extended manifest */
plat_data->fw_offset = ext_man_size;
@@ -756,7 +75,7 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
return ret;
/* make sure the FW header and file is valid */
- ret = check_header(sdev, plat_data->fw, plat_data->fw_offset);
+ ret = sdev->ipc->ops->fw_loader->validate(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: invalid FW header\n");
goto error;
@@ -770,10 +89,12 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
}
/* parse and load firmware modules to DSP */
- ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset);
- if (ret < 0) {
- dev_err(sdev->dev, "error: invalid FW modules\n");
- goto error;
+ if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
+ ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Firmware loading failed\n");
+ goto error;
+ }
}
return 0;
@@ -854,6 +175,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
dev_dbg(sdev->dev, "firmware boot complete\n");
sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
+ if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration)
+ return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev);
+
return 0;
}
EXPORT_SYMBOL(snd_sof_run_firmware);
diff --git a/sound/soc/sof/mediatek/Kconfig b/sound/soc/sof/mediatek/Kconfig
index aeacf0e5bfbb..a149dd1b3f44 100644
--- a/sound/soc/sof/mediatek/Kconfig
+++ b/sound/soc/sof/mediatek/Kconfig
@@ -21,9 +21,20 @@ config SND_SOC_SOF_MTK_COMMON
This option is not user-selectable but automagically handled by
'select' statements at a higher level
+config SND_SOC_SOF_MT8186
+ tristate "SOF support for MT8186 audio DSP"
+ select SND_SOC_SOF_MTK_COMMON
+ depends on MTK_ADSP_IPC
+ help
+ This adds support for Sound Open Firmware for Mediatek platforms
+ using the mt8186 processors.
+ Say Y if you have such a device.
+ If unsure select "N".
+
config SND_SOC_SOF_MT8195
tristate "SOF support for MT8195 audio DSP"
select SND_SOC_SOF_MTK_COMMON
+ depends on MTK_ADSP_IPC
help
This adds support for Sound Open Firmware for Mediatek platforms
using the mt8195 processors.
diff --git a/sound/soc/sof/mediatek/Makefile b/sound/soc/sof/mediatek/Makefile
index e8ec6da981de..29c5afb2f3d6 100644
--- a/sound/soc/sof/mediatek/Makefile
+++ b/sound/soc/sof/mediatek/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+obj-$(CONFIG_SND_SOC_SOF_MTK_COMMON) += mtk-adsp-common.o
obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/
+obj-$(CONFIG_SND_SOC_SOF_MT8186) += mt8186/
diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h
index 6734e2c0c6b1..4ab998756bbc 100644
--- a/sound/soc/sof/mediatek/adsp_helper.h
+++ b/sound/soc/sof/mediatek/adsp_helper.h
@@ -7,37 +7,41 @@
#ifndef __MTK_ADSP_HELPER_H__
#define __MTK_ADSP_HELPER_H__
+#include <linux/firmware/mediatek/mtk-adsp-ipc.h>
+
/*
* Global important adsp data structure.
*/
-#define DSP_MBOX_NUM 3
-
struct mtk_adsp_chip_info {
phys_addr_t pa_sram;
phys_addr_t pa_dram; /* adsp dram physical base */
phys_addr_t pa_shared_dram; /* adsp dram physical base */
phys_addr_t pa_cfgreg;
- phys_addr_t pa_mboxreg[DSP_MBOX_NUM];
u32 sramsize;
u32 dramsize;
u32 cfgregsize;
void __iomem *va_sram; /* corresponding to pa_sram */
void __iomem *va_dram; /* corresponding to pa_dram */
void __iomem *va_cfgreg;
- void __iomem *va_mboxreg[DSP_MBOX_NUM];
void __iomem *shared_sram; /* part of va_sram */
void __iomem *shared_dram; /* part of va_dram */
phys_addr_t adsp_bootup_addr;
int dram_offset; /*dram offset between system and dsp view*/
+
+ phys_addr_t pa_secreg;
+ u32 secregsize;
+ void __iomem *va_secreg;
+
+ phys_addr_t pa_busreg;
+ u32 busregsize;
+ void __iomem *va_busreg;
};
struct adsp_priv {
struct device *dev;
struct snd_sof_dev *sdev;
-
- /* DSP IPC handler */
- struct mbox_controller *adsp_mbox;
-
+ struct mtk_adsp_ipc *dsp_ipc;
+ struct platform_device *ipc_dev;
struct mtk_adsp_chip_info *adsp;
struct clk **clk;
u32 (*ap2adsp_addr)(u32 addr, void *data);
diff --git a/sound/soc/sof/mediatek/mt8186/Makefile b/sound/soc/sof/mediatek/mt8186/Makefile
new file mode 100644
index 000000000000..c1f5fc4e2495
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8186/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+snd-sof-mt8186-objs := mt8186.o mt8186-clk.o mt8186-loader.o
+obj-$(CONFIG_SND_SOC_SOF_MT8186) += snd-sof-mt8186.o
+
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c
new file mode 100644
index 000000000000..22220fd50b62
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2022 Mediatek Corporation. All rights reserved.
+//
+// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+// Tinghan Shen <tinghan.shen@mediatek.com>
+//
+// Hardware interface for mt8186 DSP clock
+
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+
+#include "../../sof-audio.h"
+#include "../../ops.h"
+#include "../adsp_helper.h"
+#include "mt8186.h"
+#include "mt8186-clk.h"
+
+static const char *adsp_clks[ADSP_CLK_MAX] = {
+ [CLK_TOP_AUDIODSP] = "audiodsp_sel",
+ [CLK_TOP_ADSP_BUS] = "adsp_bus_sel",
+};
+
+int mt8186_adsp_init_clock(struct snd_sof_dev *sdev)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+ struct device *dev = sdev->dev;
+ int i;
+
+ priv->clk = devm_kcalloc(dev, ADSP_CLK_MAX, sizeof(*priv->clk), GFP_KERNEL);
+ if (!priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < ADSP_CLK_MAX; i++) {
+ priv->clk[i] = devm_clk_get(dev, adsp_clks[i]);
+
+ if (IS_ERR(priv->clk[i]))
+ return PTR_ERR(priv->clk[i]);
+ }
+
+ return 0;
+}
+
+static int adsp_enable_all_clock(struct snd_sof_dev *sdev)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+ struct device *dev = sdev->dev;
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIODSP]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(audiodsp) fail %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP_BUS]);
+ if (ret) {
+ dev_err(dev, "%s clk_prepare_enable(adsp_bus) fail %d\n",
+ __func__, ret);
+ clk_disable_unprepare(priv->clk[CLK_TOP_AUDIODSP]);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void adsp_disable_all_clock(struct snd_sof_dev *sdev)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+ clk_disable_unprepare(priv->clk[CLK_TOP_ADSP_BUS]);
+ clk_disable_unprepare(priv->clk[CLK_TOP_AUDIODSP]);
+}
+
+int mt8186_adsp_clock_on(struct snd_sof_dev *sdev)
+{
+ struct device *dev = sdev->dev;
+ int ret;
+
+ ret = adsp_enable_all_clock(sdev);
+ if (ret) {
+ dev_err(dev, "failed to adsp_enable_clock: %d\n", ret);
+ return ret;
+ }
+ snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_CK_EN,
+ UART_EN | DMA_EN | TIMER_EN | COREDBG_EN | CORE_CLK_EN);
+ snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_UART_CTRL,
+ UART_BCLK_CG | UART_RSTN);
+
+ return 0;
+}
+
+void mt8186_adsp_clock_off(struct snd_sof_dev *sdev)
+{
+ snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_CK_EN, 0);
+ snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_UART_CTRL, 0);
+ adsp_disable_all_clock(sdev);
+}
+
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.h b/sound/soc/sof/mediatek/mt8186/mt8186-clk.h
new file mode 100644
index 000000000000..89c23caf0fee
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+/*
+ * Copyright (c) 2022 MediaTek Corporation. All rights reserved.
+ *
+ * Header file for the mt8186 DSP clock definition
+ */
+
+#ifndef __MT8186_CLK_H
+#define __MT8186_CLK_H
+
+struct snd_sof_dev;
+
+/* DSP clock */
+enum adsp_clk_id {
+ CLK_TOP_AUDIODSP,
+ CLK_TOP_ADSP_BUS,
+ ADSP_CLK_MAX
+};
+
+int mt8186_adsp_init_clock(struct snd_sof_dev *sdev);
+int mt8186_adsp_clock_on(struct snd_sof_dev *sdev);
+void mt8186_adsp_clock_off(struct snd_sof_dev *sdev);
+#endif
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-loader.c b/sound/soc/sof/mediatek/mt8186/mt8186-loader.c
new file mode 100644
index 000000000000..946e6c43204f
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8186/mt8186-loader.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright (c) 2022 Mediatek Corporation. All rights reserved.
+//
+// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+// Tinghan Shen <tinghan.shen@mediatek.com>
+//
+// Hardware interface for mt8186 DSP code loader
+
+#include <sound/sof.h>
+#include "mt8186.h"
+#include "../../ops.h"
+
+void mt8186_sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr)
+{
+ /* set RUNSTALL to stop core */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG,
+ RUNSTALL, RUNSTALL);
+
+ /* enable mbox 0 & 1 IRQ */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_MBOX_IRQ_EN,
+ DSP_MBOX0_IRQ_EN | DSP_MBOX1_IRQ_EN,
+ DSP_MBOX0_IRQ_EN | DSP_MBOX1_IRQ_EN);
+
+ /* set core boot address */
+ snd_sof_dsp_write(sdev, DSP_SECREG_BAR, ADSP_ALTVEC_C0, boot_addr);
+ snd_sof_dsp_write(sdev, DSP_SECREG_BAR, ADSP_ALTVECSEL, ADSP_ALTVECSEL_C0);
+
+ /* assert core reset */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN,
+ SW_RSTN_C0 | SW_DBG_RSTN_C0,
+ SW_RSTN_C0 | SW_DBG_RSTN_C0);
+
+ /* hardware requirement */
+ udelay(1);
+
+ /* release core reset */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN,
+ SW_RSTN_C0 | SW_DBG_RSTN_C0,
+ 0);
+
+ /* clear RUNSTALL (bit31) to start core */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG,
+ RUNSTALL, 0);
+}
+
+void mt8186_sof_hifixdsp_shutdown(struct snd_sof_dev *sdev)
+{
+ /* set RUNSTALL to stop core */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG,
+ RUNSTALL, RUNSTALL);
+
+ /* assert core reset */
+ snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN,
+ SW_RSTN_C0 | SW_DBG_RSTN_C0,
+ SW_RSTN_C0 | SW_DBG_RSTN_C0);
+}
+
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
new file mode 100644
index 000000000000..3333a0634e29
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2022 Mediatek Inc. All rights reserved.
+//
+// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
+// Tinghan Shen <tinghan.shen@mediatek.com>
+
+/*
+ * Hardware interface for audio DSP on mt8186
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/module.h>
+
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+#include "../../ops.h"
+#include "../../sof-of-dev.h"
+#include "../../sof-audio.h"
+#include "../adsp_helper.h"
+#include "mt8186.h"
+#include "mt8186-clk.h"
+
+static int mt8186_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MBOX_OFFSET;
+}
+
+static int mt8186_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MBOX_OFFSET;
+}
+
+static int mt8186_send_msg(struct snd_sof_dev *sdev,
+ struct snd_sof_ipc_msg *msg)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+ msg->msg_size);
+
+ 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);
+ spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
+}
+
+static void mt8186_dsp_handle_request(struct mtk_adsp_ipc *ipc)
+{
+ struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
+ u32 p; /* panic code */
+ int ret;
+
+ /* Read the message from the debug box. */
+ sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4,
+ &p, sizeof(p));
+
+ /* Check to see if the message is a panic code 0x0dead*** */
+ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ snd_sof_dsp_panic(priv->sdev, p, true);
+ } else {
+ snd_sof_ipc_msgs_rx(priv->sdev);
+
+ /* tell DSP cmd is done */
+ ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP);
+ if (ret)
+ dev_err(priv->dev, "request send ipc failed");
+ }
+}
+
+static struct mtk_adsp_ipc_ops dsp_ops = {
+ .handle_reply = mt8186_dsp_handle_reply,
+ .handle_request = mt8186_dsp_handle_request,
+};
+
+static int platform_parse_resource(struct platform_device *pdev, void *data)
+{
+ struct resource *mmio;
+ struct resource res;
+ struct device_node *mem_region;
+ struct device *dev = &pdev->dev;
+ struct mtk_adsp_chip_info *adsp = data;
+ int ret;
+
+ mem_region = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!mem_region) {
+ dev_err(dev, "no dma memory-region phandle\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(mem_region, 0, &res);
+ of_node_put(mem_region);
+ if (ret) {
+ dev_err(dev, "of_address_to_resource dma failed\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "DMA %pR\n", &res);
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_err(dev, "of_reserved_mem_device_init failed\n");
+ return ret;
+ }
+
+ mem_region = of_parse_phandle(dev->of_node, "memory-region", 1);
+ if (!mem_region) {
+ dev_err(dev, "no memory-region sysmem phandle\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(mem_region, 0, &res);
+ of_node_put(mem_region);
+ if (ret) {
+ dev_err(dev, "of_address_to_resource sysmem failed\n");
+ return ret;
+ }
+
+ adsp->pa_dram = (phys_addr_t)res.start;
+ if (adsp->pa_dram & DRAM_REMAP_MASK) {
+ dev_err(dev, "adsp memory(%#x) is not 4K-aligned\n",
+ (u32)adsp->pa_dram);
+ return -EINVAL;
+ }
+
+ adsp->dramsize = resource_size(&res);
+ if (adsp->dramsize < TOTAL_SIZE_SHARED_DRAM_FROM_TAIL) {
+ dev_err(dev, "adsp memory(%#x) is not enough for share\n",
+ adsp->dramsize);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "dram pbase=%pa size=%#x\n", &adsp->pa_dram, adsp->dramsize);
+
+ mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+ if (!mmio) {
+ dev_err(dev, "no ADSP-CFG register resource\n");
+ return -ENXIO;
+ }
+
+ adsp->va_cfgreg = devm_ioremap_resource(dev, mmio);
+ if (IS_ERR(adsp->va_cfgreg))
+ return PTR_ERR(adsp->va_cfgreg);
+
+ adsp->pa_cfgreg = (phys_addr_t)mmio->start;
+ adsp->cfgregsize = resource_size(mmio);
+
+ dev_dbg(dev, "cfgreg pbase=%pa size=%#x\n", &adsp->pa_cfgreg, adsp->cfgregsize);
+
+ mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+ if (!mmio) {
+ dev_err(dev, "no SRAM resource\n");
+ return -ENXIO;
+ }
+
+ adsp->pa_sram = (phys_addr_t)mmio->start;
+ adsp->sramsize = resource_size(mmio);
+
+ dev_dbg(dev, "sram pbase=%pa size=%#x\n", &adsp->pa_sram, adsp->sramsize);
+
+ mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sec");
+ if (!mmio) {
+ dev_err(dev, "no SEC register resource\n");
+ return -ENXIO;
+ }
+
+ adsp->va_secreg = devm_ioremap_resource(dev, mmio);
+ if (IS_ERR(adsp->va_secreg))
+ return PTR_ERR(adsp->va_secreg);
+
+ adsp->pa_secreg = (phys_addr_t)mmio->start;
+ adsp->secregsize = resource_size(mmio);
+
+ dev_dbg(dev, "secreg pbase=%pa size=%#x\n", &adsp->pa_secreg, adsp->secregsize);
+
+ mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bus");
+ if (!mmio) {
+ dev_err(dev, "no BUS register resource\n");
+ return -ENXIO;
+ }
+
+ adsp->va_busreg = devm_ioremap_resource(dev, mmio);
+ if (IS_ERR(adsp->va_busreg))
+ return PTR_ERR(adsp->va_busreg);
+
+ adsp->pa_busreg = (phys_addr_t)mmio->start;
+ adsp->busregsize = resource_size(mmio);
+
+ dev_dbg(dev, "busreg pbase=%pa size=%#x\n", &adsp->pa_busreg, adsp->busregsize);
+
+ return 0;
+}
+
+static void adsp_sram_power_on(struct snd_sof_dev *sdev)
+{
+ snd_sof_dsp_update_bits(sdev, DSP_BUSREG_BAR, ADSP_SRAM_POOL_CON,
+ DSP_SRAM_POOL_PD_MASK, 0);
+}
+
+static void adsp_sram_power_off(struct snd_sof_dev *sdev)
+{
+ snd_sof_dsp_update_bits(sdev, DSP_BUSREG_BAR, ADSP_SRAM_POOL_CON,
+ DSP_SRAM_POOL_PD_MASK, DSP_SRAM_POOL_PD_MASK);
+}
+
+/* Init the basic DSP DRAM address */
+static int adsp_memory_remap_init(struct snd_sof_dev *sdev, struct mtk_adsp_chip_info *adsp)
+{
+ u32 offset;
+
+ offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW;
+ adsp->dram_offset = offset;
+ offset >>= DRAM_REMAP_SHIFT;
+
+ dev_dbg(sdev->dev, "adsp->pa_dram %pa, offset %#x\n", &adsp->pa_dram, offset);
+
+ snd_sof_dsp_write(sdev, DSP_BUSREG_BAR, DSP_C0_EMI_MAP_ADDR, offset);
+ snd_sof_dsp_write(sdev, DSP_BUSREG_BAR, DSP_C0_DMAEMI_MAP_ADDR, offset);
+
+ if (offset != snd_sof_dsp_read(sdev, DSP_BUSREG_BAR, DSP_C0_EMI_MAP_ADDR) ||
+ offset != snd_sof_dsp_read(sdev, DSP_BUSREG_BAR, DSP_C0_DMAEMI_MAP_ADDR)) {
+ dev_err(sdev->dev, "emi remap fail\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_adsp_chip_info *adsp = data;
+ u32 shared_size;
+
+ /* remap shared-dram base to be non-cachable */
+ shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL;
+ adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size;
+ if (adsp->va_dram) {
+ adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size;
+ } else {
+ adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram,
+ shared_size);
+ if (!adsp->shared_dram) {
+ dev_err(dev, "ioremap failed for shared DRAM\n");
+ return -ENOMEM;
+ }
+ }
+ dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n",
+ adsp->shared_dram, &adsp->pa_shared_dram, shared_size);
+
+ return 0;
+}
+
+static int mt8186_run(struct snd_sof_dev *sdev)
+{
+ u32 adsp_bootup_addr;
+
+ adsp_bootup_addr = SRAM_PHYS_BASE_FROM_DSP_VIEW;
+ dev_dbg(sdev->dev, "HIFIxDSP boot from base : 0x%08X\n", adsp_bootup_addr);
+ mt8186_sof_hifixdsp_boot_sequence(sdev, adsp_bootup_addr);
+
+ return 0;
+}
+
+static int mt8186_dsp_probe(struct snd_sof_dev *sdev)
+{
+ struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev);
+ struct adsp_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ sdev->pdata->hw_pdata = priv;
+ priv->dev = sdev->dev;
+ priv->sdev = sdev;
+
+ priv->adsp = devm_kzalloc(&pdev->dev, sizeof(struct mtk_adsp_chip_info), GFP_KERNEL);
+ if (!priv->adsp)
+ return -ENOMEM;
+
+ ret = platform_parse_resource(pdev, priv->adsp);
+ if (ret)
+ return ret;
+
+ sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev,
+ priv->adsp->pa_sram,
+ priv->adsp->sramsize);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
+ dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n",
+ &priv->adsp->pa_sram, priv->adsp->sramsize);
+ return -ENOMEM;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev,
+ priv->adsp->pa_dram,
+ priv->adsp->dramsize);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
+ dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n",
+ &priv->adsp->pa_dram, priv->adsp->dramsize);
+ return -ENOMEM;
+ }
+
+ priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM];
+
+ ret = adsp_shared_base_ioremap(pdev, priv->adsp);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n");
+ return ret;
+ }
+
+ sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg;
+ sdev->bar[DSP_SECREG_BAR] = priv->adsp->va_secreg;
+ sdev->bar[DSP_BUSREG_BAR] = priv->adsp->va_busreg;
+
+ sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM;
+ sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = mt8186_get_mailbox_offset(sdev);
+
+ ret = adsp_memory_remap_init(sdev, priv->adsp);
+ if (ret) {
+ dev_err(sdev->dev, "adsp_memory_remap_init fail!\n");
+ return ret;
+ }
+
+ /* enable adsp clock before touching registers */
+ ret = mt8186_adsp_init_clock(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "mt8186_adsp_init_clock failed\n");
+ return ret;
+ }
+
+ ret = mt8186_adsp_clock_on(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "mt8186_adsp_clock_on fail!\n");
+ return ret;
+ }
+
+ adsp_sram_power_on(sdev);
+
+ priv->ipc_dev = platform_device_register_data(&pdev->dev, "mtk-adsp-ipc",
+ PLATFORM_DEVID_NONE,
+ pdev, sizeof(*pdev));
+ if (IS_ERR(priv->ipc_dev)) {
+ ret = IS_ERR(priv->ipc_dev);
+ dev_err(sdev->dev, "failed to create mtk-adsp-ipc device\n");
+ goto err_adsp_off;
+ }
+
+ priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
+ if (!priv->dsp_ipc) {
+ ret = -EPROBE_DEFER;
+ dev_err(sdev->dev, "failed to get drvdata\n");
+ goto exit_pdev_unregister;
+ }
+
+ mtk_adsp_ipc_set_data(priv->dsp_ipc, priv);
+ priv->dsp_ipc->ops = &dsp_ops;
+
+ return 0;
+
+exit_pdev_unregister:
+ platform_device_unregister(priv->ipc_dev);
+err_adsp_off:
+ adsp_sram_power_off(sdev);
+ mt8186_adsp_clock_off(sdev);
+
+ return ret;
+}
+
+static int mt8186_dsp_remove(struct snd_sof_dev *sdev)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+ platform_device_unregister(priv->ipc_dev);
+ mt8186_sof_hifixdsp_shutdown(sdev);
+ adsp_sram_power_off(sdev);
+ mt8186_adsp_clock_off(sdev);
+
+ return 0;
+}
+
+static int mt8186_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
+{
+ mt8186_sof_hifixdsp_shutdown(sdev);
+ adsp_sram_power_off(sdev);
+ mt8186_adsp_clock_off(sdev);
+
+ return 0;
+}
+
+static int mt8186_dsp_resume(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ ret = mt8186_adsp_clock_on(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "mt8186_adsp_clock_on fail!\n");
+ return ret;
+ }
+
+ adsp_sram_power_on(sdev);
+
+ return ret;
+}
+
+/* on mt8186 there is 1 to 1 match between type and BAR idx */
+static int mt8186_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+ return type;
+}
+
+static int mt8186_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz)
+{
+ sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
+ return 0;
+}
+
+/* mt8186 ops */
+static struct snd_sof_dsp_ops sof_mt8186_ops = {
+ /* probe and remove */
+ .probe = mt8186_dsp_probe,
+ .remove = mt8186_dsp_remove,
+
+ /* DSP core boot */
+ .run = mt8186_run,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* Register IO */
+ .write = sof_io_write,
+ .read = sof_io_read,
+ .write64 = sof_io_write64,
+ .read64 = sof_io_read64,
+
+ /* ipc */
+ .send_msg = mt8186_send_msg,
+ .get_mailbox_offset = mt8186_get_mailbox_offset,
+ .get_window_offset = mt8186_get_window_offset,
+ .ipc_msg_data = mt8186_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
+
+ /* misc */
+ .get_bar_index = mt8186_get_bar_index,
+
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* Firmware ops */
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
+
+ /* PM */
+ .suspend = mt8186_dsp_suspend,
+ .resume = mt8186_dsp_resume,
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+};
+
+static const struct sof_dev_desc sof_of_mt8186_desc = {
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "mediatek/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "mediatek/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-mt8186.ri",
+ },
+ .nocodec_tplg_filename = "sof-mt8186-nocodec.tplg",
+ .ops = &sof_mt8186_ops,
+};
+
+static const struct of_device_id sof_of_mt8186_ids[] = {
+ { .compatible = "mediatek,mt8186-dsp", .data = &sof_of_mt8186_desc},
+ { }
+};
+MODULE_DEVICE_TABLE(of, sof_of_mt8186_ids);
+
+/* DT driver definition */
+static struct platform_driver snd_sof_of_mt8186_driver = {
+ .probe = sof_of_probe,
+ .remove = sof_of_remove,
+ .driver = {
+ .name = "sof-audio-of-mt8186",
+ .pm = &sof_of_pm,
+ .of_match_table = sof_of_mt8186_ids,
+ },
+};
+module_platform_driver(snd_sof_of_mt8186_driver);
+
+MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_IMPORT_NS(SND_SOC_SOF_MTK_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h
new file mode 100644
index 000000000000..98b2965e5ba6
--- /dev/null
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+/*
+ * Copyright (c) 2022 MediaTek Corporation. All rights reserved.
+ *
+ * Header file for the mt8186 DSP register definition
+ */
+
+#ifndef __MT8186_H
+#define __MT8186_H
+
+struct mtk_adsp_chip_info;
+struct snd_sof_dev;
+
+#define DSP_REG_BAR 4
+#define DSP_SECREG_BAR 5
+#define DSP_BUSREG_BAR 6
+
+/*****************************************************************************
+ * R E G I S T E R TABLE
+ *****************************************************************************/
+/* dsp cfg */
+#define ADSP_CFGREG_SW_RSTN 0x0000
+#define SW_DBG_RSTN_C0 BIT(0)
+#define SW_RSTN_C0 BIT(4)
+#define ADSP_HIFI_IO_CONFIG 0x000C
+#define TRACEMEMREADY BIT(15)
+#define RUNSTALL BIT(31)
+#define ADSP_IRQ_MASK 0x0030
+#define ADSP_DVFSRC_REQ 0x0040
+#define ADSP_DDREN_REQ_0 0x0044
+#define ADSP_SEMAPHORE 0x0064
+#define ADSP_WDT_CON_C0 0x007C
+#define ADSP_MBOX_IRQ_EN 0x009C
+#define DSP_MBOX0_IRQ_EN BIT(0)
+#define DSP_MBOX1_IRQ_EN BIT(1)
+#define DSP_MBOX2_IRQ_EN BIT(2)
+#define DSP_MBOX3_IRQ_EN BIT(3)
+#define DSP_MBOX4_IRQ_EN BIT(4)
+#define DSP_PDEBUGPC 0x013C
+#define ADSP_CK_EN 0x1000
+#define CORE_CLK_EN BIT(0)
+#define COREDBG_EN BIT(1)
+#define TIMER_EN BIT(3)
+#define DMA_EN BIT(4)
+#define UART_EN BIT(5)
+#define ADSP_UART_CTRL 0x1010
+#define UART_BCLK_CG BIT(0)
+#define UART_RSTN BIT(3)
+
+/* dsp sec */
+#define ADSP_PRID 0x0
+#define ADSP_ALTVEC_C0 0x04
+#define ADSP_ALTVECSEL 0x0C
+#define ADSP_ALTVECSEL_C0 BIT(1)
+
+/* dsp bus */
+#define ADSP_SRAM_POOL_CON 0x190
+#define DSP_SRAM_POOL_PD_MASK 0xF00F /* [0:3] and [12:15] */
+#define DSP_C0_EMI_MAP_ADDR 0xA00 /* ADSP Core0 To EMI Address Remap */
+#define DSP_C0_DMAEMI_MAP_ADDR 0xA08 /* DMA0 To EMI Address Remap */
+
+/* DSP memories */
+#define MBOX_OFFSET 0x500000 /* DRAM */
+#define MBOX_SIZE 0x1000 /* consistent with which in memory.h of sof fw */
+#define DSP_DRAM_SIZE 0xA00000 /* 16M */
+
+/*remap dram between AP and DSP view, 4KB aligned*/
+#define SRAM_PHYS_BASE_FROM_DSP_VIEW 0x4E100000 /* MT8186 DSP view */
+#define DRAM_PHYS_BASE_FROM_DSP_VIEW 0x60000000 /* MT8186 DSP view */
+#define DRAM_REMAP_SHIFT 12
+#define DRAM_REMAP_MASK 0xFFF
+
+#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/
+#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/
+#define TOTAL_SIZE_SHARED_DRAM_FROM_TAIL (SIZE_SHARED_DRAM_DL + SIZE_SHARED_DRAM_UL)
+
+void mt8186_sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr);
+void mt8186_sof_hifixdsp_shutdown(struct snd_sof_dev *sdev);
+#endif
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index 3ab12f352935..30111ab23bf5 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -24,9 +24,103 @@
#include "../../sof-of-dev.h"
#include "../../sof-audio.h"
#include "../adsp_helper.h"
+#include "../mtk-adsp-common.h"
#include "mt8195.h"
#include "mt8195-clk.h"
+static int mt8195_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MBOX_OFFSET;
+}
+
+static int mt8195_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MBOX_OFFSET;
+}
+
+static int mt8195_send_msg(struct snd_sof_dev *sdev,
+ struct snd_sof_ipc_msg *msg)
+{
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+ msg->msg_size);
+
+ 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);
+ spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
+}
+
+static void mt8195_dsp_handle_request(struct mtk_adsp_ipc *ipc)
+{
+ struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
+ u32 p; /* panic code */
+ int ret;
+
+ /* Read the message from the debug box. */
+ sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4,
+ &p, sizeof(p));
+
+ /* Check to see if the message is a panic code 0x0dead*** */
+ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ snd_sof_dsp_panic(priv->sdev, p, true);
+ } else {
+ snd_sof_ipc_msgs_rx(priv->sdev);
+
+ /* tell DSP cmd is done */
+ ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP);
+ if (ret)
+ dev_err(priv->dev, "request send ipc failed");
+ }
+}
+
+static struct mtk_adsp_ipc_ops dsp_ops = {
+ .handle_reply = mt8195_dsp_handle_reply,
+ .handle_request = mt8195_dsp_handle_request,
+};
+
static int platform_parse_resource(struct platform_device *pdev, void *data)
{
struct resource *mmio;
@@ -285,15 +379,36 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev)
}
sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg;
- sdev->bar[DSP_MBOX0_BAR] = priv->adsp->va_mboxreg[0];
- sdev->bar[DSP_MBOX1_BAR] = priv->adsp->va_mboxreg[1];
- sdev->bar[DSP_MBOX2_BAR] = priv->adsp->va_mboxreg[2];
sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM;
sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = mt8195_get_mailbox_offset(sdev);
+
+ priv->ipc_dev = platform_device_register_data(&pdev->dev, "mtk-adsp-ipc",
+ PLATFORM_DEVID_NONE,
+ pdev, sizeof(*pdev));
+ if (IS_ERR(priv->ipc_dev)) {
+ ret = PTR_ERR(priv->ipc_dev);
+ dev_err(sdev->dev, "failed to register mtk-adsp-ipc device\n");
+ goto err_adsp_sram_power_off;
+ }
+
+ priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
+ if (!priv->dsp_ipc) {
+ ret = -EPROBE_DEFER;
+ dev_err(sdev->dev, "failed to get drvdata\n");
+ goto exit_pdev_unregister;
+ }
+
+ mtk_adsp_ipc_set_data(priv->dsp_ipc, priv);
+ priv->dsp_ipc->ops = &dsp_ops;
+
return 0;
+exit_pdev_unregister:
+ platform_device_unregister(priv->ipc_dev);
err_adsp_sram_power_off:
adsp_sram_power_on(&pdev->dev, false);
exit_clk_disable:
@@ -302,10 +417,17 @@ exit_clk_disable:
return ret;
}
+static int mt8195_dsp_shutdown(struct snd_sof_dev *sdev)
+{
+ return snd_sof_suspend(sdev->dev);
+}
+
static int mt8195_dsp_remove(struct snd_sof_dev *sdev)
{
struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev);
+ struct adsp_priv *priv = sdev->pdata->hw_pdata;
+ platform_device_unregister(priv->ipc_dev);
adsp_sram_power_on(&pdev->dev, false);
adsp_clock_off(sdev);
@@ -356,6 +478,39 @@ static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type)
return type;
}
+static int mt8195_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz)
+{
+ sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
+ return 0;
+}
+
+static void mt8195_adsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ u32 dbg_pc, dbg_data, dbg_bus0, dbg_bus1, dbg_inst;
+ u32 dbg_ls0stat, dbg_ls1stat, faultbus, faultinfo, swrest;
+
+ /* 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_bus0 = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGBUS0);
+ dbg_bus1 = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGBUS1);
+ 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_ls1stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS1STAT);
+ faultbus = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTBUS);
+ faultinfo = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTINFO);
+ swrest = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_RESET_SW);
+
+ dev_info(sdev->dev, "adsp dump : pc %#x, data %#x, bus0 %#x, bus1 %#x, swrest %#x",
+ dbg_pc, dbg_data, dbg_bus0, dbg_bus1, swrest);
+ dev_info(sdev->dev, "dbg_inst %#x, ls0stat %#x, ls1stat %#x, faultbus %#x, faultinfo %#x",
+ dbg_inst, dbg_ls0stat, dbg_ls1stat, faultbus, faultinfo);
+
+ mtk_adsp_dump(sdev, flags);
+}
+
static struct snd_soc_dai_driver mt8195_dai[] = {
{
.name = "SOF_DL2",
@@ -388,10 +543,11 @@ static struct snd_soc_dai_driver mt8195_dai[] = {
};
/* mt8195 ops */
-static const struct snd_sof_dsp_ops sof_mt8195_ops = {
+static struct snd_sof_dsp_ops sof_mt8195_ops = {
/* probe and remove */
.probe = mt8195_dsp_probe,
.remove = mt8195_dsp_remove,
+ .shutdown = mt8195_dsp_shutdown,
/* DSP core boot */
.run = mt8195_run,
@@ -406,17 +562,25 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = {
.write64 = sof_io_write64,
.read64 = sof_io_read64,
+ /* ipc */
+ .send_msg = mt8195_send_msg,
+ .get_mailbox_offset = mt8195_get_mailbox_offset,
+ .get_window_offset = mt8195_get_window_offset,
+ .ipc_msg_data = mt8195_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
+
/* misc */
.get_bar_index = mt8195_get_bar_index,
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
/* firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
/* Firmware ops */
.dsp_arch_ops = &sof_xtensa_arch_ops,
+ /* Debug information */
+ .dbg_dump = mt8195_adsp_dump,
+
/* DAI drivers */
.drv = mt8195_dai,
.num_drv = ARRAY_SIZE(mt8195_dai),
@@ -434,11 +598,20 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = {
};
static const struct sof_dev_desc sof_of_mt8195_desc = {
- .default_fw_path = "mediatek/sof",
- .default_tplg_path = "mediatek/sof-tplg",
- .default_fw_filename = "sof-mt8195.ri",
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "mediatek/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "mediatek/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-mt8195.ri",
+ },
.nocodec_tplg_filename = "sof-mt8195-nocodec.tplg",
.ops = &sof_mt8195_ops,
+ .ipc_timeout = 1000,
};
static const struct of_device_id sof_of_mt8195_ids[] = {
@@ -451,6 +624,7 @@ MODULE_DEVICE_TABLE(of, sof_of_mt8195_ids);
static struct platform_driver snd_sof_of_mt8195_driver = {
.probe = sof_of_probe,
.remove = sof_of_remove,
+ .shutdown = sof_of_shutdown,
.driver = {
.name = "sof-audio-of-mt8195",
.pm = &sof_of_pm,
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c
new file mode 100644
index 000000000000..1e0769c668a7
--- /dev/null
+++ b/sound/soc/sof/mediatek/mtk-adsp-common.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 MediaTek Inc. All rights reserved.
+//
+// Author: YC Hung <yc.hung@mediatek.com>
+
+/*
+ * Common helpers for the audio DSP on MediaTek platforms
+ */
+
+#include <linux/module.h>
+#include <sound/sof/xtensa.h>
+#include "../ops.h"
+#include "mtk-adsp-common.h"
+
+/**
+ * mtk_adsp_get_registers() - This function is called in case of DSP oops
+ * in order to gather information about the registers, filename and
+ * linenumber and stack.
+ * @sdev: SOF device
+ * @xoops: Stores information about registers.
+ * @panic_info: Stores information about filename and line number.
+ * @stack: Stores the stack dump.
+ * @stack_words: Size of the stack dump.
+ */
+static void mtk_adsp_get_registers(struct snd_sof_dev *sdev,
+ struct sof_ipc_dsp_oops_xtensa *xoops,
+ struct sof_ipc_panic_info *panic_info,
+ u32 *stack, size_t stack_words)
+{
+ u32 offset = sdev->dsp_oops_offset;
+
+ /* first read registers */
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
+ offset += xoops->arch_hdr.totalsize;
+ sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
+
+ /* then get the stack */
+ offset += sizeof(*panic_info);
+ sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
+}
+
+/**
+ * mtk_adsp_dump() - This function is called when a panic message is
+ * received from the firmware.
+ * @sdev: SOF device
+ * @flags: parameter not used but required by ops prototype
+ */
+void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
+ struct sof_ipc_dsp_oops_xtensa xoops;
+ struct sof_ipc_panic_info panic_info;
+ u32 stack[MTK_ADSP_STACK_DUMP_SIZE];
+ u32 status;
+
+ /* Get information about the panic status from the debug box area.
+ * Compute the trace point based on the status.
+ */
+ sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4, &status, 4);
+
+ /* Get information about the registers, the filename and line
+ * number and the stack.
+ */
+ mtk_adsp_get_registers(sdev, &xoops, &panic_info, stack,
+ MTK_ADSP_STACK_DUMP_SIZE);
+
+ /* Print the information to the console */
+ sof_print_oops_and_stack(sdev, level, status, status, &xoops, &panic_info,
+ stack, MTK_ADSP_STACK_DUMP_SIZE);
+}
+EXPORT_SYMBOL(mtk_adsp_dump);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h
new file mode 100644
index 000000000000..612cff1f38f7
--- /dev/null
+++ b/sound/soc/sof/mediatek/mtk-adsp-common.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+#ifndef __MEDIATEK_ADSP_COMMON_H__
+#define __MEDIATEK_ADSP_COMMON_H__
+
+#define EXCEPT_MAX_HDR_SIZE 0x400
+#define MTK_ADSP_STACK_DUMP_SIZE 32
+
+void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags);
+#endif
diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c
index 235e2ef72178..ff066de4ceb9 100644
--- a/sound/soc/sof/ops.c
+++ b/sound/soc/sof/ops.c
@@ -177,7 +177,7 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl
snd_sof_dsp_dbg_dump(sdev, "DSP panic!",
SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
sof_set_fw_state(sdev, SOF_FW_CRASHED);
- snd_sof_trace_notify_for_error(sdev);
+ sof_fw_trace_fw_crashed(sdev);
} else {
snd_sof_dsp_dbg_dump(sdev,
"DSP panic (recovery will be attempted)",
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index a19474663767..b79ae4f66eba 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -21,6 +21,14 @@
#define sof_ops(sdev) \
((sdev)->pdata->desc->ops)
+static inline int sof_ops_init(struct snd_sof_dev *sdev)
+{
+ if (sdev->pdata->desc->ops_init)
+ return sdev->pdata->desc->ops_init(sdev);
+
+ return 0;
+}
+
/* Mandatory operations are verified during probing */
/* init */
@@ -367,32 +375,6 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev,
return sof_ops(sdev)->send_msg(sdev, msg);
}
-/* host DMA trace */
-static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev,
- struct sof_ipc_dma_trace_params_ext *dtrace_params)
-{
- if (sof_ops(sdev)->trace_init)
- return sof_ops(sdev)->trace_init(sdev, dtrace_params);
-
- return 0;
-}
-
-static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev)
-{
- if (sof_ops(sdev)->trace_release)
- return sof_ops(sdev)->trace_release(sdev);
-
- return 0;
-}
-
-static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd)
-{
- if (sof_ops(sdev)->trace_trigger)
- return sof_ops(sdev)->trace_trigger(sdev, cmd);
-
- return 0;
-}
-
/* host PCM ops */
static inline int
snd_sof_pcm_platform_open(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 658cd8966c9a..a76d0b5b2ad9 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -82,8 +82,10 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
-int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
- struct snd_sof_pcm *spcm, int dir)
+static int
+sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
+ struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
+ struct snd_sof_platform_stream_params *platform_params, int dir)
{
struct snd_soc_dai *dai;
int ret, j;
@@ -102,7 +104,7 @@ int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm
spcm->stream[dir].list = list;
- ret = sof_widget_list_setup(sdev, spcm, dir);
+ ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
spcm->pcm.pcm_id, dir);
@@ -150,9 +152,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
+ ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
+ if (ret < 0) {
+ dev_err(component->dev, "platform hw params failed\n");
+ return ret;
+ }
+
/* if this is a repeated hw_params without hw_free, skip setting up widgets */
if (!spcm->stream[substream->stream].list) {
- ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
+ ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
+ substream->stream);
if (ret < 0)
return ret;
}
@@ -166,12 +175,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
- if (ret < 0) {
- dev_err(component->dev, "platform hw params failed\n");
- return ret;
- }
-
if (pcm_ops->hw_params) {
ret = pcm_ops->hw_params(component, substream, params, &platform_params);
if (ret < 0)
@@ -347,12 +350,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
- if (!ret && reset_hw_params) {
+ if (!ret && reset_hw_params)
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
free_widget_list);
- if (ret < 0)
- return ret;
- }
return ret;
}
@@ -396,7 +396,7 @@ static int sof_pcm_open(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
+ struct snd_sof_dsp_ops *ops = sof_ops(sdev);
struct snd_sof_pcm *spcm;
struct snd_soc_tplg_stream_caps *caps;
int ret;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 1c319582ca6f..18eb327a57f0 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -102,11 +102,18 @@ static int sof_resume(struct device *dev, bool runtime_resume)
/*
* Nothing further to be done for platforms that support the low power
- * D0 substate.
+ * D0 substate. Resume trace and return when resuming from
+ * low-power D0 substate
*/
if (!runtime_resume && sof_ops(sdev)->set_power_state &&
- old_state == SOF_DSP_PM_D0)
+ old_state == SOF_DSP_PM_D0) {
+ ret = sof_fw_trace_resume(sdev);
+ if (ret < 0)
+ /* non fatal */
+ dev_warn(sdev->dev,
+ "failed to enable trace after resume %d\n", ret);
return 0;
+ }
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
@@ -135,8 +142,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
- /* resume DMA trace, only need send ipc */
- ret = snd_sof_init_trace_ipc(sdev);
+ /* resume DMA trace */
+ ret = sof_fw_trace_resume(sdev);
if (ret < 0) {
/* non fatal */
dev_warn(sdev->dev,
@@ -187,7 +194,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
/* prepare for streams to be resumed properly upon resume */
if (!runtime_suspend) {
- ret = sof_set_hw_params_upon_resume(sdev->dev);
+ ret = snd_sof_dsp_hw_params_upon_resume(sdev);
if (ret < 0) {
dev_err(sdev->dev,
"error: setting hw_params flag during suspend %d\n",
@@ -201,6 +208,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
/* 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;
@@ -209,8 +217,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (tplg_ops->tear_down_all_pipelines)
tplg_ops->tear_down_all_pipelines(sdev, false);
- /* release trace */
- snd_sof_release_trace(sdev);
+ /* 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);
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index 74982c04497b..1b04dcb33293 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -74,20 +74,20 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
- sof_pdata->fw_filename = desc->default_fw_filename;
+ sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC];
/* alternate fw and tplg filenames ? */
if (fw_path)
sof_pdata->fw_filename_prefix = fw_path;
else
sof_pdata->fw_filename_prefix =
- sof_pdata->desc->default_fw_path;
+ sof_pdata->desc->default_fw_path[SOF_IPC];
if (tplg_path)
sof_pdata->tplg_filename_prefix = tplg_path;
else
sof_pdata->tplg_filename_prefix =
- sof_pdata->desc->default_tplg_path;
+ sof_pdata->desc->default_tplg_path[SOF_IPC];
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_acpi_probe_complete;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index b2f009a0c5b7..8d740635a4bb 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -12,61 +12,18 @@
#include "sof-audio.h"
#include "ops.h"
-static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
-{
- int ret;
-
- /* reset readback offset for scontrol */
- scontrol->readback_offset = 0;
-
- ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
- if (ret < 0)
- dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n",
- scontrol->comp_id);
-
- return ret;
-}
-
-static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
-{
- struct snd_sof_control *scontrol;
- int ret;
-
- /* set up all controls for the widget */
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
- if (scontrol->comp_id == swidget->comp_id) {
- /* set kcontrol data in DSP */
- ret = sof_kcontrol_setup(sdev, scontrol);
- if (ret < 0) {
- dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n",
- swidget->widget->name);
- return ret;
- }
-
- /*
- * Read back the data from the DSP for static widgets. This is particularly
- * useful for binary kcontrols associated with static pipeline widgets to
- * initialize the data size to match that in the DSP.
- */
- if (swidget->dynamic_pipeline_widget)
- continue;
-
- ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
- if (ret < 0)
- dev_warn(sdev->dev, "Failed kcontrol get for control in widget %s\n",
- swidget->widget->name);
- }
-
- return 0;
-}
-
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 = sdev->ipc->ops->tplg;
struct snd_sof_route *sroute;
list_for_each_entry(sroute, &sdev->route_list, list)
- if (sroute->src_widget == widget || sroute->sink_widget == widget)
+ if (sroute->src_widget == widget || sroute->sink_widget == widget) {
+ if (sroute->setup && tplg_ops->route_free)
+ tplg_ops->route_free(sdev, sroute);
+
sroute->setup = false;
+ }
}
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
@@ -82,6 +39,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
+ /* reset route setup status for all routes that contain this widget */
+ sof_reset_route_setup_status(sdev, swidget);
+
/* continue to disable core even if IPC fails */
if (tplg_ops->widget_free)
err = tplg_ops->widget_free(sdev, swidget);
@@ -98,10 +58,6 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
err = ret;
}
- /* reset route setup status for all routes that contain this widget */
- sof_reset_route_setup_status(sdev, swidget);
- swidget->complete = 0;
-
/*
* free the scheduler widget (same as pipe_widget) associated with the current swidget.
* skip for static pipelines
@@ -110,6 +66,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
ret = sof_widget_free(sdev, swidget->pipe_widget);
if (ret < 0 && !err)
err = ret;
+ swidget->pipe_widget->complete = 0;
}
if (!err)
@@ -179,11 +136,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
/* restore kcontrols for widget */
- ret = sof_widget_kcontrol_setup(sdev, swidget);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
- swidget->widget->name);
- goto widget_free;
+ if (tplg_ops->control->widget_kcontrol_setup) {
+ ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
+ if (ret < 0)
+ goto widget_free;
}
dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
@@ -301,28 +257,249 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
return 0;
}
-int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+static void
+sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
+{
+ const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_soc_dapm_path *p;
+
+ if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared)
+ goto sink_unprepare;
+
+ /* unprepare the source widget */
+ widget_ops[widget->id].ipc_unprepare(swidget);
+ swidget->prepared = false;
+
+sink_unprepare:
+ /* unprepare all widgets in the sink paths */
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!p->walking && p->sink->dobj.private) {
+ p->walking = true;
+ sof_unprepare_widgets_in_path(sdev, p->sink);
+ p->walking = false;
+ }
+ }
+}
+
+static int
+sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ struct snd_pcm_hw_params *pipeline_params, int dir)
+{
+ const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_soc_dapm_path *p;
+ int ret;
+
+ if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
+ goto sink_prepare;
+
+ /* prepare the source widget */
+ ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
+ pipeline_params, dir);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
+ return ret;
+ }
+
+ swidget->prepared = true;
+
+sink_prepare:
+ /* prepare all widgets in the sink paths */
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!p->walking && p->sink->dobj.private) {
+ p->walking = true;
+ ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
+ platform_params, pipeline_params, dir);
+ p->walking = false;
+ if (ret < 0) {
+ /* unprepare the source widget */
+ if (!widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
+ widget_ops[widget->id].ipc_unprepare(swidget);
+ swidget->prepared = false;
+ }
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * free all widgets in the sink path starting from the source widget
+ * (DAI type for capture, AIF type for playback)
+ */
+static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+ int dir)
+{
+ struct snd_soc_dapm_path *p;
+ int err;
+ int ret = 0;
+
+ /* free all widgets even in case of error to keep use counts balanced */
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
+ p->walking = true;
+ if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
+ err = sof_widget_free(sdev, widget->dobj.private);
+ if (err < 0)
+ ret = err;
+ }
+
+ err = sof_widget_free(sdev, p->sink->dobj.private);
+ if (err < 0)
+ ret = err;
+
+ err = sof_free_widgets_in_path(sdev, p->sink, dir);
+ if (err < 0)
+ ret = err;
+ p->walking = false;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * set up all widgets in the sink path starting from the source widget
+ * (DAI type for capture, AIF type for playback).
+ * The error path in this function ensures that all successfully set up widgets getting freed.
+ */
+static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+ int dir)
+{
+ struct snd_soc_dapm_path *p;
+ int ret;
+
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
+ p->walking = true;
+ if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
+ ret = sof_widget_setup(sdev, widget->dobj.private);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = sof_widget_setup(sdev, p->sink->dobj.private);
+ if (ret < 0) {
+ if (WIDGET_IS_AIF_OR_DAI(widget->id))
+ sof_widget_free(sdev, widget->dobj.private);
+ goto out;
+ }
+
+ ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
+ if (ret < 0) {
+ if (WIDGET_IS_AIF_OR_DAI(widget->id))
+ sof_widget_free(sdev, widget->dobj.private);
+ sof_widget_free(sdev, p->sink->dobj.private);
+ }
+out:
+ p->walking = false;
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int
+sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params, int dir,
+ enum sof_widget_op op)
+{
+ struct snd_soc_dapm_widget *widget;
+ char *str;
+ int ret = 0;
+ int i;
+
+ for_each_dapm_widgets(list, i, widget) {
+ /* starting widget for playback is AIF type */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
+ continue;
+
+ /* starting widget for capture is DAI type */
+ if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
+ continue;
+
+ switch (op) {
+ case SOF_WIDGET_SETUP:
+ ret = sof_set_up_widgets_in_path(sdev, widget, dir);
+ str = "set up";
+ break;
+ case SOF_WIDGET_FREE:
+ ret = sof_free_widgets_in_path(sdev, widget, dir);
+ str = "free";
+ break;
+ case SOF_WIDGET_PREPARE:
+ {
+ struct snd_pcm_hw_params pipeline_params;
+
+ str = "prepare";
+ /*
+ * When walking the list of connected widgets, the pipeline_params for each
+ * widget is modified by the source widget in the path. Use a local
+ * copy of the runtime params as the pipeline_params so that the runtime
+ * params does not get overwritten.
+ */
+ memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
+
+ ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
+ platform_params, &pipeline_params, dir);
+ break;
+ }
+ case SOF_WIDGET_UNPREPARE:
+ sof_unprepare_widgets_in_path(sdev, widget);
+ break;
+ default:
+ dev_err(sdev->dev, "Invalid widget op %d\n", op);
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ int dir)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
- int i, ret, num_widgets;
+ int i, ret;
/* nothing to set up */
if (!list)
return 0;
- /* set up widgets in the list */
- for_each_dapm_widgets(list, num_widgets, widget) {
- struct snd_sof_widget *swidget = widget->dobj.private;
-
- if (!swidget)
- continue;
+ /*
+ * Prepare widgets for set up. The prepare step is used to allocate memory, assign
+ * instance ID and pick the widget configuration based on the runtime PCM params.
+ */
+ ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ dir, SOF_WIDGET_PREPARE);
+ if (ret < 0)
+ return ret;
- /* set up the widget */
- ret = sof_widget_setup(sdev, swidget);
- if (ret < 0)
- goto widget_free;
+ /* Set up is used to send the IPC to the DSP to create the widget */
+ ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ dir, SOF_WIDGET_SETUP);
+ if (ret < 0) {
+ ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ dir, SOF_WIDGET_UNPREPARE);
+ return ret;
}
/*
@@ -364,18 +541,9 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
return 0;
widget_free:
- /* free all widgets that have been set up successfully */
- for_each_dapm_widgets(list, i, widget) {
- struct snd_sof_widget *swidget = widget->dobj.private;
-
- if (!swidget)
- continue;
-
- if (!num_widgets--)
- break;
-
- sof_widget_free(sdev, swidget);
- }
+ sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
+ SOF_WIDGET_FREE);
+ sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
return ret;
}
@@ -383,37 +551,22 @@ widget_free:
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
- struct snd_soc_dapm_widget *widget;
- int i, ret;
- int ret1 = 0;
+ int ret;
/* nothing to free */
if (!list)
return 0;
- /*
- * Free widgets in the list. This can fail but continue freeing other widgets to keep
- * use_counts balanced.
- */
- for_each_dapm_widgets(list, i, widget) {
- struct snd_sof_widget *swidget = widget->dobj.private;
-
- if (!swidget)
- continue;
+ /* send IPC to free widget in the DSP */
+ ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
- /*
- * free widget and its pipe_widget. Either of these can fail, but free as many as
- * possible before freeing the list and returning the error.
- */
- ret = sof_widget_free(sdev, swidget);
- if (ret < 0)
- ret1 = ret;
- }
+ /* unprepare the widget */
+ sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
snd_soc_dapm_dai_free_widgets(&list);
spcm->stream[dir].list = NULL;
- return ret1;
+ return ret;
}
/*
@@ -462,42 +615,6 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
return false;
}
-int sof_set_hw_params_upon_resume(struct device *dev)
-{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct snd_pcm_substream *substream;
- struct snd_sof_pcm *spcm;
- snd_pcm_state_t state;
- int dir;
-
- /*
- * SOF requires hw_params to be set-up internally upon resume.
- * So, set the flag to indicate this for those streams that
- * have been suspended.
- */
- list_for_each_entry(spcm, &sdev->pcm_list, list) {
- for_each_pcm_streams(dir) {
- /*
- * do not reset hw_params upon resume for streams that
- * were kept running during suspend
- */
- if (spcm->stream[dir].suspend_ignored)
- continue;
-
- substream = spcm->stream[dir].substream;
- if (!substream || !substream->runtime)
- continue;
-
- state = substream->runtime->status->state;
- if (state == SNDRV_PCM_STATE_SUSPENDED)
- spcm->prepared[dir] = false;
- }
- }
-
- /* set internal flag for BE */
- return snd_sof_dsp_hw_params_upon_resume(sdev);
-}
-
int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
{
@@ -701,7 +818,8 @@ int sof_machine_check(struct snd_sof_dev *sdev)
return -ENOMEM;
mach->drv_name = "sof-nocodec";
- sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
+ if (!sof_pdata->tplg_filename)
+ sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
sof_pdata->machine = mach;
snd_sof_set_mach_params(mach, sdev);
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 7f15b3bc8196..27cc5fb642e5 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -29,16 +29,47 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
+#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
+#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
+enum sof_widget_op {
+ SOF_WIDGET_PREPARE,
+ SOF_WIDGET_SETUP,
+ SOF_WIDGET_FREE,
+ SOF_WIDGET_UNPREPARE,
+};
+
/*
* Volume fractional word length define to 16 sets
* the volume linear gain value to use Qx.16 format
*/
#define VOLUME_FWL 16
+#define SOF_TLV_ITEMS 3
+
+static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
+{
+ if (value >= size)
+ return volume_map[size - 1];
+
+ return volume_map[value];
+}
+
+static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (volume_map[i] >= value)
+ return i;
+ }
+
+ return i - 1;
+}
+
struct snd_sof_widget;
struct snd_sof_route;
struct snd_sof_control;
@@ -86,6 +117,11 @@ struct sof_ipc_tplg_control_ops {
const unsigned int __user *binary_data, unsigned int size);
/* update control data based on notification from the DSP */
void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message);
+ /* Optional callback to setup kcontrols associated with an swidget */
+ int (*widget_kcontrol_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+ /* mandatory callback to set up volume table for volume kcontrols */
+ int (*set_up_volume_table)(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS],
+ int size);
};
/**
@@ -95,6 +131,8 @@ struct sof_ipc_tplg_control_ops {
* @token_list: List of token ID's that should be parsed for the widget
* @token_list_size: number of elements in token_list
* @bind_event: Function pointer for binding events to the widget
+ * @ipc_prepare: Optional op for preparing a widget for set up
+ * @ipc_unprepare: Optional op for unpreparing a widget
*/
struct sof_ipc_tplg_widget_ops {
int (*ipc_setup)(struct snd_sof_widget *swidget);
@@ -103,6 +141,11 @@ struct sof_ipc_tplg_widget_ops {
int token_list_size;
int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget,
u16 event_type);
+ int (*ipc_prepare)(struct snd_sof_widget *swidget,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ struct snd_pcm_hw_params *source_params, int dir);
+ void (*ipc_unprepare)(struct snd_sof_widget *swidget);
};
/**
@@ -112,6 +155,7 @@ struct sof_ipc_tplg_widget_ops {
* initialized to 0.
* @control: Pointer to the IPC-specific ops for topology kcontrol IO
* @route_setup: Function pointer for setting up pipeline connections
+ * @route_free: Optional op for freeing pipeline connections.
* @token_list: List of all tokens supported by the IPC version. The size of the token_list
* array should be SOF_TOKEN_COUNT. The unused elements in the array will be
* initialized to 0.
@@ -129,6 +173,7 @@ struct sof_ipc_tplg_ops {
const struct sof_ipc_tplg_widget_ops *widget;
const struct sof_ipc_tplg_control_ops *control;
int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
+ int (*route_free)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
const struct sof_token_info *token_list;
int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
@@ -247,7 +292,6 @@ struct snd_sof_control {
int max_volume_step; /* max volume step for volume_table */
int num_channels;
unsigned int access;
- u32 readback_offset; /* offset to mmapped data if used */
int info_type;
int index; /* pipeline ID */
void *priv; /* private data copied from topology */
@@ -292,10 +336,24 @@ struct snd_sof_widget {
struct snd_soc_component *scomp;
int comp_id;
int pipeline_id;
+ /*
+ * complete flag is used to indicate that pipeline set up is complete for scheduler type
+ * widgets. It is unused for all other widget types.
+ */
int complete;
+ /*
+ * the prepared flag is used to indicate that a widget has been prepared for getting set
+ * up in the DSP.
+ */
+ bool prepared;
int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
- int id;
+ int id; /* id is the DAPM widget type */
+ /*
+ * Instance ID is set dynamically when the widget gets set up in the FW. It should be
+ * unique for each module type across all pipelines. This will not be used in SOF_IPC.
+ */
+ int instance_id;
/*
* Flag indicating if the widget should be set up dynamically when a PCM is opened.
@@ -310,6 +368,7 @@ struct snd_sof_widget {
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_widget *pipe_widget;
+ void *module_info;
const guid_t uuid;
@@ -403,8 +462,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-
- struct snd_sof_pcm *spcm = NULL;
+ struct snd_sof_pcm *spcm;
list_for_each_entry(spcm, &sdev->pcm_list, list) {
if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id)
@@ -430,16 +488,10 @@ static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstre
static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { }
#endif
-/*
- * Mixer IPC
- */
-int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set);
-
/* DAI link fixup */
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
/* PM */
-int sof_set_hw_params_upon_resume(struct device *dev);
bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev);
bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
@@ -453,7 +505,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
struct snd_soc_dapm_widget *wsink);
/* PCM */
-int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ int dir);
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
struct snd_sof_pcm *spcm);
@@ -467,6 +522,5 @@ int get_token_uuid(void *elem, void *object, u32 offset);
int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id,
struct snd_sof_tuple *tuples, int num_tuples,
size_t object_size, int token_instance_num);
-int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
- struct snd_sof_pcm *spcm, int dir);
+u32 vol_compute_gain(u32 value, int *tlv);
#endif
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
index db3a052c5dd2..4bdecd80248a 100644
--- a/sound/soc/sof/sof-client-ipc-flood-test.c
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -217,10 +217,9 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf
ipc_count = MAX_IPC_FLOOD_COUNT;
}
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
- pm_runtime_put_noidle(dev);
goto out;
}
diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c
index dba6cfd7db09..03490a4d4ae7 100644
--- a/sound/soc/sof/sof-client-ipc-msg-injector.c
+++ b/sound/soc/sof/sof-client-ipc-msg-injector.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
#include "sof-client.h"
@@ -22,6 +23,8 @@
struct sof_msg_inject_priv {
struct dentry *dfs_file;
+ size_t max_msg_size;
+ enum sof_ipc_type ipc_type;
void *tx_buffer;
void *rx_buffer;
@@ -66,44 +69,157 @@ static ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer,
return count;
}
-static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *ppos)
+static ssize_t sof_msg_inject_ipc4_dfs_read(struct file *file,
+ 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 device *dev = &cdev->auxdev.dev;
- int ret, err;
- size_t size;
+ struct sof_ipc4_msg *ipc4_msg = priv->rx_buffer;
+ size_t header_size = sizeof(ipc4_msg->header_u64);
+ size_t remaining;
- if (*ppos)
+ if (!ipc4_msg->header_u64 || !count || *ppos)
return 0;
- size = simple_write_to_buffer(priv->tx_buffer, SOF_IPC_MSG_MAX_SIZE,
- ppos, buffer, count);
- if (size != count)
- return size > 0 ? -EFAULT : size;
+ /* we need space for the header at minimum (u64) */
+ if (count < header_size)
+ return -ENOSPC;
+
+ remaining = header_size;
+
+ /* Only get large config have payload */
+ if (SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_msg->primary) &&
+ (SOF_IPC4_MSG_TYPE_GET(ipc4_msg->primary) == SOF_IPC4_MOD_LARGE_CONFIG_GET))
+ remaining += ipc4_msg->data_size;
+
+ if (count > remaining)
+ count = remaining;
+ else if (count < remaining)
+ remaining = count;
+
+ /* copy the header first */
+ if (copy_to_user(buffer, &ipc4_msg->header_u64, header_size))
+ return -EFAULT;
+
+ *ppos += header_size;
+ remaining -= header_size;
+
+ if (!remaining)
+ return count;
+
+ if (remaining > ipc4_msg->data_size)
+ remaining = ipc4_msg->data_size;
+
+ /* Copy the payload */
+ if (copy_to_user(buffer + *ppos, ipc4_msg->data_ptr, remaining))
+ return -EFAULT;
+
+ *ppos += remaining;
+ return count;
+}
- ret = pm_runtime_get_sync(dev);
+static int sof_msg_inject_send_message(struct sof_client_dev *cdev)
+{
+ struct sof_msg_inject_priv *priv = cdev->data;
+ struct device *dev = &cdev->auxdev.dev;
+ int ret, err;
+
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
- pm_runtime_put_noidle(dev);
return ret;
}
/* send the message */
- memset(priv->rx_buffer, 0, SOF_IPC_MSG_MAX_SIZE);
ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
- SOF_IPC_MSG_MAX_SIZE);
+ priv->max_msg_size);
+ if (ret)
+ dev_err(dev, "IPC message send failed: %d\n", ret);
+
pm_runtime_mark_last_busy(dev);
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
- /* return size if test is successful */
- if (ret >= 0)
- ret = size;
-
return ret;
+}
+
+static ssize_t sof_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;
+ size_t size;
+ int ret;
+
+ if (*ppos)
+ return 0;
+
+ size = simple_write_to_buffer(priv->tx_buffer, priv->max_msg_size,
+ ppos, buffer, count);
+ if (size != count)
+ return size > 0 ? -EFAULT : size;
+
+ memset(priv->rx_buffer, 0, priv->max_msg_size);
+
+ ret = sof_msg_inject_send_message(cdev);
+
+ /* return the error code if test failed */
+ if (ret < 0)
+ size = ret;
+
+ return size;
+};
+
+static ssize_t sof_msg_inject_ipc4_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_ipc4_msg *ipc4_msg = priv->tx_buffer;
+ size_t size;
+ int ret;
+
+ if (*ppos)
+ return 0;
+
+ if (count < sizeof(ipc4_msg->header_u64))
+ return -EINVAL;
+
+ /* copy the header first */
+ size = simple_write_to_buffer(&ipc4_msg->header_u64,
+ sizeof(ipc4_msg->header_u64),
+ ppos, buffer, count);
+ if (size != sizeof(ipc4_msg->header_u64))
+ return size > 0 ? -EFAULT : size;
+
+ count -= size;
+ if (!count) {
+ /* Copy the payload */
+ size = simple_write_to_buffer(ipc4_msg->data_ptr,
+ priv->max_msg_size, ppos, buffer,
+ count);
+ if (size != count)
+ return size > 0 ? -EFAULT : size;
+ }
+
+ ipc4_msg->data_size = count;
+
+ /* Initialize the reply storage */
+ ipc4_msg = priv->rx_buffer;
+ ipc4_msg->header_u64 = 0;
+ ipc4_msg->data_size = priv->max_msg_size;
+ memset(ipc4_msg->data_ptr, 0, priv->max_msg_size);
+
+ ret = sof_msg_inject_send_message(cdev);
+
+ /* return the error code if test failed */
+ if (ret < 0)
+ size = ret;
+
+ return size;
};
static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
@@ -123,28 +239,61 @@ static const struct file_operations sof_msg_inject_fops = {
.owner = THIS_MODULE,
};
+static const struct file_operations sof_msg_inject_ipc4_fops = {
+ .open = sof_msg_inject_dfs_open,
+ .read = sof_msg_inject_ipc4_dfs_read,
+ .write = sof_msg_inject_ipc4_dfs_write,
+ .llseek = default_llseek,
+ .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);
+ static const struct file_operations *fops;
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->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- priv->rx_buffer = devm_kzalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ priv->ipc_type = sof_client_get_ipc_type(cdev);
+ priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
+ alloc_size = priv->max_msg_size;
+
+ if (priv->ipc_type == SOF_INTEL_IPC4)
+ alloc_size += sizeof(struct sof_ipc4_msg);
+
+ priv->tx_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL);
+ priv->rx_buffer = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
if (!priv->tx_buffer || !priv->rx_buffer)
return -ENOMEM;
+ if (priv->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_msg *ipc4_msg;
+
+ ipc4_msg = priv->tx_buffer;
+ ipc4_msg->data_ptr = priv->tx_buffer + sizeof(struct sof_ipc4_msg);
+
+ ipc4_msg = priv->rx_buffer;
+ ipc4_msg->data_ptr = priv->rx_buffer + sizeof(struct sof_ipc4_msg);
+
+ fops = &sof_msg_inject_ipc4_fops;
+ } else {
+ fops = &sof_msg_inject_fops;
+ }
+
cdev->data = priv;
priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root,
- cdev, &sof_msg_inject_fops);
+ cdev, fops);
/* enable runtime PM */
pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index 797dedb26163..34e6bd356e71 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -132,6 +132,7 @@ static int sof_probes_deinit(struct sof_client_dev *cdev)
static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
void **params, size_t *num_params)
{
+ size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
struct sof_ipc_probe_info_params msg = {{{0}}};
struct sof_ipc_probe_info_params *reply;
size_t bytes;
@@ -140,13 +141,13 @@ static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
*params = NULL;
*num_params = 0;
- reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ reply = kzalloc(max_msg_size, GFP_KERNEL);
if (!reply)
return -ENOMEM;
msg.rhdr.hdr.size = sizeof(msg);
msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
- ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE);
+ ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size);
if (ret < 0 || reply->rhdr.error < 0)
goto exit;
@@ -503,10 +504,9 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
if (!buf)
return -ENOMEM;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
- pm_runtime_put_noidle(dev);
goto exit;
}
@@ -568,10 +568,9 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
desc = (struct sof_probe_point_desc *)tkns;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
- pm_runtime_put_noidle(dev);
goto exit;
}
@@ -621,10 +620,9 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
goto exit;
}
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
- pm_runtime_put_noidle(dev);
goto exit;
}
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 686ad0c3bb61..16cca666bb85 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <sound/sof/ipc4/header.h>
#include "ops.h"
#include "sof-client.h"
#include "sof-priv.h"
@@ -72,6 +73,9 @@ static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
int ret = 0;
int i;
+ if (sdev->pdata->ipc_type != SOF_IPC)
+ return 0;
+
for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) {
ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0);
if (ret < 0)
@@ -245,10 +249,19 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
void *reply_data, size_t reply_bytes)
{
- struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+ if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
+ struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+
+ return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size,
+ reply_data, reply_bytes);
+ } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_msg *msg = ipc_msg;
- return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size,
- reply_data, reply_bytes);
+ return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size,
+ reply_data, reply_bytes);
+ }
+
+ return -EINVAL;
}
EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
@@ -319,6 +332,22 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev
}
EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT);
+size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return sdev->ipc->max_payload_size;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, SND_SOC_SOF_CLIENT);
+
+enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return sdev->pdata->ipc_type;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, SND_SOC_SOF_CLIENT);
+
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev)
{
@@ -342,9 +371,22 @@ EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT);
/* IPC event handling */
void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
{
- struct sof_ipc_cmd_hdr *hdr = msg_buf;
- u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
struct sof_ipc_event_entry *event;
+ u32 msg_type;
+
+ if (sdev->pdata->ipc_type == SOF_IPC) {
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+
+ msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
+ } else if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_msg *msg = msg_buf;
+
+ msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
+ } else {
+ dev_dbg_once(sdev->dev, "%s: Not supported IPC version: %d\n",
+ __func__, sdev->pdata->ipc_type);
+ return;
+ }
mutex_lock(&sdev->client_event_handler_mutex);
@@ -363,9 +405,21 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct sof_ipc_event_entry *event;
- if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK))
+ if (!callback)
return -EINVAL;
+ if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
+ if (!(ipc_msg_type & SOF_GLB_TYPE_MASK))
+ return -EINVAL;
+ } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK))
+ return -EINVAL;
+ } else {
+ dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n",
+ __func__, sdev->pdata->ipc_type);
+ return -EINVAL;
+ }
+
event = kmalloc(sizeof(*event), GFP_KERNEL);
if (!event)
return -ENOMEM;
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index 4b6394b4c694..46b215d9200f 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -41,6 +41,8 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev);
+size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev);
+enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev);
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev);
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index e3718638f9ce..53faeccedd4f 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -64,17 +64,17 @@ int sof_of_probe(struct platform_device *pdev)
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
- sof_pdata->fw_filename = desc->default_fw_filename;
+ sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC];
if (fw_path)
sof_pdata->fw_filename_prefix = fw_path;
else
- sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path;
+ sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path[SOF_IPC];
if (tplg_path)
sof_pdata->tplg_filename_prefix = tplg_path;
else
- sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path;
+ sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path[SOF_IPC];
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_of_probe_complete;
@@ -95,4 +95,10 @@ int sof_of_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL(sof_of_remove);
+void sof_of_shutdown(struct platform_device *pdev)
+{
+ snd_sof_device_shutdown(&pdev->dev);
+}
+EXPORT_SYMBOL(sof_of_shutdown);
+
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h
index 4e0f6588dad9..fd950a222ba4 100644
--- a/sound/soc/sof/sof-of-dev.h
+++ b/sound/soc/sof/sof-of-dev.h
@@ -13,5 +13,6 @@ extern const struct dev_pm_ops sof_of_pm;
int sof_of_probe(struct platform_device *pdev);
int sof_of_remove(struct platform_device *pdev);
+void sof_of_shutdown(struct platform_device *pdev);
#endif
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index 7fa2649e56e5..d627092b399d 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -12,6 +12,7 @@
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_data/x86/soc.h>
#include <linux/pm_runtime.h>
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
@@ -23,21 +24,34 @@ static char *fw_path;
module_param(fw_path, charp, 0444);
MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware.");
+static char *fw_filename;
+module_param(fw_filename, charp, 0444);
+MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware.");
+
static char *tplg_path;
module_param(tplg_path, charp, 0444);
MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
+static char *tplg_filename;
+module_param(tplg_filename, charp, 0444);
+MODULE_PARM_DESC(tplg_filename, "alternate filename for SOF topology.");
+
static int sof_pci_debug;
module_param_named(sof_pci_debug, sof_pci_debug, int, 0444);
MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)");
-static const char *sof_override_tplg_name;
+static int sof_pci_ipc_type = -1;
+module_param_named(ipc_type, sof_pci_ipc_type, int, 0444);
+MODULE_PARM_DESC(ipc_type, "SOF IPC type (0): SOF, (1) Intel CAVS");
+
+static const char *sof_dmi_override_tplg_name;
+static bool sof_dmi_use_community_key;
#define SOF_PCI_DISABLE_PM_RUNTIME BIT(0)
static int sof_tplg_cb(const struct dmi_system_id *id)
{
- sof_override_tplg_name = id->driver_data;
+ sof_dmi_override_tplg_name = id->driver_data;
return 1;
}
@@ -94,15 +108,35 @@ static const struct dmi_system_id sof_tplg_table[] = {
{}
};
+/* all Up boards use the community key */
+static int up_use_community_key(const struct dmi_system_id *id)
+{
+ sof_dmi_use_community_key = true;
+ return 1;
+}
+
+/*
+ * For ApolloLake Chromebooks we want to force the use of the Intel production key.
+ * All newer platforms use the community key
+ */
+static int chromebook_use_community_key(const struct dmi_system_id *id)
+{
+ if (!soc_intel_is_apl())
+ sof_dmi_use_community_key = true;
+ return 1;
+}
+
static const struct dmi_system_id community_key_platforms[] = {
{
.ident = "Up boards",
+ .callback = up_use_community_key,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
}
},
{
.ident = "Google Chromebooks",
+ .callback = chromebook_use_community_key,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
@@ -178,7 +212,36 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
sof_pdata->name = pci_name(pci);
sof_pdata->desc = desc;
sof_pdata->dev = dev;
- sof_pdata->fw_filename = desc->default_fw_filename;
+
+ sof_pdata->ipc_type = desc->ipc_default;
+
+ if (sof_pci_ipc_type < 0) {
+ sof_pdata->ipc_type = desc->ipc_default;
+ } else {
+ dev_info(dev, "overriding default IPC %d to requested %d\n",
+ desc->ipc_default, sof_pci_ipc_type);
+ if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) {
+ dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) {
+ dev_err(dev, "invalid request value %d, supported mask is %#x\n",
+ sof_pci_ipc_type, desc->ipc_supported_mask);
+ ret = -EINVAL;
+ goto out;
+ }
+ sof_pdata->ipc_type = sof_pci_ipc_type;
+ }
+
+ if (fw_filename) {
+ sof_pdata->fw_filename = fw_filename;
+
+ dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
+ sof_pdata->fw_filename);
+ } else {
+ sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type];
+ }
/*
* for platforms using the SOF community key, change the
@@ -195,10 +258,10 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
"Module parameter used, changed fw path to %s\n",
sof_pdata->fw_filename_prefix);
- } else if (dmi_check_system(community_key_platforms)) {
+ } else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) {
sof_pdata->fw_filename_prefix =
devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
- sof_pdata->desc->default_fw_path,
+ sof_pdata->desc->default_fw_path[sof_pdata->ipc_type],
"community");
dev_dbg(dev,
@@ -206,24 +269,37 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
sof_pdata->fw_filename_prefix);
} else {
sof_pdata->fw_filename_prefix =
- sof_pdata->desc->default_fw_path;
+ sof_pdata->desc->default_fw_path[sof_pdata->ipc_type];
}
if (tplg_path)
sof_pdata->tplg_filename_prefix = tplg_path;
else
sof_pdata->tplg_filename_prefix =
- sof_pdata->desc->default_tplg_path;
+ sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type];
- dmi_check_system(sof_tplg_table);
- if (sof_override_tplg_name)
- sof_pdata->tplg_filename = sof_override_tplg_name;
+ /*
+ * the topology filename will be provided in the machine descriptor, unless
+ * it is overridden by a module parameter or DMI quirk.
+ */
+ if (tplg_filename) {
+ sof_pdata->tplg_filename = tplg_filename;
+
+ dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n",
+ sof_pdata->tplg_filename);
+ } else {
+ dmi_check_system(sof_tplg_table);
+ if (sof_dmi_override_tplg_name)
+ sof_pdata->tplg_filename = sof_dmi_override_tplg_name;
+ }
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_pci_probe_complete;
/* call sof helper for DSP hardware probe */
ret = snd_sof_device_probe(dev, sof_pdata);
+
+out:
if (ret)
pci_release_regions(pci);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 0d9b640ae24c..9d7f53ff9c70 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -181,11 +181,6 @@ struct snd_sof_dsp_ops {
int (*load_firmware)(struct snd_sof_dev *sof_dev); /* mandatory */
int (*load_module)(struct snd_sof_dev *sof_dev,
struct snd_sof_mod_hdr *hdr); /* optional */
- /*
- * FW ready checks for ABI compatibility and creates
- * memory windows at first boot
- */
- int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* mandatory */
/* connect pcm substream to a host stream */
int (*pcm_open)(struct snd_sof_dev *sdev,
@@ -259,8 +254,9 @@ struct snd_sof_dsp_ops {
size_t size, const char *name,
enum sof_debugfs_access_type access_type); /* optional */
- /* host DMA trace initialization */
+ /* host DMA trace (IPC3) */
int (*trace_init)(struct snd_sof_dev *sdev,
+ struct snd_dma_buffer *dmatb,
struct sof_ipc_dma_trace_params_ext *dtrace_params); /* optional */
int (*trace_release)(struct snd_sof_dev *sdev); /* optional */
int (*trace_trigger)(struct snd_sof_dev *sdev,
@@ -349,18 +345,36 @@ struct snd_sof_mailbox {
/* IPC message descriptor for host <-> DSP IO */
struct snd_sof_ipc_msg {
/* message data */
- u32 header;
void *msg_data;
void *reply_data;
size_t msg_size;
size_t reply_size;
int reply_error;
+ /* notification, firmware initiated messages */
+ void *rx_data;
+
wait_queue_head_t waitq;
bool ipc_complete;
};
/**
+ * struct sof_ipc_fw_tracing_ops - IPC-specific firmware tracing ops
+ * @init: Function pointer for initialization of the tracing
+ * @free: Optional function pointer for freeing of the tracing
+ * @fw_crashed: Optional function pointer to notify the tracing of a firmware crash
+ * @suspend: Function pointer for system/runtime suspend
+ * @resume: Function pointer for system/runtime resume
+ */
+struct sof_ipc_fw_tracing_ops {
+ int (*init)(struct snd_sof_dev *sdev);
+ void (*free)(struct snd_sof_dev *sdev);
+ void (*fw_crashed)(struct snd_sof_dev *sdev);
+ void (*suspend)(struct snd_sof_dev *sdev, pm_message_t pm_state);
+ int (*resume)(struct snd_sof_dev *sdev);
+};
+
+/**
* struct sof_ipc_pm_ops - IPC-specific PM ops
* @ctx_save: Function pointer for context save
* @ctx_restore: Function pointer for context restore
@@ -370,6 +384,25 @@ struct sof_ipc_pm_ops {
int (*ctx_restore)(struct snd_sof_dev *sdev);
};
+/**
+ * struct sof_ipc_fw_loader_ops - IPC/FW-specific loader ops
+ * @validate: Function pointer for validating the firmware image
+ * @parse_ext_manifest: Function pointer for parsing the manifest of the firmware
+ * @load_fw_to_dsp: Optional function pointer for loading the firmware to the
+ * DSP.
+ * The function implements generic, hardware independent way
+ * of loading the initial firmware and its modules (if any).
+ * @query_fw_configuration: Optional function pointer to query information and
+ * configuration from the booted firmware.
+ * Executed after the first successful firmware boot.
+ */
+struct sof_ipc_fw_loader_ops {
+ int (*validate)(struct snd_sof_dev *sdev);
+ size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev);
+ int (*load_fw_to_dsp)(struct snd_sof_dev *sdev);
+ int (*query_fw_configuration)(struct snd_sof_dev *sdev);
+};
+
struct sof_ipc_tplg_ops;
struct sof_ipc_pcm_ops;
@@ -378,11 +411,36 @@ struct sof_ipc_pcm_ops;
* @tplg: Pointer to IPC-specific topology ops
* @pm: Pointer to PM ops
* @pcm: Pointer to PCM ops
+ * @fw_loader: Pointer to Firmware Loader ops
+ * @fw_tracing: Pointer to Firmware tracing ops
+ *
+ * @tx_msg: Function pointer for sending a 'short' IPC message
+ * @set_get_data: Function pointer for set/get data ('large' IPC message). This
+ * function may split up the 'large' message and use the @tx_msg
+ * path to transfer individual chunks, or use other means to transfer
+ * the message.
+ * @get_reply: Function pointer for fetching the reply to
+ * sdev->ipc->msg.reply_data
+ * @rx_msg: Function pointer for handling a received message
+ *
+ * Note: both @tx_msg and @set_get_data considered as TX functions and they are
+ * serialized for the duration of the instructed transfer. A large message sent
+ * via @set_get_data is a single transfer even if at the hardware level it is
+ * handled with multiple chunks.
*/
struct sof_ipc_ops {
const struct sof_ipc_tplg_ops *tplg;
const struct sof_ipc_pm_ops *pm;
const struct sof_ipc_pcm_ops *pcm;
+ const struct sof_ipc_fw_loader_ops *fw_loader;
+ const struct sof_ipc_fw_tracing_ops *fw_tracing;
+
+ int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes, bool no_pm);
+ int (*set_get_data)(struct snd_sof_dev *sdev, void *data, size_t data_bytes,
+ bool set);
+ int (*get_reply)(struct snd_sof_dev *sdev);
+ void (*rx_msg)(struct snd_sof_dev *sdev);
};
/* SOF generic IPC data */
@@ -394,6 +452,9 @@ struct snd_sof_ipc {
/* disables further sending of ipc's */
bool disable_ipc_tx;
+ /* Maximum allowed size of a single IPC message/reply */
+ size_t max_payload_size;
+
struct snd_sof_ipc_msg msg;
/* IPC ops based on version */
@@ -457,8 +518,6 @@ struct snd_sof_dev {
bool ipc_dump_printed;
/* firmware loader */
- struct snd_dma_buffer dmab;
- struct snd_dma_buffer dmab_bdl;
struct sof_ipc_fw_ready fw_ready;
struct sof_ipc_fw_version fw_version;
struct sof_ipc_cc_version *cc_version;
@@ -473,6 +532,7 @@ struct snd_sof_dev {
struct list_head route_list;
struct snd_soc_component *component;
u32 enabled_cores_mask; /* keep track of enabled cores */
+ bool led_present;
/* FW configuration */
struct sof_ipc_window *info_window;
@@ -481,16 +541,9 @@ struct snd_sof_dev {
int ipc_timeout;
int boot_timeout;
- /* DMA for Trace */
- struct snd_dma_buffer dmatb;
- struct snd_dma_buffer dmatp;
- int dma_trace_pages;
- wait_queue_head_t trace_sleep;
- u32 host_offset;
- bool dtrace_is_supported; /* set with Kconfig or module parameter */
- bool dtrace_is_enabled;
- bool dtrace_error;
- bool dtrace_draining;
+ /* firmwre tracing */
+ bool fw_trace_is_supported; /* set with Kconfig or module parameter */
+ void *fw_trace_data; /* private data used by firmware tracing implementation */
bool msi_enabled;
@@ -564,8 +617,6 @@ extern struct snd_compress_ops sof_compressed_ops;
int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev);
int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev);
int snd_sof_run_firmware(struct snd_sof_dev *sdev);
-int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
- struct snd_sof_mod_hdr *module);
void snd_sof_fw_unload(struct snd_sof_dev *sdev);
/*
@@ -575,15 +626,17 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev);
void snd_sof_ipc_free(struct snd_sof_dev *sdev);
void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev);
void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id);
-void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev);
-int snd_sof_ipc_valid(struct snd_sof_dev *sdev);
-int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
- void *msg_data, size_t msg_bytes, void *reply_data,
- size_t reply_bytes);
-int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
- void *msg_data, size_t msg_bytes,
+static inline void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
+{
+ sdev->ipc->ops->rx_msg(sdev);
+}
+int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes);
+int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes);
-int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev);
+int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+ size_t reply_bytes);
+
static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id)
{
snd_sof_ipc_get_reply(sdev);
@@ -593,27 +646,26 @@ static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_i
/*
* Trace/debug
*/
-int snd_sof_init_trace(struct snd_sof_dev *sdev);
-void snd_sof_release_trace(struct snd_sof_dev *sdev);
-void snd_sof_free_trace(struct snd_sof_dev *sdev);
int snd_sof_dbg_init(struct snd_sof_dev *sdev);
void snd_sof_free_debug(struct snd_sof_dev *sdev);
int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
void *base, size_t size,
const char *name, mode_t mode);
-int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
- struct sof_ipc_dma_trace_posn *posn);
-void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev);
void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
u32 panic_code, u32 tracep_code, void *oops,
struct sof_ipc_panic_info *panic_info,
void *stack, size_t stack_words);
-int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev);
void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev);
int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev);
int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev,
enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size,
const char *name, enum sof_debugfs_access_type access_type);
+/* Firmware tracing */
+int sof_fw_trace_init(struct snd_sof_dev *sdev);
+void sof_fw_trace_free(struct snd_sof_dev *sdev);
+void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev);
+void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state);
+int sof_fw_trace_resume(struct snd_sof_dev *sdev);
/*
* DSP Architectures.
@@ -654,8 +706,6 @@ int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
u32 offset, void *dest, size_t size);
-int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id);
-
int sof_ipc_msg_data(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
void *p, size_t sz);
@@ -721,4 +771,8 @@ static inline int sof_resume_clients(struct snd_sof_dev *sdev)
}
#endif /* CONFIG_SND_SOC_SOF_CLIENT */
+/* Main ops for IPC implementations */
+extern const struct sof_ipc_ops ipc3_ops;
+extern const struct sof_ipc_ops ipc4_ops;
+
#endif
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 3e5b319b44c7..b1fcab7ce48e 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -32,7 +32,6 @@
#define VOL_HALF_DB_STEP 50
/* TLV data items */
-#define TLV_ITEMS 3
#define TLV_MIN 0
#define TLV_STEP 1
#define TLV_MUTE 2
@@ -134,7 +133,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so
return 0;
}
-static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS])
+static inline int get_tlv_data(const int *p, int tlv[SOF_TLV_ITEMS])
{
/* we only support dB scale TLV type at the moment */
if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
@@ -224,7 +223,7 @@ static u32 vol_pow32(u32 a, int exp, u32 fwl)
* Function to calculate volume gain from TLV data.
* This function can only handle gain steps that are multiples of 0.5 dB
*/
-static u32 vol_compute_gain(u32 value, int *tlv)
+u32 vol_compute_gain(u32 value, int *tlv)
{
int dB_gain;
u32 linear_gain;
@@ -263,20 +262,17 @@ static u32 vol_compute_gain(u32 value, int *tlv)
* "size" specifies the number of entries in the table
*/
static int set_up_volume_table(struct snd_sof_control *scontrol,
- int tlv[TLV_ITEMS], int size)
+ int tlv[SOF_TLV_ITEMS], int size)
{
- int j;
-
- /* init the volume table */
- scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
- if (!scontrol->volume_table)
- return -ENOMEM;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* populate the volume table */
- for (j = 0; j < size ; j++)
- scontrol->volume_table[j] = vol_compute_gain(j, tlv);
+ if (tplg_ops->control->set_up_volume_table)
+ return tplg_ops->control->set_up_volume_table(scontrol, tlv, size);
- return 0;
+ dev_err(scomp->dev, "Mandatory op %s not set\n", __func__);
+ return -EINVAL;
}
struct sof_dai_types {
@@ -772,7 +768,8 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_mixer_control *mc =
container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
- int tlv[TLV_ITEMS];
+ int tlv[SOF_TLV_ITEMS];
+ unsigned int mask;
int ret;
/* validate topology data */
@@ -821,6 +818,16 @@ skip:
goto err;
}
+ if (scontrol->led_ctl.use_led) {
+ mask = scontrol->led_ctl.direction ? SNDRV_CTL_ELEM_ACCESS_MIC_LED :
+ SNDRV_CTL_ELEM_ACCESS_SPK_LED;
+ scontrol->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ scontrol->access |= mask;
+ kc->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ kc->access |= mask;
+ sdev->led_present = true;
+ }
+
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
scontrol->comp_id, scontrol->num_channels);
@@ -1001,15 +1008,18 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_dai *cpu_dai;
int i;
+ if (!w->sname) {
+ dev_err(scomp->dev, "Widget %s does not have stream\n", w->name);
+ return -EINVAL;
+ }
+
list_for_each_entry(rtd, &card->rtd_list, list) {
dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n",
w->name, w->sname, rtd->dai_link->stream_name);
- if (!w->sname || !rtd->dai_link->stream_name)
- continue;
-
/* does stream match DAI link ? */
- if (strcmp(w->sname, rtd->dai_link->stream_name))
+ if (!rtd->dai_link->stream_name ||
+ strcmp(w->sname, rtd->dai_link->stream_name))
continue;
switch (w->id) {
@@ -1140,7 +1150,6 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
struct snd_soc_tplg_private *private = &tw->priv;
int num_tuples = 0;
- size_t size;
int ret, i;
if (count > 0 && !object_token_list) {
@@ -1153,8 +1162,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
num_tuples += token_list[object_token_list[i]].count;
/* allocate memory for tuples array */
- size = sizeof(struct snd_sof_tuple) * num_tuples;
- swidget->tuples = kzalloc(size, GFP_KERNEL);
+ swidget->tuples = kcalloc(num_tuples, sizeof(*swidget->tuples), GFP_KERNEL);
if (!swidget->tuples)
return -ENOMEM;
@@ -1595,7 +1603,6 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
struct snd_soc_tplg_private *private = &cfg->priv;
struct snd_sof_dai_link *slink;
- size_t size;
u32 token_id = 0;
int num_tuples = 0;
int ret, num_sets;
@@ -1707,22 +1714,23 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
}
/* allocate memory for tuples array */
- size = sizeof(struct snd_sof_tuple) * num_tuples;
- slink->tuples = kzalloc(size, GFP_KERNEL);
+ slink->tuples = kcalloc(num_tuples, sizeof(*slink->tuples), GFP_KERNEL);
if (!slink->tuples) {
kfree(slink->hw_configs);
kfree(slink);
return -ENOMEM;
}
- /* parse one set of DAI link tokens */
- ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
- SOF_DAI_LINK_TOKENS, 1, slink->tuples,
- num_tuples, &slink->num_tuples);
- if (ret < 0) {
- dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
- token_list[SOF_DAI_LINK_TOKENS].name, link->name);
- goto err;
+ if (token_list[SOF_DAI_LINK_TOKENS].tokens) {
+ /* parse one set of DAI link tokens */
+ ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
+ SOF_DAI_LINK_TOKENS, 1, slink->tuples,
+ num_tuples, &slink->num_tuples);
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
+ token_list[SOF_DAI_LINK_TOKENS].name, link->name);
+ goto err;
+ }
}
/* nothing more to do if there are no DAI type-specific tokens defined */
@@ -2075,6 +2083,7 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct firmware *fw;
int ret;
@@ -2097,6 +2106,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
}
release_firmware(fw);
+
+ if (ret >= 0 && sdev->led_present)
+ ret = snd_ctl_led_request();
+
return ret;
}
EXPORT_SYMBOL(snd_sof_load_topology);
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index ea8e4506d02e..6f662642d611 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -1,601 +1,51 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
-//
-// This file is provided under a dual BSD/GPLv2 license. When using or
-// redistributing this file, you may do so under either license.
-//
-// Copyright(c) 2018 Intel Corporation. All rights reserved.
-//
-// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+// SPDX-License-Identifier: GPL-2.0-only
//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
-#include <linux/debugfs.h>
-#include <linux/sched/signal.h>
#include "sof-priv.h"
-#include "ops.h"
-#include "sof-utils.h"
-
-#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
-#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
-
-static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value,
- struct sof_ipc_trace_filter_elem *elem_list,
- int capacity, int *counter)
-{
- if (*counter >= capacity)
- return -ENOMEM;
-
- elem_list[*counter].key = key;
- elem_list[*counter].value = value;
- ++*counter;
-
- return 0;
-}
-
-static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
- struct sof_ipc_trace_filter_elem *elem,
- int capacity, int *counter)
-{
- int len = strlen(line);
- int cnt = *counter;
- uint32_t uuid_id;
- int log_level;
- int pipe_id;
- int comp_id;
- int read;
- int ret;
-
- /* ignore empty content */
- ret = sscanf(line, " %n", &read);
- if (!ret && read == len)
- return len;
-
- ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
- if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
- dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line);
- return -EINVAL;
- }
-
- if (uuid_id > 0) {
- ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
- uuid_id, elem, capacity, &cnt);
- if (ret)
- return ret;
- }
- if (pipe_id >= 0) {
- ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
- pipe_id, elem, capacity, &cnt);
- if (ret)
- return ret;
- }
- if (comp_id >= 0) {
- ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
- comp_id, elem, capacity, &cnt);
- if (ret)
- return ret;
- }
-
- ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
- SOF_IPC_TRACE_FILTER_ELEM_FIN,
- log_level, elem, capacity, &cnt);
- if (ret)
- return ret;
-
- /* update counter only when parsing whole entry passed */
- *counter = cnt;
-
- return len;
-}
-
-static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
- int *out_elem_cnt,
- struct sof_ipc_trace_filter_elem **out)
-{
- static const char entry_delimiter[] = ";";
- char *entry = string;
- int capacity = 0;
- int entry_len;
- int cnt = 0;
-
- /*
- * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
- * IPC elements, depending on content. Calculate IPC elements capacity
- * for the input string where each element is set.
- */
- while (entry) {
- capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
- entry = strchr(entry + 1, entry_delimiter[0]);
- }
- *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
- if (!*out)
- return -ENOMEM;
-
- /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
- while ((entry = strsep(&string, entry_delimiter))) {
- entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
- if (entry_len < 0) {
- dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry,
- entry_len);
- return -EINVAL;
- }
- }
-
- *out_elem_cnt = cnt;
-
- return 0;
-}
-
-static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
- struct sof_ipc_trace_filter_elem *elems)
-{
- struct sof_ipc_trace_filter *msg;
- struct sof_ipc_reply reply;
- size_t size;
- int ret;
-
- size = struct_size(msg, elems, num_elems);
- if (size > SOF_IPC_MSG_MAX_SIZE)
- return -EINVAL;
-
- msg = kmalloc(size, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- msg->hdr.size = size;
- msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
- msg->elem_cnt = num_elems;
- memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0 && ret != -EACCES) {
- pm_runtime_put_noidle(sdev->dev);
- dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
- goto error;
- }
- ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
- &reply, sizeof(reply));
- pm_runtime_mark_last_busy(sdev->dev);
- pm_runtime_put_autosuspend(sdev->dev);
-
-error:
- kfree(msg);
- return ret ? ret : reply.error;
-}
-
-static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from,
- size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct sof_ipc_trace_filter_elem *elems = NULL;
- struct snd_sof_dev *sdev = dfse->sdev;
- loff_t pos = 0;
- int num_elems;
- char *string;
- int ret;
-
- if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
- dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
- TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
- return -EINVAL;
- }
-
- string = kmalloc(count + 1, GFP_KERNEL);
- if (!string)
- return -ENOMEM;
-
- /* assert null termination */
- string[count] = 0;
- ret = simple_write_to_buffer(string, count, &pos, from, count);
- if (ret < 0)
- goto error;
-
- ret = trace_filter_parse(sdev, string, &num_elems, &elems);
- if (ret < 0) {
- dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret);
- goto error;
- }
-
- if (num_elems) {
- ret = sof_ipc_trace_update_filter(sdev, num_elems, elems);
- if (ret < 0) {
- dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret);
- goto error;
- }
- }
- ret = count;
-error:
- kfree(string);
- kfree(elems);
- return ret;
-}
-
-static const struct file_operations sof_dfs_trace_filter_fops = {
- .open = simple_open,
- .write = sof_dfsentry_trace_filter_write,
- .llseek = default_llseek,
-};
-
-static int trace_debugfs_filter_create(struct snd_sof_dev *sdev)
-{
- struct snd_sof_dfsentry *dfse;
-
- dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
- if (!dfse)
- return -ENOMEM;
-
- dfse->sdev = sdev;
- dfse->type = SOF_DFSENTRY_TYPE_BUF;
-
- debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
- &sof_dfs_trace_filter_fops);
- /* add to dfsentry list */
- list_add(&dfse->list, &sdev->dfsentry_list);
-
- return 0;
-}
-
-static size_t sof_trace_avail(struct snd_sof_dev *sdev,
- loff_t pos, size_t buffer_size)
-{
- loff_t host_offset = READ_ONCE(sdev->host_offset);
-
- /*
- * If host offset is less than local pos, it means write pointer of
- * host DMA buffer has been wrapped. We should output the trace data
- * at the end of host DMA buffer at first.
- */
- if (host_offset < pos)
- return buffer_size - pos;
-
- /* If there is available trace data now, it is unnecessary to wait. */
- if (host_offset > pos)
- return host_offset - pos;
-
- return 0;
-}
-
-static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
- loff_t pos, size_t buffer_size)
-{
- wait_queue_entry_t wait;
- size_t ret = sof_trace_avail(sdev, pos, buffer_size);
-
- /* data immediately available */
- if (ret)
- return ret;
-
- if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) {
- /*
- * tracing has ended and all traces have been
- * read by client, return EOF
- */
- sdev->dtrace_draining = false;
- return 0;
- }
-
- /* wait for available trace data from FW */
- init_waitqueue_entry(&wait, current);
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&sdev->trace_sleep, &wait);
-
- if (!signal_pending(current)) {
- /* set timeout to max value, no error code */
- schedule_timeout(MAX_SCHEDULE_TIMEOUT);
- }
- remove_wait_queue(&sdev->trace_sleep, &wait);
- return sof_trace_avail(sdev, pos, buffer_size);
-}
-
-static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct snd_sof_dev *sdev = dfse->sdev;
- unsigned long rem;
- loff_t lpos = *ppos;
- size_t avail, buffer_size = dfse->size;
- u64 lpos_64;
-
- /* make sure we know about any failures on the DSP side */
- sdev->dtrace_error = false;
-
- /* check pos and count */
- if (lpos < 0)
- return -EINVAL;
- if (!count)
- return 0;
-
- /* check for buffer wrap and count overflow */
- lpos_64 = lpos;
- lpos = do_div(lpos_64, buffer_size);
-
- /* get available count based on current host offset */
- avail = sof_wait_trace_avail(sdev, lpos, buffer_size);
- if (sdev->dtrace_error) {
- dev_err(sdev->dev, "error: trace IO error\n");
- return -EIO;
- }
-
- /* make sure count is <= avail */
- if (count > avail)
- count = avail;
-
- /*
- * make sure that all trace data is available for the CPU as the trace
- * data buffer might be allocated from non consistent memory.
- * Note: snd_dma_buffer_sync() is called for normal audio playback and
- * capture streams also.
- */
- snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU);
- /* copy available trace data to debugfs */
- rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
- if (rem)
- return -EFAULT;
-
- *ppos += count;
-
- /* move debugfs reading position */
- return count;
-}
-
-static int sof_dfsentry_trace_release(struct inode *inode, struct file *file)
-{
- struct snd_sof_dfsentry *dfse = inode->i_private;
- struct snd_sof_dev *sdev = dfse->sdev;
-
- /* avoid duplicate traces at next open */
- if (!sdev->dtrace_is_enabled)
- sdev->host_offset = 0;
-
- return 0;
-}
-
-static const struct file_operations sof_dfs_trace_fops = {
- .open = simple_open,
- .read = sof_dfsentry_trace_read,
- .llseek = default_llseek,
- .release = sof_dfsentry_trace_release,
-};
-
-static int trace_debugfs_create(struct snd_sof_dev *sdev)
-{
- struct snd_sof_dfsentry *dfse;
- int ret;
-
- if (!sdev)
- return -EINVAL;
-
- ret = trace_debugfs_filter_create(sdev);
- if (ret < 0)
- dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret);
-
- dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
- if (!dfse)
- return -ENOMEM;
-
- dfse->type = SOF_DFSENTRY_TYPE_BUF;
- dfse->buf = sdev->dmatb.area;
- dfse->size = sdev->dmatb.bytes;
- dfse->sdev = sdev;
-
- debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
- &sof_dfs_trace_fops);
-
- return 0;
-}
-
-int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
-{
- struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
- struct sof_ipc_fw_version *v = &ready->version;
- struct sof_ipc_dma_trace_params_ext params;
- struct sof_ipc_reply ipc_reply;
- int ret;
-
- if (!sdev->dtrace_is_supported)
- return 0;
-
- if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages)
- return -EINVAL;
-
- /* set IPC parameters */
- params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
- /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
- if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
- params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
- params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
- params.timestamp_ns = ktime_get(); /* in nanosecond */
- } else {
- params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
- params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
- }
- params.buffer.phy_addr = sdev->dmatp.addr;
- params.buffer.size = sdev->dmatb.bytes;
- params.buffer.pages = sdev->dma_trace_pages;
- params.stream_tag = 0;
-
- sdev->host_offset = 0;
- sdev->dtrace_draining = false;
-
- ret = snd_sof_dma_trace_init(sdev, &params);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: fail in snd_sof_dma_trace_init %d\n", ret);
- return ret;
- }
- dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag);
-
- /* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- params.hdr.cmd, &params, sizeof(params),
- &ipc_reply, sizeof(ipc_reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: can't set params for DMA for trace %d\n", ret);
- goto trace_release;
- }
-
- ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: snd_sof_dma_trace_trigger: start: %d\n", ret);
- goto trace_release;
- }
-
- sdev->dtrace_is_enabled = true;
-
- return 0;
-
-trace_release:
- snd_sof_dma_trace_release(sdev);
- return ret;
-}
-
-int snd_sof_init_trace(struct snd_sof_dev *sdev)
+int sof_fw_trace_init(struct snd_sof_dev *sdev)
{
- int ret;
+ if (!sdev->ipc->ops->fw_tracing) {
+ dev_info(sdev->dev, "Firmware tracing is not available\n");
+ sdev->fw_trace_is_supported = false;
- if (!sdev->dtrace_is_supported)
return 0;
-
- /* set false before start initialization */
- sdev->dtrace_is_enabled = false;
-
- /* allocate trace page table buffer */
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
- PAGE_SIZE, &sdev->dmatp);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: can't alloc page table for trace %d\n", ret);
- return ret;
- }
-
- /* allocate trace data buffer */
- ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
- DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
- &sdev->dmatb);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: can't alloc buffer for trace %d\n", ret);
- goto page_err;
- }
-
- /* create compressed page table for audio firmware */
- ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb,
- sdev->dmatp.area, sdev->dmatb.bytes);
- if (ret < 0)
- goto table_err;
-
- sdev->dma_trace_pages = ret;
- dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n",
- __func__, sdev->dma_trace_pages);
-
- if (sdev->first_boot) {
- ret = trace_debugfs_create(sdev);
- if (ret < 0)
- goto table_err;
}
- init_waitqueue_head(&sdev->trace_sleep);
-
- ret = snd_sof_init_trace_ipc(sdev);
- if (ret < 0)
- goto table_err;
-
- return 0;
-table_err:
- sdev->dma_trace_pages = 0;
- snd_dma_free_pages(&sdev->dmatb);
-page_err:
- snd_dma_free_pages(&sdev->dmatp);
- return ret;
+ return sdev->ipc->ops->fw_tracing->init(sdev);
}
-EXPORT_SYMBOL(snd_sof_init_trace);
-int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
- struct sof_ipc_dma_trace_posn *posn)
+void sof_fw_trace_free(struct snd_sof_dev *sdev)
{
- if (!sdev->dtrace_is_supported)
- return 0;
-
- if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) {
- sdev->host_offset = posn->host_offset;
- wake_up(&sdev->trace_sleep);
- }
-
- if (posn->overflow != 0)
- dev_err(sdev->dev,
- "error: DSP trace buffer overflow %u bytes. Total messages %d\n",
- posn->overflow, posn->messages);
+ if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing)
+ return;
- return 0;
+ if (sdev->ipc->ops->fw_tracing->free)
+ sdev->ipc->ops->fw_tracing->free(sdev);
}
-/* an error has occurred within the DSP that prevents further trace */
-void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev)
+void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev)
{
- if (!sdev->dtrace_is_supported)
+ if (!sdev->fw_trace_is_supported)
return;
- if (sdev->dtrace_is_enabled) {
- sdev->dtrace_error = true;
- wake_up(&sdev->trace_sleep);
- }
+ if (sdev->ipc->ops->fw_tracing->fw_crashed)
+ sdev->ipc->ops->fw_tracing->fw_crashed(sdev);
}
-EXPORT_SYMBOL(snd_sof_trace_notify_for_error);
-void snd_sof_release_trace(struct snd_sof_dev *sdev)
+void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
{
- struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
- struct sof_ipc_fw_version *v = &ready->version;
- struct sof_ipc_cmd_hdr hdr;
- struct sof_ipc_reply ipc_reply;
- int ret;
-
- if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled)
+ if (!sdev->fw_trace_is_supported)
return;
- ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
- if (ret < 0)
- dev_err(sdev->dev,
- "error: snd_sof_dma_trace_trigger: stop: %d\n", ret);
-
- /*
- * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from
- * ABI 3.20.0 onwards
- */
- if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) {
- hdr.size = sizeof(hdr);
- hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
-
- ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size,
- &ipc_reply, sizeof(ipc_reply));
- if (ret < 0)
- dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
- }
-
- ret = snd_sof_dma_trace_release(sdev);
- if (ret < 0)
- dev_err(sdev->dev,
- "error: fail in snd_sof_dma_trace_release %d\n", ret);
-
- sdev->dtrace_is_enabled = false;
- sdev->dtrace_draining = true;
- wake_up(&sdev->trace_sleep);
+ sdev->ipc->ops->fw_tracing->suspend(sdev, pm_state);
}
-EXPORT_SYMBOL(snd_sof_release_trace);
-void snd_sof_free_trace(struct snd_sof_dev *sdev)
+int sof_fw_trace_resume(struct snd_sof_dev *sdev)
{
- if (!sdev->dtrace_is_supported)
- return;
-
- snd_sof_release_trace(sdev);
+ if (!sdev->fw_trace_is_supported)
+ return 0;
- if (sdev->dma_trace_pages) {
- snd_dma_free_pages(&sdev->dmatb);
- snd_dma_free_pages(&sdev->dmatp);
- sdev->dma_trace_pages = 0;
- }
+ return sdev->ipc->ops->fw_tracing->resume(sdev);
}
-EXPORT_SYMBOL(snd_sof_free_trace);
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index cd454871d654..2482d9867357 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -85,6 +85,18 @@ config SND_SOC_TEGRA210_I2S
compatible devices.
Say Y or M if you want to add support for Tegra210 I2S module.
+config SND_SOC_TEGRA186_ASRC
+ tristate "Tegra186 ASRC module"
+ help
+ Config to enable the Asynchronous Sample Rate Converter (ASRC),
+ which converts the sampling frequency of the input signal from
+ one frequency to another. It can handle over a wide range of
+ sample rate ratios (freq_in/freq_out) from 1:24 to 24:1.
+ ASRC has two modes of operation. One where ratio can be programmed
+ in SW and the other where it gets information from ratio estimator
+ module.
+ Say Y or M if you want to add support for Tegra186 ASRC module.
+
config SND_SOC_TEGRA186_DSPK
tristate "Tegra186 DSPK module"
help
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index f19d56690a0d..70a498ddb2fa 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -11,6 +11,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o
snd-soc-tegra210-ahub-objs := tegra210_ahub.o
snd-soc-tegra210-dmic-objs := tegra210_dmic.o
snd-soc-tegra210-i2s-objs := tegra210_i2s.o
+snd-soc-tegra186-asrc-objs := tegra186_asrc.o
snd-soc-tegra186-dspk-objs := tegra186_dspk.o
snd-soc-tegra210-admaif-objs := tegra210_admaif.o
snd-soc-tegra210-mvc-objs := tegra210_mvc.o
@@ -29,6 +30,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
obj-$(CONFIG_SND_SOC_TEGRA210_DMIC) += snd-soc-tegra210-dmic.o
obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o
obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA186_ASRC) += snd-soc-tegra186-asrc.o
obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o
obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o
obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
new file mode 100644
index 000000000000..9f12faaa609d
--- /dev/null
+++ b/sound/soc/tegra/tegra186_asrc.c
@@ -0,0 +1,1046 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra186_asrc.c - Tegra186 ASRC driver
+//
+// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra186_asrc.h"
+#include "tegra_cif.h"
+
+#define ASRC_STREAM_SOURCE_SELECT(id) \
+ (TEGRA186_ASRC_CFG + ((id) * TEGRA186_ASRC_STREAM_STRIDE))
+
+#define ASRC_STREAM_REG(reg, id) ((reg) + ((id) * TEGRA186_ASRC_STREAM_STRIDE))
+
+#define ASRC_STREAM_REG_DEFAULTS(id) \
+ { ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id), \
+ (((id) + 1) << 4) }, \
+ { ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id), \
+ 0x1 }, \
+ { ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id), \
+ 0x0 }, \
+ { ASRC_STREAM_REG(TEGRA186_ASRC_MUTE_UNMUTE_DURATION, id), \
+ 0x400 }, \
+ { ASRC_STREAM_REG(TEGRA186_ASRC_RX_CIF_CTRL, id), \
+ 0x7500 }, \
+ { ASRC_STREAM_REG(TEGRA186_ASRC_TX_CIF_CTRL, id), \
+ 0x7500 }
+
+static const struct reg_default tegra186_asrc_reg_defaults[] = {
+ ASRC_STREAM_REG_DEFAULTS(0),
+ ASRC_STREAM_REG_DEFAULTS(1),
+ ASRC_STREAM_REG_DEFAULTS(2),
+ ASRC_STREAM_REG_DEFAULTS(3),
+ ASRC_STREAM_REG_DEFAULTS(4),
+ ASRC_STREAM_REG_DEFAULTS(5),
+
+ { TEGRA186_ASRC_GLOBAL_ENB, 0},
+ { TEGRA186_ASRC_GLOBAL_SOFT_RESET, 0},
+ { TEGRA186_ASRC_GLOBAL_CG, 0x1 },
+ { TEGRA186_ASRC_GLOBAL_CFG, 0x0 },
+ { TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, 0},
+ { TEGRA186_ASRC_GLOBAL_SCRATCH_CFG, 0x0c207980 },
+ { TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL, 0x00115500 },
+ { TEGRA186_ASRC_GLOBAL_INT_MASK, 0x0},
+ { TEGRA186_ASRC_GLOBAL_INT_SET, 0x0},
+ { TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x0},
+ { TEGRA186_ASRC_GLOBAL_APR_CTRL, 0x0},
+ { TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL, 0x0},
+ { TEGRA186_ASRC_GLOBAL_DISARM_APR, 0x0},
+ { TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL, 0x0},
+ { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS, 0x0},
+ { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL, 0x0},
+ { TEGRA186_ASRC_CYA, 0x0},
+};
+
+static void tegra186_asrc_lock_stream(struct tegra186_asrc *asrc,
+ unsigned int id)
+{
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_LOCK_STATUS,
+ id),
+ 1);
+}
+
+static int __maybe_unused tegra186_asrc_runtime_suspend(struct device *dev)
+{
+ struct tegra186_asrc *asrc = dev_get_drvdata(dev);
+
+ regcache_cache_only(asrc->regmap, true);
+ regcache_mark_dirty(asrc->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra186_asrc_runtime_resume(struct device *dev)
+{
+ struct tegra186_asrc *asrc = dev_get_drvdata(dev);
+ int id;
+
+ regcache_cache_only(asrc->regmap, false);
+
+ /*
+ * Below sequence is recommended after a runtime PM cycle.
+ * This otherwise leads to transfer failures. The cache
+ * sync is done after this to restore other settings.
+ */
+ regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR,
+ TEGRA186_ASRC_ARAM_START_ADDR);
+ regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_ENB,
+ TEGRA186_ASRC_GLOBAL_EN);
+
+ regcache_sync(asrc->regmap);
+
+ for (id = 0; id < TEGRA186_ASRC_STREAM_MAX; id++) {
+ if (asrc->lane[id].ratio_source !=
+ TEGRA186_ASRC_RATIO_SOURCE_SW)
+ continue;
+
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART,
+ id),
+ asrc->lane[id].int_part);
+
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART,
+ id),
+ asrc->lane[id].frac_part);
+
+ tegra186_asrc_lock_stream(asrc, id);
+ }
+
+ return 0;
+}
+
+static int tegra186_asrc_set_audio_cif(struct tegra186_asrc *asrc,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra_cif_conf cif_conf;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = TEGRA_ACIF_BITS_24;
+
+ tegra_set_cif(asrc->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra186_asrc_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+ int ret, id = dai->id;
+
+ /* Set input threshold */
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, dai->id),
+ asrc->lane[id].input_thresh);
+
+ ret = tegra186_asrc_set_audio_cif(asrc, params,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_CIF_CTRL, dai->id));
+ if (ret) {
+ dev_err(dev, "Can't set ASRC RX%d CIF: %d\n", dai->id, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int tegra186_asrc_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+ int ret, id = dai->id - 7;
+
+ /* Set output threshold */
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, id),
+ asrc->lane[id].output_thresh);
+
+ ret = tegra186_asrc_set_audio_cif(asrc, params,
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_CIF_CTRL, id));
+ if (ret) {
+ dev_err(dev, "Can't set ASRC TX%d CIF: %d\n", id, ret);
+ return ret;
+ }
+
+ /* Set ENABLE_HW_RATIO_COMP */
+ if (asrc->lane[id].hwcomp_disable) {
+ regmap_update_bits(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),
+ TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK,
+ TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE);
+ } else {
+ regmap_update_bits(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),
+ TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK,
+ TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE);
+
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_COMP, id),
+ TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE);
+ }
+
+ /* Set lock */
+ regmap_update_bits(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id),
+ 1, asrc->lane[id].ratio_source);
+
+ if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_SW) {
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id),
+ asrc->lane[id].int_part);
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id),
+ asrc->lane[id].frac_part);
+ tegra186_asrc_lock_stream(asrc, id);
+ }
+
+ return ret;
+}
+
+static int tegra186_asrc_get_ratio_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *asrc_private =
+ (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+ ucontrol->value.enumerated.item[0] = asrc->lane[id].ratio_source;
+
+ return 0;
+}
+
+static int tegra186_asrc_put_ratio_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *asrc_private =
+ (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+ bool change = false;
+
+ asrc->lane[id].ratio_source = ucontrol->value.enumerated.item[0];
+
+ regmap_update_bits_check(asrc->regmap, asrc_private->reg,
+ TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK,
+ asrc->lane[id].ratio_source,
+ &change);
+
+ return change ? 1 : 0;
+}
+
+static int tegra186_asrc_get_ratio_int(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+ regmap_read(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id),
+ &asrc->lane[id].int_part);
+
+ ucontrol->value.integer.value[0] = asrc->lane[id].int_part;
+
+ return 0;
+}
+
+static int tegra186_asrc_put_ratio_int(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+ bool change = false;
+
+ if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_ARAD) {
+ dev_err(cmpnt->dev,
+ "Lane %d ratio source is ARAD, invalid SW update\n",
+ id);
+ return -EINVAL;
+ }
+
+ asrc->lane[id].int_part = ucontrol->value.integer.value[0];
+
+ regmap_update_bits_check(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART,
+ id),
+ TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK,
+ asrc->lane[id].int_part, &change);
+
+ tegra186_asrc_lock_stream(asrc, id);
+
+ return change ? 1 : 0;
+}
+
+static int tegra186_asrc_get_ratio_frac(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mreg_control *asrc_private =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE;
+
+ regmap_read(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id),
+ &asrc->lane[id].frac_part);
+
+ ucontrol->value.integer.value[0] = asrc->lane[id].frac_part;
+
+ return 0;
+}
+
+static int tegra186_asrc_put_ratio_frac(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mreg_control *asrc_private =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE;
+ bool change = false;
+
+ if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_ARAD) {
+ dev_err(cmpnt->dev,
+ "Lane %d ratio source is ARAD, invalid SW update\n",
+ id);
+ return -EINVAL;
+ }
+
+ asrc->lane[id].frac_part = ucontrol->value.integer.value[0];
+
+ regmap_update_bits_check(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART,
+ id),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ asrc->lane[id].frac_part, &change);
+
+ tegra186_asrc_lock_stream(asrc, id);
+
+ return change ? 1 : 0;
+}
+
+static int tegra186_asrc_get_hwcomp_disable(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+ ucontrol->value.integer.value[0] = asrc->lane[id].hwcomp_disable;
+
+ return 0;
+}
+
+static int tegra186_asrc_put_hwcomp_disable(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+ int value = ucontrol->value.integer.value[0];
+
+ if (value == asrc->lane[id].hwcomp_disable)
+ return 0;
+
+ asrc->lane[id].hwcomp_disable = value;
+
+ return 1;
+}
+
+static int tegra186_asrc_get_input_threshold(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+ ucontrol->value.integer.value[0] = (asrc->lane[id].input_thresh & 0x3);
+
+ return 0;
+}
+
+static int tegra186_asrc_put_input_threshold(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+ int value = (asrc->lane[id].input_thresh & ~(0x3)) |
+ ucontrol->value.integer.value[0];
+
+ if (value == asrc->lane[id].input_thresh)
+ return 0;
+
+ asrc->lane[id].input_thresh = value;
+
+ return 1;
+}
+
+static int tegra186_asrc_get_output_threshold(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+
+ ucontrol->value.integer.value[0] = (asrc->lane[id].output_thresh & 0x3);
+
+ return 0;
+}
+
+static int tegra186_asrc_put_output_threshold(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *asrc_private =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE;
+ int value = (asrc->lane[id].output_thresh & ~(0x3)) |
+ ucontrol->value.integer.value[0];
+
+ if (value == asrc->lane[id].output_thresh)
+ return 0;
+
+ asrc->lane[id].output_thresh = value;
+
+ return 1;
+}
+
+static int tegra186_asrc_widget_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 tegra186_asrc *asrc = dev_get_drvdata(cmpnt->dev);
+ unsigned int id =
+ (w->reg - TEGRA186_ASRC_ENABLE) / TEGRA186_ASRC_STREAM_STRIDE;
+
+ regmap_write(asrc->regmap,
+ ASRC_STREAM_REG(TEGRA186_ASRC_SOFT_RESET, id),
+ 0x1);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tegra186_asrc_in_dai_ops = {
+ .hw_params = tegra186_asrc_in_hw_params,
+};
+
+static const struct snd_soc_dai_ops tegra186_asrc_out_dai_ops = {
+ .hw_params = tegra186_asrc_out_hw_params,
+};
+
+#define IN_DAI(id) \
+ { \
+ .name = "ASRC-RX-CIF"#id, \
+ .playback = { \
+ .stream_name = "RX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 12, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "RX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 12, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra186_asrc_in_dai_ops, \
+ }
+
+#define OUT_DAI(id) \
+ { \
+ .name = "ASRC-TX-CIF"#id, \
+ .playback = { \
+ .stream_name = "TX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 12, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "TX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 12, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra186_asrc_out_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra186_asrc_dais[] = {
+ /* ASRC Input */
+ IN_DAI(1),
+ IN_DAI(2),
+ IN_DAI(3),
+ IN_DAI(4),
+ IN_DAI(5),
+ IN_DAI(6),
+ IN_DAI(7),
+ /* ASRC Output */
+ OUT_DAI(1),
+ OUT_DAI(2),
+ OUT_DAI(3),
+ OUT_DAI(4),
+ OUT_DAI(5),
+ OUT_DAI(6),
+};
+
+static const struct snd_soc_dapm_widget tegra186_asrc_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT_E("TX1", NULL, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 0),
+ TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+ tegra186_asrc_widget_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("TX2", NULL, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 1),
+ TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+ tegra186_asrc_widget_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("TX3", NULL, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 2),
+ TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+ tegra186_asrc_widget_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("TX4", NULL, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 3),
+ TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+ tegra186_asrc_widget_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("TX5", NULL, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 4),
+ TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+ tegra186_asrc_widget_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("TX6", NULL, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 5),
+ TEGRA186_ASRC_STREAM_EN_SHIFT, 0,
+ tegra186_asrc_widget_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Depacketizer", NULL),
+};
+
+#define ASRC_STREAM_ROUTE(id, sname) \
+ { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \
+ { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname }, \
+ { "RX" #id, NULL, "RX" #id "-CIF-" sname }, \
+ { "TX" #id, NULL, "RX" #id }, \
+ { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
+ { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
+ { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname },
+
+#define ASRC_ROUTE(id) \
+ ASRC_STREAM_ROUTE(id, "Playback") \
+ ASRC_STREAM_ROUTE(id, "Capture")
+
+#define ASRC_RATIO_ROUTE(sname) \
+ { "RX7 XBAR-" sname, NULL, "RX7 XBAR-TX" }, \
+ { "RX7-CIF-" sname, NULL, "RX7 XBAR-" sname }, \
+ { "RX7", NULL, "RX7-CIF-" sname }, \
+ { "Depacketizer", NULL, "RX7" },
+
+static const struct snd_soc_dapm_route tegra186_asrc_routes[] = {
+ ASRC_ROUTE(1)
+ ASRC_ROUTE(2)
+ ASRC_ROUTE(3)
+ ASRC_ROUTE(4)
+ ASRC_ROUTE(5)
+ ASRC_ROUTE(6)
+ ASRC_RATIO_ROUTE("Playback")
+ ASRC_RATIO_ROUTE("Capture")
+};
+
+static const char * const tegra186_asrc_ratio_source_text[] = {
+ "ARAD",
+ "SW",
+};
+
+#define ASRC_SOURCE_DECL(name, id) \
+ static const struct soc_enum name = \
+ SOC_ENUM_SINGLE(ASRC_STREAM_SOURCE_SELECT(id), \
+ 0, 2, tegra186_asrc_ratio_source_text)
+
+ASRC_SOURCE_DECL(src_select1, 0);
+ASRC_SOURCE_DECL(src_select2, 1);
+ASRC_SOURCE_DECL(src_select3, 2);
+ASRC_SOURCE_DECL(src_select4, 3);
+ASRC_SOURCE_DECL(src_select5, 4);
+ASRC_SOURCE_DECL(src_select6, 5);
+
+#define SOC_SINGLE_EXT_FRAC(xname, xregbase, xmax, xget, xput) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = (xname), \
+ .info = snd_soc_info_xr_sx, \
+ .get = xget, \
+ .put = xput, \
+ \
+ .private_value = (unsigned long)&(struct soc_mreg_control) \
+ { \
+ .regbase = xregbase, \
+ .regcount = 1, \
+ .nbits = 32, \
+ .invert = 0, \
+ .min = 0, \
+ .max = xmax \
+ } \
+}
+
+static const struct snd_kcontrol_new tegra186_asrc_controls[] = {
+ /* Controls for integer part of ratio */
+ SOC_SINGLE_EXT("Ratio1 Integer Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 0),
+ 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+ tegra186_asrc_get_ratio_int,
+ tegra186_asrc_put_ratio_int),
+
+ SOC_SINGLE_EXT("Ratio2 Integer Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 1),
+ 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+ tegra186_asrc_get_ratio_int,
+ tegra186_asrc_put_ratio_int),
+
+ SOC_SINGLE_EXT("Ratio3 Integer Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 2),
+ 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+ tegra186_asrc_get_ratio_int,
+ tegra186_asrc_put_ratio_int),
+
+ SOC_SINGLE_EXT("Ratio4 Integer Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 3),
+ 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+ tegra186_asrc_get_ratio_int,
+ tegra186_asrc_put_ratio_int),
+
+ SOC_SINGLE_EXT("Ratio5 Integer Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 4),
+ 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+ tegra186_asrc_get_ratio_int,
+ tegra186_asrc_put_ratio_int),
+
+ SOC_SINGLE_EXT("Ratio6 Integer Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 5),
+ 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0,
+ tegra186_asrc_get_ratio_int,
+ tegra186_asrc_put_ratio_int),
+
+ /* Controls for fractional part of ratio */
+ SOC_SINGLE_EXT_FRAC("Ratio1 Fractional Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 0),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ tegra186_asrc_get_ratio_frac,
+ tegra186_asrc_put_ratio_frac),
+
+ SOC_SINGLE_EXT_FRAC("Ratio2 Fractional Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 1),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ tegra186_asrc_get_ratio_frac,
+ tegra186_asrc_put_ratio_frac),
+
+ SOC_SINGLE_EXT_FRAC("Ratio3 Fractional Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 2),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ tegra186_asrc_get_ratio_frac,
+ tegra186_asrc_put_ratio_frac),
+
+ SOC_SINGLE_EXT_FRAC("Ratio4 Fractional Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 3),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ tegra186_asrc_get_ratio_frac,
+ tegra186_asrc_put_ratio_frac),
+
+ SOC_SINGLE_EXT_FRAC("Ratio5 Fractional Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 4),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ tegra186_asrc_get_ratio_frac,
+ tegra186_asrc_put_ratio_frac),
+
+ SOC_SINGLE_EXT_FRAC("Ratio6 Fractional Part",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 5),
+ TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK,
+ tegra186_asrc_get_ratio_frac,
+ tegra186_asrc_put_ratio_frac),
+
+ /* Source of ratio provider */
+ SOC_ENUM_EXT("Ratio1 Source", src_select1,
+ tegra186_asrc_get_ratio_source,
+ tegra186_asrc_put_ratio_source),
+
+ SOC_ENUM_EXT("Ratio2 Source", src_select2,
+ tegra186_asrc_get_ratio_source,
+ tegra186_asrc_put_ratio_source),
+
+ SOC_ENUM_EXT("Ratio3 Source", src_select3,
+ tegra186_asrc_get_ratio_source,
+ tegra186_asrc_put_ratio_source),
+
+ SOC_ENUM_EXT("Ratio4 Source", src_select4,
+ tegra186_asrc_get_ratio_source,
+ tegra186_asrc_put_ratio_source),
+
+ SOC_ENUM_EXT("Ratio5 Source", src_select5,
+ tegra186_asrc_get_ratio_source,
+ tegra186_asrc_put_ratio_source),
+
+ SOC_ENUM_EXT("Ratio6 Source", src_select6,
+ tegra186_asrc_get_ratio_source,
+ tegra186_asrc_put_ratio_source),
+
+ /* Disable HW managed overflow/underflow issue at input and output */
+ SOC_SINGLE_EXT("Stream1 HW Component Disable",
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 0), 0, 1, 0,
+ tegra186_asrc_get_hwcomp_disable,
+ tegra186_asrc_put_hwcomp_disable),
+
+ SOC_SINGLE_EXT("Stream2 HW Component Disable",
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 1), 0, 1, 0,
+ tegra186_asrc_get_hwcomp_disable,
+ tegra186_asrc_put_hwcomp_disable),
+
+ SOC_SINGLE_EXT("Stream3 HW Component Disable",
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 2), 0, 1, 0,
+ tegra186_asrc_get_hwcomp_disable,
+ tegra186_asrc_put_hwcomp_disable),
+
+ SOC_SINGLE_EXT("Stream4 HW Component Disable",
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 3), 0, 1, 0,
+ tegra186_asrc_get_hwcomp_disable,
+ tegra186_asrc_put_hwcomp_disable),
+
+ SOC_SINGLE_EXT("Stream5 HW Component Disable",
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 4), 0, 1, 0,
+ tegra186_asrc_get_hwcomp_disable,
+ tegra186_asrc_put_hwcomp_disable),
+
+ SOC_SINGLE_EXT("Stream6 HW Component Disable",
+ ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 5), 0, 1, 0,
+ tegra186_asrc_get_hwcomp_disable,
+ tegra186_asrc_put_hwcomp_disable),
+
+ /* Input threshold for watermark fields */
+ SOC_SINGLE_EXT("Stream1 Input Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 0), 0, 3, 0,
+ tegra186_asrc_get_input_threshold,
+ tegra186_asrc_put_input_threshold),
+
+ SOC_SINGLE_EXT("Stream2 Input Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 1), 0, 3, 0,
+ tegra186_asrc_get_input_threshold,
+ tegra186_asrc_put_input_threshold),
+
+ SOC_SINGLE_EXT("Stream3 Input Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 2), 0, 3, 0,
+ tegra186_asrc_get_input_threshold,
+ tegra186_asrc_put_input_threshold),
+
+ SOC_SINGLE_EXT("Stream4 Input Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 3), 0, 3, 0,
+ tegra186_asrc_get_input_threshold,
+ tegra186_asrc_put_input_threshold),
+
+ SOC_SINGLE_EXT("Stream5 Input Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0,
+ tegra186_asrc_get_input_threshold,
+ tegra186_asrc_put_input_threshold),
+
+ SOC_SINGLE_EXT("Stream6 Input Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0,
+ tegra186_asrc_get_input_threshold,
+ tegra186_asrc_put_input_threshold),
+
+ /* Output threshold for watermark fields */
+ SOC_SINGLE_EXT("Stream1 Output Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 0), 0, 3, 0,
+ tegra186_asrc_get_output_threshold,
+ tegra186_asrc_put_output_threshold),
+
+ SOC_SINGLE_EXT("Stream2 Output Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 1), 0, 3, 0,
+ tegra186_asrc_get_output_threshold,
+ tegra186_asrc_put_output_threshold),
+
+ SOC_SINGLE_EXT("Stream3 Output Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 2), 0, 3, 0,
+ tegra186_asrc_get_output_threshold,
+ tegra186_asrc_put_output_threshold),
+
+ SOC_SINGLE_EXT("Stream4 Output Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 3), 0, 3, 0,
+ tegra186_asrc_get_output_threshold,
+ tegra186_asrc_put_output_threshold),
+
+ SOC_SINGLE_EXT("Stream5 Output Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 4), 0, 3, 0,
+ tegra186_asrc_get_output_threshold,
+ tegra186_asrc_put_output_threshold),
+
+ SOC_SINGLE_EXT("Stream6 Output Threshold",
+ ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 5), 0, 3, 0,
+ tegra186_asrc_get_output_threshold,
+ tegra186_asrc_put_output_threshold),
+};
+
+static const struct snd_soc_component_driver tegra186_asrc_cmpnt = {
+ .dapm_widgets = tegra186_asrc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra186_asrc_widgets),
+ .dapm_routes = tegra186_asrc_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra186_asrc_routes),
+ .controls = tegra186_asrc_controls,
+ .num_controls = ARRAY_SIZE(tegra186_asrc_controls),
+};
+
+static bool tegra186_asrc_wr_reg(struct device *dev, unsigned int reg)
+{
+ if (reg < TEGRA186_ASRC_STREAM_LIMIT)
+ reg %= TEGRA186_ASRC_STREAM_STRIDE;
+
+ switch (reg) {
+ case TEGRA186_ASRC_CFG ... TEGRA186_ASRC_RATIO_COMP:
+ case TEGRA186_ASRC_RX_CIF_CTRL:
+ case TEGRA186_ASRC_TX_CIF_CTRL:
+ case TEGRA186_ASRC_ENABLE:
+ case TEGRA186_ASRC_SOFT_RESET:
+ case TEGRA186_ASRC_GLOBAL_ENB ... TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL:
+ case TEGRA186_ASRC_GLOBAL_INT_MASK ... TEGRA186_ASRC_GLOBAL_INT_CLEAR:
+ case TEGRA186_ASRC_GLOBAL_APR_CTRL ... TEGRA186_ASRC_CYA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra186_asrc_rd_reg(struct device *dev, unsigned int reg)
+{
+ if (reg < TEGRA186_ASRC_STREAM_LIMIT)
+ reg %= TEGRA186_ASRC_STREAM_STRIDE;
+
+ if (tegra186_asrc_wr_reg(dev, reg))
+ return true;
+
+ switch (reg) {
+ case TEGRA186_ASRC_RX_STATUS:
+ case TEGRA186_ASRC_TX_STATUS:
+ case TEGRA186_ASRC_STATUS ... TEGRA186_ASRC_OUTSAMPLEBUF_CFG:
+ case TEGRA186_ASRC_RATIO_UPD_RX_STATUS:
+ case TEGRA186_ASRC_GLOBAL_STATUS ... TEGRA186_ASRC_GLOBAL_INT_STATUS:
+ case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra186_asrc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg < TEGRA186_ASRC_STREAM_LIMIT)
+ reg %= TEGRA186_ASRC_STREAM_STRIDE;
+
+ switch (reg) {
+ case TEGRA186_ASRC_RX_STATUS:
+ case TEGRA186_ASRC_TX_STATUS:
+ case TEGRA186_ASRC_SOFT_RESET:
+ case TEGRA186_ASRC_RATIO_INT_PART:
+ case TEGRA186_ASRC_RATIO_FRAC_PART:
+ case TEGRA186_ASRC_STATUS:
+ case TEGRA186_ASRC_RATIO_LOCK_STATUS:
+ case TEGRA186_ASRC_RATIO_UPD_RX_STATUS:
+ case TEGRA186_ASRC_GLOBAL_SOFT_RESET:
+ case TEGRA186_ASRC_GLOBAL_STATUS:
+ case TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS:
+ case TEGRA186_ASRC_GLOBAL_INT_STATUS:
+ case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tegra186_asrc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA186_ASRC_CYA,
+ .writeable_reg = tegra186_asrc_wr_reg,
+ .readable_reg = tegra186_asrc_rd_reg,
+ .volatile_reg = tegra186_asrc_volatile_reg,
+ .reg_defaults = tegra186_asrc_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra186_asrc_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra186_asrc_of_match[] = {
+ { .compatible = "nvidia,tegra186-asrc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra186_asrc_of_match);
+
+static int tegra186_asrc_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra186_asrc *asrc;
+ void __iomem *regs;
+ unsigned int i;
+ int err;
+
+ asrc = devm_kzalloc(dev, sizeof(*asrc), GFP_KERNEL);
+ if (!asrc)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, asrc);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ asrc->regmap = devm_regmap_init_mmio(dev, regs,
+ &tegra186_asrc_regmap_config);
+ if (IS_ERR(asrc->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(asrc->regmap);
+ }
+
+ regcache_cache_only(asrc->regmap, true);
+
+ regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_CFG,
+ TEGRA186_ASRC_GLOBAL_CFG_FRAC_32BIT_PRECISION);
+
+ /* Initialize default output srate */
+ for (i = 0; i < TEGRA186_ASRC_STREAM_MAX; i++) {
+ asrc->lane[i].ratio_source = TEGRA186_ASRC_RATIO_SOURCE_SW;
+ asrc->lane[i].int_part = 1;
+ asrc->lane[i].frac_part = 0;
+ asrc->lane[i].hwcomp_disable = 0;
+ asrc->lane[i].input_thresh =
+ TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CFG;
+ asrc->lane[i].output_thresh =
+ TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CFG;
+ }
+
+ err = devm_snd_soc_register_component(dev, &tegra186_asrc_cmpnt,
+ tegra186_asrc_dais,
+ ARRAY_SIZE(tegra186_asrc_dais));
+ if (err) {
+ dev_err(dev, "can't register ASRC component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int tegra186_asrc_platform_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+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)
+};
+
+static struct platform_driver tegra186_asrc_driver = {
+ .driver = {
+ .name = "tegra186-asrc",
+ .of_match_table = tegra186_asrc_of_match,
+ .pm = &tegra186_asrc_pm_ops,
+ },
+ .probe = tegra186_asrc_platform_probe,
+ .remove = tegra186_asrc_platform_remove,
+};
+module_platform_driver(tegra186_asrc_driver)
+
+MODULE_AUTHOR("Junghyun Kim <juskim@nvidia.com>");
+MODULE_DESCRIPTION("Tegra186 ASRC ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra186_asrc.h b/sound/soc/tegra/tegra186_asrc.h
new file mode 100644
index 000000000000..094fcc723c02
--- /dev/null
+++ b/sound/soc/tegra/tegra186_asrc.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra186_asrc.h - Definitions for Tegra186 ASRC driver
+ *
+ * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA186_ASRC_H__
+#define __TEGRA186_ASRC_H__
+
+/* ASRC stream related offset */
+#define TEGRA186_ASRC_CFG 0x0
+#define TEGRA186_ASRC_RATIO_INT_PART 0x4
+#define TEGRA186_ASRC_RATIO_FRAC_PART 0x8
+#define TEGRA186_ASRC_RATIO_LOCK_STATUS 0xc
+#define TEGRA186_ASRC_MUTE_UNMUTE_DURATION 0x10
+#define TEGRA186_ASRC_TX_THRESHOLD 0x14
+#define TEGRA186_ASRC_RX_THRESHOLD 0x18
+#define TEGRA186_ASRC_RATIO_COMP 0x1c
+#define TEGRA186_ASRC_RX_STATUS 0x20
+#define TEGRA186_ASRC_RX_CIF_CTRL 0x24
+#define TEGRA186_ASRC_TX_STATUS 0x2c
+#define TEGRA186_ASRC_TX_CIF_CTRL 0x30
+#define TEGRA186_ASRC_ENABLE 0x38
+#define TEGRA186_ASRC_SOFT_RESET 0x3c
+#define TEGRA186_ASRC_STATUS 0x4c
+#define TEGRA186_ASRC_STATEBUF_ADDR 0x5c
+#define TEGRA186_ASRC_STATEBUF_CFG 0x60
+#define TEGRA186_ASRC_INSAMPLEBUF_ADDR 0x64
+#define TEGRA186_ASRC_INSAMPLEBUF_CFG 0x68
+#define TEGRA186_ASRC_OUTSAMPLEBUF_ADDR 0x6c
+#define TEGRA186_ASRC_OUTSAMPLEBUF_CFG 0x70
+
+/* ASRC Global registers offset */
+#define TEGRA186_ASRC_GLOBAL_ENB 0x2f4
+#define TEGRA186_ASRC_GLOBAL_SOFT_RESET 0x2f8
+#define TEGRA186_ASRC_GLOBAL_CG 0x2fc
+#define TEGRA186_ASRC_GLOBAL_CFG 0x300
+#define TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR 0x304
+#define TEGRA186_ASRC_GLOBAL_SCRATCH_CFG 0x308
+#define TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL 0x30c
+#define TEGRA186_ASRC_RATIO_UPD_RX_STATUS 0x310
+#define TEGRA186_ASRC_GLOBAL_STATUS 0x314
+#define TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS 0x318
+#define TEGRA186_ASRC_GLOBAL_INT_STATUS 0x324
+#define TEGRA186_ASRC_GLOBAL_INT_MASK 0x328
+#define TEGRA186_ASRC_GLOBAL_INT_SET 0x32c
+#define TEGRA186_ASRC_GLOBAL_INT_CLEAR 0x330
+#define TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG 0x334
+#define TEGRA186_ASRC_GLOBAL_APR_CTRL 0x1000
+#define TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL 0x1004
+#define TEGRA186_ASRC_GLOBAL_DISARM_APR 0x1008
+#define TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL 0x100c
+#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS 0x1010
+#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL 0x1014
+#define TEGRA186_ASRC_CYA 0x1018
+
+#define TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE 0xaaaa
+#define TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CFG 0x00201002
+#define TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CFG 0x00201002
+
+#define TEGRA186_ASRC_GLOBAL_CFG_FRAC_28BIT_PRECISION 0
+#define TEGRA186_ASRC_GLOBAL_CFG_FRAC_32BIT_PRECISION 1
+
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT 31
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
+#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE (0 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT 0
+#define TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK (1 << TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_EN_SHIFT 0
+#define TEGRA186_ASRC_STREAM_EN (1 << TEGRA186_ASRC_STREAM_EN_SHIFT)
+#define TEGRA186_ASRC_GLOBAL_EN_SHIFT 0
+#define TEGRA186_ASRC_GLOBAL_EN (1 << TEGRA186_ASRC_GLOBAL_EN_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_SHIFT 0
+#define TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_MASK (0xffff << TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_SHIFT)
+#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_SHIFT 0
+#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_MASK (0xffff << TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_SHIFT)
+#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_SHIFT 0
+#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_MASK (0xffff << TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_SHIFT)
+
+#define TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK 0x1f
+#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK 0xffffffff
+
+#define TEGRA186_ASRC_STREAM_STRIDE 0x80
+#define TEGRA186_ASRC_STREAM_MAX 0x6
+#define TEGRA186_ASRC_STREAM_LIMIT 0x2f0
+
+#define TEGRA186_ASRC_RATIO_SOURCE_ARAD 0x0
+#define TEGRA186_ASRC_RATIO_SOURCE_SW 0x1
+
+#define TEGRA186_ASRC_ARAM_START_ADDR 0x3f800000
+
+struct tegra186_asrc_lane {
+ unsigned int int_part;
+ unsigned int frac_part;
+ unsigned int ratio_source;
+ unsigned int hwcomp_disable;
+ unsigned int input_thresh;
+ unsigned int output_thresh;
+};
+
+struct tegra186_asrc {
+ struct tegra186_asrc_lane lane[TEGRA186_ASRC_STREAM_MAX];
+ struct regmap *regmap;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index bccf8b8ca714..e1f90daea7a1 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -280,6 +280,20 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
DAI(MIXER1 TX3),
DAI(MIXER1 TX4),
DAI(MIXER1 TX5),
+ /* XBAR -> ASRC -> XBAR */
+ DAI(ASRC1 RX1),
+ DAI(ASRC1 TX1),
+ DAI(ASRC1 RX2),
+ DAI(ASRC1 TX2),
+ DAI(ASRC1 RX3),
+ DAI(ASRC1 TX3),
+ DAI(ASRC1 RX4),
+ DAI(ASRC1 TX4),
+ DAI(ASRC1 RX5),
+ DAI(ASRC1 TX5),
+ DAI(ASRC1 RX6),
+ DAI(ASRC1 TX6),
+ DAI(ASRC1 RX7),
};
static const char * const tegra210_ahub_mux_texts[] = {
@@ -388,6 +402,12 @@ static const char * const tegra186_ahub_mux_texts[] = {
"MIXER1 TX3",
"MIXER1 TX4",
"MIXER1 TX5",
+ "ASRC1 TX1",
+ "ASRC1 TX2",
+ "ASRC1 TX3",
+ "ASRC1 TX4",
+ "ASRC1 TX5",
+ "ASRC1 TX6",
};
static const unsigned int tegra210_ahub_mux_values[] = {
@@ -513,6 +533,13 @@ static const unsigned int tegra186_ahub_mux_values[] = {
MUX_VALUE(1, 2),
MUX_VALUE(1, 3),
MUX_VALUE(1, 4),
+ /* ASRC */
+ MUX_VALUE(3, 24),
+ MUX_VALUE(3, 25),
+ MUX_VALUE(3, 26),
+ MUX_VALUE(3, 27),
+ MUX_VALUE(3, 28),
+ MUX_VALUE(3, 29),
};
/* Controls for t210 */
@@ -623,6 +650,13 @@ MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26);
MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27);
MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28);
MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29);
+MUX_ENUM_CTRL_DECL_186(t186_asrc11_tx, 0x6c);
+MUX_ENUM_CTRL_DECL_186(t186_asrc12_tx, 0x6d);
+MUX_ENUM_CTRL_DECL_186(t186_asrc13_tx, 0x6e);
+MUX_ENUM_CTRL_DECL_186(t186_asrc14_tx, 0x6f);
+MUX_ENUM_CTRL_DECL_186(t186_asrc15_tx, 0x70);
+MUX_ENUM_CTRL_DECL_186(t186_asrc16_tx, 0x71);
+MUX_ENUM_CTRL_DECL_186(t186_asrc17_tx, 0x72);
/* Controls for t234 */
MUX_ENUM_CTRL_DECL_234(t234_mvc1_tx, 0x44);
@@ -651,6 +685,13 @@ MUX_ENUM_CTRL_DECL_234(t234_admaif17_tx, 0x60);
MUX_ENUM_CTRL_DECL_234(t234_admaif18_tx, 0x61);
MUX_ENUM_CTRL_DECL_234(t234_admaif19_tx, 0x62);
MUX_ENUM_CTRL_DECL_234(t234_admaif20_tx, 0x63);
+MUX_ENUM_CTRL_DECL_234(t234_asrc11_tx, 0x64);
+MUX_ENUM_CTRL_DECL_234(t234_asrc12_tx, 0x65);
+MUX_ENUM_CTRL_DECL_234(t234_asrc13_tx, 0x66);
+MUX_ENUM_CTRL_DECL_234(t234_asrc14_tx, 0x67);
+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
@@ -813,6 +854,19 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
TX_WIDGETS("MIXER1 TX3"),
TX_WIDGETS("MIXER1 TX4"),
TX_WIDGETS("MIXER1 TX5"),
+ WIDGETS("ASRC1 RX1", t186_asrc11_tx),
+ WIDGETS("ASRC1 RX2", t186_asrc12_tx),
+ WIDGETS("ASRC1 RX3", t186_asrc13_tx),
+ WIDGETS("ASRC1 RX4", t186_asrc14_tx),
+ WIDGETS("ASRC1 RX5", t186_asrc15_tx),
+ WIDGETS("ASRC1 RX6", t186_asrc16_tx),
+ WIDGETS("ASRC1 RX7", t186_asrc17_tx),
+ TX_WIDGETS("ASRC1 TX1"),
+ TX_WIDGETS("ASRC1 TX2"),
+ TX_WIDGETS("ASRC1 TX3"),
+ TX_WIDGETS("ASRC1 TX4"),
+ TX_WIDGETS("ASRC1 TX5"),
+ TX_WIDGETS("ASRC1 TX6"),
};
static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
@@ -909,6 +963,19 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
TX_WIDGETS("MIXER1 TX3"),
TX_WIDGETS("MIXER1 TX4"),
TX_WIDGETS("MIXER1 TX5"),
+ WIDGETS("ASRC1 RX1", t234_asrc11_tx),
+ WIDGETS("ASRC1 RX2", t234_asrc12_tx),
+ WIDGETS("ASRC1 RX3", t234_asrc13_tx),
+ WIDGETS("ASRC1 RX4", t234_asrc14_tx),
+ WIDGETS("ASRC1 RX5", t234_asrc15_tx),
+ WIDGETS("ASRC1 RX6", t234_asrc16_tx),
+ WIDGETS("ASRC1 RX7", t234_asrc17_tx),
+ TX_WIDGETS("ASRC1 TX1"),
+ TX_WIDGETS("ASRC1 TX2"),
+ TX_WIDGETS("ASRC1 TX3"),
+ TX_WIDGETS("ASRC1 TX4"),
+ TX_WIDGETS("ASRC1 TX5"),
+ TX_WIDGETS("ASRC1 TX6"),
};
#define TEGRA_COMMON_MUX_ROUTES(name) \
@@ -975,7 +1042,13 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
{ name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \
{ name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \
{ name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \
- { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" },
+ { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" }, \
+ { name " Mux", "ASRC1 TX1", "ASRC1 TX1 XBAR-RX" }, \
+ { name " Mux", "ASRC1 TX2", "ASRC1 TX2 XBAR-RX" }, \
+ { name " Mux", "ASRC1 TX3", "ASRC1 TX3 XBAR-RX" }, \
+ { name " Mux", "ASRC1 TX4", "ASRC1 TX4 XBAR-RX" }, \
+ { name " Mux", "ASRC1 TX5", "ASRC1 TX5 XBAR-RX" }, \
+ { name " Mux", "ASRC1 TX6", "ASRC1 TX6 XBAR-RX" },
#define TEGRA210_MUX_ROUTES(name) \
TEGRA_COMMON_MUX_ROUTES(name)
@@ -1135,6 +1208,13 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
TEGRA186_MUX_ROUTES("MIXER1 RX8")
TEGRA186_MUX_ROUTES("MIXER1 RX9")
TEGRA186_MUX_ROUTES("MIXER1 RX10")
+ TEGRA186_MUX_ROUTES("ASRC1 RX1")
+ TEGRA186_MUX_ROUTES("ASRC1 RX2")
+ TEGRA186_MUX_ROUTES("ASRC1 RX3")
+ TEGRA186_MUX_ROUTES("ASRC1 RX4")
+ TEGRA186_MUX_ROUTES("ASRC1 RX5")
+ TEGRA186_MUX_ROUTES("ASRC1 RX6")
+ TEGRA186_MUX_ROUTES("ASRC1 RX7")
};
static const struct snd_soc_component_driver tegra210_ahub_component = {
diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c
index a73404879aa1..78faa8bcae27 100644
--- a/sound/soc/tegra/tegra_asoc_machine.c
+++ b/sound/soc/tegra/tegra_asoc_machine.c
@@ -133,11 +133,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
else
jack_name = "Headphones Jack";
- err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_HEADPHONE,
- &tegra_machine_hp_jack,
- tegra_machine_hp_jack_pins,
- ARRAY_SIZE(tegra_machine_hp_jack_pins));
+ err = snd_soc_card_jack_new_pins(card, jack_name,
+ SND_JACK_HEADPHONE,
+ &tegra_machine_hp_jack,
+ tegra_machine_hp_jack_pins,
+ ARRAY_SIZE(tegra_machine_hp_jack_pins));
if (err) {
dev_err(rtd->dev,
"Headphones Jack creation failed: %d\n", err);
@@ -153,11 +153,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
}
if (machine->gpiod_hp_det && machine->asoc->add_headset_jack) {
- err = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADSET,
- &tegra_machine_headset_jack,
- tegra_machine_headset_jack_pins,
- ARRAY_SIZE(tegra_machine_headset_jack_pins));
+ err = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET,
+ &tegra_machine_headset_jack,
+ tegra_machine_headset_jack_pins,
+ ARRAY_SIZE(tegra_machine_headset_jack_pins));
if (err) {
dev_err(rtd->dev,
"Headset Jack creation failed: %d\n", err);
@@ -173,11 +173,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd)
}
if (machine->gpiod_mic_det && machine->asoc->add_mic_jack) {
- err = snd_soc_card_jack_new(rtd->card, "Mic Jack",
- SND_JACK_MICROPHONE,
- &tegra_machine_mic_jack,
- tegra_machine_mic_jack_pins,
- ARRAY_SIZE(tegra_machine_mic_jack_pins));
+ err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE,
+ &tegra_machine_mic_jack,
+ tegra_machine_mic_jack_pins,
+ ARRAY_SIZE(tegra_machine_mic_jack_pins));
if (err) {
dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
return err;
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 5751fb398c1a..b3cd0a34da63 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -79,11 +79,11 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = codec_dai->component;
int shrt = 0;
- err = snd_soc_card_jack_new(rtd->card, "Mic Jack",
- SND_JACK_MICROPHONE,
- machine->mic_jack,
- tegra_wm8903_mic_jack_pins,
- ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
+ err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE,
+ machine->mic_jack,
+ tegra_wm8903_mic_jack_pins,
+ ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
if (err) {
dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
return err;
diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c
index b1a32545babd..438e2fa843a0 100644
--- a/sound/soc/ti/ams-delta.c
+++ b/sound/soc/ti/ams-delta.c
@@ -471,8 +471,8 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
/* Add hook switch - can be used to control the codec from userspace
* even if line discipline fails */
- ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET,
- &ams_delta_hook_switch, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(card, "hook_switch", SND_JACK_HEADSET,
+ &ams_delta_hook_switch, NULL, 0);
if (ret)
dev_warn(card->dev,
"Failed to allocate resources for hook switch, "
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 2c146b91fca3..377be2e2b6ee 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -2047,6 +2047,8 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
return PCM_SDMA;
else if (strstr(tmp, "udmap"))
return PCM_UDMA;
+ else if (strstr(tmp, "bcdma"))
+ return PCM_UDMA;
return PCM_EDMA;
}
diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c
index 4077e15ec48b..6a969874c927 100644
--- a/sound/soc/ti/j721e-evm.c
+++ b/sound/soc/ti/j721e-evm.c
@@ -630,17 +630,18 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
codec_node = of_parse_phandle(node, "ti,cpb-codec", 0);
if (!codec_node) {
dev_err(priv->dev, "CPB codec node is not provided\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_dai_node;
}
domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB];
ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki");
if (ret)
- return ret;
+ goto put_codec_node;
ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk");
if (ret)
- return ret;
+ goto put_codec_node;
/*
* Common Processor Board, two links
@@ -650,8 +651,10 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
comp_count = 6;
compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
GFP_KERNEL);
- if (!compnent)
- return -ENOMEM;
+ if (!compnent) {
+ ret = -ENOMEM;
+ goto put_codec_node;
+ }
comp_idx = 0;
priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
@@ -702,6 +705,12 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
(*conf_idx)++;
return 0;
+
+put_codec_node:
+ of_node_put(codec_node);
+put_dai_node:
+ of_node_put(dai_node);
+ return ret;
}
static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
@@ -726,23 +735,25 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0);
if (!codeca_node) {
dev_err(priv->dev, "IVI codec-a node is not provided\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_dai_node;
}
codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0);
if (!codecb_node) {
dev_warn(priv->dev, "IVI codec-b node is not provided\n");
- return 0;
+ ret = 0;
+ goto put_codeca_node;
}
domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI];
ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki");
if (ret)
- return ret;
+ goto put_codecb_node;
ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk");
if (ret)
- return ret;
+ goto put_codecb_node;
/*
* IVI extension, two links
@@ -754,8 +765,10 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
comp_count = 8;
compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
GFP_KERNEL);
- if (!compnent)
- return -ENOMEM;
+ if (!compnent) {
+ ret = -ENOMEM;
+ goto put_codecb_node;
+ }
comp_idx = 0;
priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
@@ -816,6 +829,15 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
(*conf_idx)++;
return 0;
+
+
+put_codecb_node:
+ of_node_put(codecb_node);
+put_codeca_node:
+ of_node_put(codeca_node);
+put_dai_node:
+ of_node_put(dai_node);
+ return ret;
}
static int j721e_soc_probe(struct platform_device *pdev)
diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c
index da809c7f25a4..805ffbf89014 100644
--- a/sound/soc/ti/omap-abe-twl6040.c
+++ b/sound/soc/ti/omap-abe-twl6040.c
@@ -182,10 +182,10 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
/* Headset jack detection only if it is supported */
if (priv->jack_detection) {
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET, &hs_jack,
- hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret)
return ret;
diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c
index 1da05a6cdc9f..950eec44503b 100644
--- a/sound/soc/ti/omap-twl4030.c
+++ b/sound/soc/ti/omap-twl4030.c
@@ -155,10 +155,10 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
if (priv->jack_detect > 0) {
hs_jack_gpios[0].gpio = priv->jack_detect;
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET, &priv->hs_jack,
- hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET,
+ &priv->hs_jack, hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret)
return ret;
diff --git a/sound/soc/ti/osk5912.c b/sound/soc/ti/osk5912.c
index 40e29dda7e7a..2790c8915f55 100644
--- a/sound/soc/ti/osk5912.c
+++ b/sound/soc/ti/osk5912.c
@@ -27,12 +27,12 @@ static struct clk *tlv320aic23_mclk;
static int osk_startup(struct snd_pcm_substream *substream)
{
- return clk_enable(tlv320aic23_mclk);
+ return clk_prepare_enable(tlv320aic23_mclk);
}
static void osk_shutdown(struct snd_pcm_substream *substream)
{
- clk_disable(tlv320aic23_mclk);
+ clk_disable_unprepare(tlv320aic23_mclk);
}
static int osk_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c
index a2629ccc1dc8..322c398d209b 100644
--- a/sound/soc/ti/rx51.c
+++ b/sound/soc/ti/rx51.c
@@ -277,7 +277,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
/* AV jack detection */
err = snd_soc_card_jack_new(rtd->card, "AV Jack",
SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
- &rx51_av_jack, NULL, 0);
+ &rx51_av_jack);
if (err) {
dev_err(card->dev, "Failed to add AV Jack\n");
return err;
diff --git a/sound/soc/uniphier/aio-compress.c b/sound/soc/uniphier/aio-compress.c
index 0f76bc601ca9..7d1492c15b57 100644
--- a/sound/soc/uniphier/aio-compress.c
+++ b/sound/soc/uniphier/aio-compress.c
@@ -139,7 +139,6 @@ static int uniphier_aio_compr_set_params(struct snd_soc_component *component,
struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
- int ret;
if (params->codec.id != SND_AUDIOCODEC_IEC61937) {
dev_err(dev, "Codec ID is not supported(%d)\n",
@@ -161,11 +160,7 @@ static int uniphier_aio_compr_set_params(struct snd_soc_component *component,
aio_port_reset(sub);
aio_src_reset(sub);
- ret = uniphier_aio_compr_prepare(component, cstream);
- if (ret)
- return ret;
-
- return 0;
+ return uniphier_aio_compr_prepare(component, cstream);
}
static int uniphier_aio_compr_hw_free(struct snd_soc_component *component,
diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c
index 3e654e708f78..1ea1729984a9 100644
--- a/sound/soc/ux500/mop500_ab8500.c
+++ b/sound/soc/ux500/mop500_ab8500.c
@@ -433,12 +433,9 @@ void mop500_ab8500_remove(struct snd_soc_card *card)
{
struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card);
- if (drvdata->clk_ptr_sysclk != NULL)
- clk_put(drvdata->clk_ptr_sysclk);
- if (drvdata->clk_ptr_ulpclk != NULL)
- clk_put(drvdata->clk_ptr_ulpclk);
- if (drvdata->clk_ptr_intclk != NULL)
- clk_put(drvdata->clk_ptr_intclk);
+ clk_put(drvdata->clk_ptr_sysclk);
+ clk_put(drvdata->clk_ptr_ulpclk);
+ clk_put(drvdata->clk_ptr_intclk);
snd_soc_card_set_drvdata(card, drvdata);
}
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 376962291c4d..0fff96a5d3ab 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -635,6 +635,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
INIT_LIST_HEAD(&chip->pcm_list);
INIT_LIST_HEAD(&chip->ep_list);
INIT_LIST_HEAD(&chip->iface_ref_list);
+ INIT_LIST_HEAD(&chip->clock_ref_list);
INIT_LIST_HEAD(&chip->midi_list);
INIT_LIST_HEAD(&chip->mixer_list);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 87f042d06ce0..ca75f2206170 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -44,6 +44,7 @@ struct audioformat {
struct snd_usb_substream;
struct snd_usb_iface_ref;
+struct snd_usb_clock_ref;
struct snd_usb_endpoint;
struct snd_usb_power_domain;
@@ -62,6 +63,7 @@ struct snd_urb_ctx {
struct snd_usb_endpoint {
struct snd_usb_audio *chip;
struct snd_usb_iface_ref *iface_ref;
+ struct snd_usb_clock_ref *clock_ref;
int opened; /* open refcount; protect with chip->mutex */
atomic_t running; /* running status */
@@ -138,7 +140,6 @@ struct snd_usb_endpoint {
unsigned int cur_period_frames;
unsigned int cur_period_bytes;
unsigned int cur_buffer_periods;
- unsigned char cur_clock;
spinlock_t lock;
struct list_head list;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 4dfe76416794..3c435d379306 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -572,6 +572,13 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip,
/* continue processing */
}
+ /* FIXME - TEAC devices require the immediate interface setup */
+ if (rate != prev_rate && USB_ID_VENDOR(chip->usb_id) == 0x0644) {
+ usb_set_interface(chip->dev, fmt->iface, fmt->altsetting);
+ if (chip->quirk_flags & QUIRK_FLAG_IFACE_DELAY)
+ msleep(50);
+ }
+
validation:
/* validate clock after rate change */
if (!uac_clock_source_is_valid(chip, fmt, clock))
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 743b8287cfcd..f9c921683948 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -35,6 +35,14 @@ struct snd_usb_iface_ref {
struct list_head list;
};
+/* clock refcounting */
+struct snd_usb_clock_ref {
+ unsigned char clock;
+ atomic_t locked;
+ int rate;
+ struct list_head list;
+};
+
/*
* snd_usb_endpoint is a model that abstracts everything related to an
* USB endpoint and its streaming.
@@ -591,6 +599,25 @@ iface_ref_find(struct snd_usb_audio *chip, int iface)
return ip;
}
+/* Similarly, a refcount object for clock */
+static struct snd_usb_clock_ref *
+clock_ref_find(struct snd_usb_audio *chip, int clock)
+{
+ struct snd_usb_clock_ref *ref;
+
+ list_for_each_entry(ref, &chip->clock_ref_list, list)
+ if (ref->clock == clock)
+ return ref;
+
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return NULL;
+ ref->clock = clock;
+ atomic_set(&ref->locked, 0);
+ list_add_tail(&ref->list, &chip->clock_ref_list);
+ return ref;
+}
+
/*
* Get the existing endpoint object corresponding EP
* Returns NULL if not present.
@@ -768,6 +795,14 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
goto unlock;
}
+ if (fp->protocol != UAC_VERSION_1) {
+ ep->clock_ref = clock_ref_find(chip, fp->clock);
+ if (!ep->clock_ref) {
+ ep = NULL;
+ goto unlock;
+ }
+ }
+
ep->cur_audiofmt = fp;
ep->cur_channels = fp->channels;
ep->cur_rate = params_rate(params);
@@ -777,7 +812,6 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->cur_period_frames = params_period_size(params);
ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes;
ep->cur_buffer_periods = params_periods(params);
- ep->cur_clock = fp->clock;
if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
endpoint_set_syncinterval(chip, ep);
@@ -894,8 +928,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
ep->altsetting = 0;
ep->cur_audiofmt = NULL;
ep->cur_rate = 0;
- ep->cur_clock = 0;
ep->iface_ref = NULL;
+ ep->clock_ref = NULL;
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
}
mutex_unlock(&chip->mutex);
@@ -907,6 +941,8 @@ void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep)
ep->need_setup = true;
if (ep->iface_ref)
ep->iface_ref->need_setup = true;
+ if (ep->clock_ref)
+ ep->clock_ref->rate = 0;
}
/*
@@ -1314,6 +1350,33 @@ static int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
return 0;
}
+static int init_sample_rate(struct snd_usb_audio *chip,
+ struct snd_usb_endpoint *ep)
+{
+ struct snd_usb_clock_ref *clock = ep->clock_ref;
+ int err;
+
+ if (clock) {
+ if (atomic_read(&clock->locked))
+ return 0;
+ if (clock->rate == ep->cur_rate)
+ return 0;
+ if (clock->rate && clock->rate != ep->cur_rate) {
+ usb_audio_dbg(chip, "Mismatched sample rate %d vs %d for EP 0x%x\n",
+ clock->rate, ep->cur_rate, ep->ep_num);
+ return -EINVAL;
+ }
+ }
+
+ err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, ep->cur_rate);
+ if (err < 0)
+ return err;
+
+ if (clock)
+ clock->rate = ep->cur_rate;
+ return 0;
+}
+
/*
* snd_usb_endpoint_configure: Configure the endpoint
*
@@ -1343,8 +1406,7 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
* to update at each EP configuration
*/
if (ep->cur_audiofmt->protocol == UAC_VERSION_1) {
- err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt,
- ep->cur_rate);
+ err = init_sample_rate(chip, ep);
if (err < 0)
goto unlock;
}
@@ -1374,7 +1436,7 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
if (err < 0)
goto unlock;
- err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, ep->cur_rate);
+ err = init_sample_rate(chip, ep);
if (err < 0)
goto unlock;
@@ -1407,15 +1469,15 @@ unlock:
/* get the current rate set to the given clock by any endpoint */
int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
{
- struct snd_usb_endpoint *ep;
+ struct snd_usb_clock_ref *ref;
int rate = 0;
if (!clock)
return 0;
mutex_lock(&chip->mutex);
- list_for_each_entry(ep, &chip->ep_list, list) {
- if (ep->cur_clock == clock && ep->cur_rate) {
- rate = ep->cur_rate;
+ list_for_each_entry(ref, &chip->clock_ref_list, list) {
+ if (ref->clock == clock) {
+ rate = ref->rate;
break;
}
}
@@ -1456,6 +1518,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
if (atomic_inc_return(&ep->running) != 1)
return 0;
+ if (ep->clock_ref)
+ atomic_inc(&ep->clock_ref->locked);
+
ep->active_mask = 0;
ep->unlink_mask = 0;
ep->phase = 0;
@@ -1565,6 +1630,9 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending)
if (ep->sync_source)
WRITE_ONCE(ep->sync_source->sync_sink, NULL);
stop_urbs(ep, false, keep_pending);
+ if (ep->clock_ref)
+ if (!atomic_dec_return(&ep->clock_ref->locked))
+ ep->clock_ref->rate = 0;
}
}
@@ -1591,12 +1659,16 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip)
{
struct snd_usb_endpoint *ep, *en;
struct snd_usb_iface_ref *ip, *in;
+ struct snd_usb_clock_ref *cp, *cn;
list_for_each_entry_safe(ep, en, &chip->ep_list, list)
kfree(ep);
list_for_each_entry_safe(ip, in, &chip->iface_ref_list, list)
kfree(ip);
+
+ list_for_each_entry_safe(cp, cn, &chip->clock_ref_list, list)
+ kfree(cp);
}
/*
diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c
index 2d444ec74202..e1bf1b5da423 100644
--- a/sound/usb/implicit.c
+++ b/sound/usb/implicit.c
@@ -45,11 +45,6 @@ struct snd_usb_implicit_fb_match {
/* Implicit feedback quirk table for playback */
static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
- /* Generic matching */
- IMPLICIT_FB_GENERIC_DEV(0x0499, 0x1509), /* Steinberg UR22 */
- IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2030), /* M-Audio Fast Track C400 */
- IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2031), /* M-Audio Fast Track C600 */
-
/* Fixed EP */
/* FIXME: check the availability of generic matching */
IMPLICIT_FB_FIXED_DEV(0x0763, 0x2080, 0x81, 2), /* M-Audio FastTrack Ultra */
@@ -350,7 +345,8 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
}
/* Try the generic implicit fb if available */
- if (chip->generic_implicit_fb)
+ if (chip->generic_implicit_fb ||
+ (chip->quirk_flags & QUIRK_FLAG_GENERIC_IMPLICIT_FB))
return add_generic_implicit_fb(chip, fmt, alts);
/* No quirk */
@@ -387,6 +383,8 @@ int snd_usb_parse_implicit_fb_quirk(struct snd_usb_audio *chip,
struct audioformat *fmt,
struct usb_host_interface *alts)
{
+ if (chip->quirk_flags & QUIRK_FLAG_SKIP_IMPLICIT_FB)
+ return 0;
if (fmt->endpoint & USB_DIR_IN)
return audioformat_capture_quirk(chip, fmt, alts);
else
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 6d699065e81a..b470404a5376 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -439,16 +439,21 @@ static int configure_endpoints(struct snd_usb_audio *chip,
/* stop any running stream beforehand */
if (stop_endpoints(subs, false))
sync_pending_stops(subs);
+ if (subs->sync_endpoint) {
+ err = snd_usb_endpoint_configure(chip, subs->sync_endpoint);
+ if (err < 0)
+ return err;
+ }
err = snd_usb_endpoint_configure(chip, subs->data_endpoint);
if (err < 0)
return err;
snd_usb_set_format_quirk(subs, subs->cur_audiofmt);
- }
-
- if (subs->sync_endpoint) {
- err = snd_usb_endpoint_configure(chip, subs->sync_endpoint);
- if (err < 0)
- return err;
+ } else {
+ if (subs->sync_endpoint) {
+ err = snd_usb_endpoint_configure(chip, subs->sync_endpoint);
+ if (err < 0)
+ return err;
+ }
}
return 0;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 40a5e3eb4ef2..78eb41b621d6 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2672,6 +2672,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.altset_idx = 1,
.attributes = 0,
.endpoint = 0x82,
+ .ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC,
.datainterval = 1,
.maxpacksize = 0x0126,
@@ -2875,6 +2876,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.altset_idx = 1,
.attributes = 0x4,
.endpoint = 0x81,
+ .ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_ASYNC,
.maxpacksize = 0x130,
@@ -3391,6 +3393,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.altset_idx = 1,
.attributes = 0,
.endpoint = 0x03,
+ .ep_idx = 1,
.rates = SNDRV_PCM_RATE_96000,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_ASYNC,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fbbe59054c3f..e8468f9b007d 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1793,6 +1793,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x046d, 0x09a4, /* Logitech QuickCam E 3500 */
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
+ DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x04d8, 0xfeea, /* Benchmark DAC1 Pre */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x04e8, 0xa051, /* Samsung USBC Headset (AKG) */
@@ -1826,6 +1828,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x074d, 0x3553, /* Outlaw RR2150 (Micronas UAC3553B) */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x0763, 0x2030, /* M-Audio Fast Track C400 */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index b8359a0aa008..ffbb4b0d09a0 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -45,6 +45,7 @@ struct snd_usb_audio {
struct list_head pcm_list; /* list of pcm streams */
struct list_head ep_list; /* list of audio-related endpoints */
struct list_head iface_ref_list; /* list of interface refcounts */
+ struct list_head clock_ref_list; /* list of clock refcounts */
int pcm_devs;
struct list_head midi_list; /* list of midi interfaces */
@@ -164,6 +165,10 @@ extern bool snd_usb_skip_validation;
* Support generic DSD raw U32_BE format
* QUIRK_FLAG_SET_IFACE_FIRST:
* Set up the interface at first like UAC1
+ * QUIRK_FLAG_GENERIC_IMPLICIT_FB
+ * Apply the generic implicit feedback sync mode (same as implicit_fb=1 option)
+ * QUIRK_FLAG_SKIP_IMPLICIT_FB
+ * Don't apply implicit feedback sync mode
*/
#define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0)
@@ -183,5 +188,7 @@ extern bool snd_usb_skip_validation;
#define QUIRK_FLAG_IGNORE_CTL_ERROR (1U << 14)
#define QUIRK_FLAG_DSD_RAW (1U << 15)
#define QUIRK_FLAG_SET_IFACE_FIRST (1U << 16)
+#define QUIRK_FLAG_GENERIC_IMPLICIT_FB (1U << 17)
+#define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18)
#endif /* __USBAUDIO_H */