From 0530e2ff4969ef30da71126003ff821305040d01 Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Fri, 9 Dec 2016 21:34:37 -0800 Subject: [PATCH] ASoC: msm: add audio drivers for MSM targets Add snapshot of audio drivers for MSM targets from msm-4.4 kernel at the below commit level - commit c03f0ace8a36 ("msm: ipa: enable suspend pipe for ODU") (Kernel TIP as on 12-06-2016) Change-Id: I7f3e057f9ec041ca8032a013a5e0c008536ec995 Signed-off-by: Banajit Goswami Signed-off-by: Xiaoyu Ye --- .../bindings/sound/qcom-audio-dev.txt | 2324 +++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/qcom/Kconfig | 20 + drivers/misc/qcom/Makefile | 1 + drivers/misc/qcom/qdsp6v2/Makefile | 6 + drivers/misc/qcom/qdsp6v2/aac_in.c | 701 + drivers/misc/qcom/qdsp6v2/amrnb_in.c | 405 + drivers/misc/qcom/qdsp6v2/amrwb_in.c | 400 + drivers/misc/qcom/qdsp6v2/audio_aac.c | 474 + drivers/misc/qcom/qdsp6v2/audio_alac.c | 435 + drivers/misc/qcom/qdsp6v2/audio_amrnb.c | 226 + drivers/misc/qcom/qdsp6v2/audio_amrwb.c | 231 + drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c | 397 + drivers/misc/qcom/qdsp6v2/audio_ape.c | 359 + drivers/misc/qcom/qdsp6v2/audio_evrc.c | 184 + drivers/misc/qcom/qdsp6v2/audio_g711alaw.c | 396 + drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c | 396 + .../misc/qcom/qdsp6v2/audio_hwacc_effects.c | 787 + drivers/misc/qcom/qdsp6v2/audio_mp3.c | 188 + drivers/misc/qcom/qdsp6v2/audio_multi_aac.c | 523 + drivers/misc/qcom/qdsp6v2/audio_qcelp.c | 191 + drivers/misc/qcom/qdsp6v2/audio_utils.c | 954 ++ drivers/misc/qcom/qdsp6v2/audio_utils.h | 114 + drivers/misc/qcom/qdsp6v2/audio_utils_aio.c | 2132 +++ drivers/misc/qcom/qdsp6v2/audio_utils_aio.h | 232 + drivers/misc/qcom/qdsp6v2/audio_wma.c | 345 + drivers/misc/qcom/qdsp6v2/audio_wmapro.c | 418 + drivers/misc/qcom/qdsp6v2/evrc_in.c | 410 + drivers/misc/qcom/qdsp6v2/g711alaw_in.c | 382 + drivers/misc/qcom/qdsp6v2/g711mlaw_in.c | 385 + drivers/misc/qcom/qdsp6v2/q6audio_common.h | 37 + drivers/misc/qcom/qdsp6v2/q6audio_v2.c | 107 + drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c | 223 + drivers/misc/qcom/qdsp6v2/qcelp_in.c | 410 + drivers/misc/qcom/qdsp6v2/ultrasound/Makefile | 2 + drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c | 1467 ++ drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h | 130 + drivers/misc/qcom/qdsp6v2/ultrasound/usf.c | 2468 ++++ .../misc/qcom/qdsp6v2/ultrasound/usfcdev.c | 422 + .../misc/qcom/qdsp6v2/ultrasound/usfcdev.h | 28 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qdsp6v2/Makefile | 9 + drivers/soc/qcom/qdsp6v2/adsp-loader.c | 307 + drivers/soc/qcom/qdsp6v2/apr.c | 970 ++ drivers/soc/qcom/qdsp6v2/apr_tal.c | 298 + drivers/soc/qcom/qdsp6v2/apr_tal_glink.c | 514 + drivers/soc/qcom/qdsp6v2/apr_v2.c | 67 + drivers/soc/qcom/qdsp6v2/apr_v3.c | 65 + drivers/soc/qcom/qdsp6v2/audio_notifier.c | 635 + drivers/soc/qcom/qdsp6v2/audio_pdr.c | 147 + drivers/soc/qcom/qdsp6v2/audio_ssr.c | 66 + drivers/soc/qcom/qdsp6v2/msm_audio_ion.c | 917 ++ drivers/soc/qcom/qdsp6v2/voice_svc.c | 792 + .../dt-bindings/clock/qcom,audio-ext-clk.h | 32 + include/linux/msm_audio_ion.h | 45 + include/linux/qdsp6v2/apr.h | 190 + include/linux/qdsp6v2/apr_tal.h | 108 + include/linux/qdsp6v2/apr_us.h | 193 + include/linux/qdsp6v2/audio_notifier.h | 105 + include/linux/qdsp6v2/audio_pdr.h | 101 + include/linux/qdsp6v2/audio_ssr.h | 78 + include/linux/qdsp6v2/dsp_debug.h | 22 + include/linux/qdsp6v2/rtac.h | 98 + include/linux/qdsp6v2/usf.h | 298 + include/soc/qcom/liquid_dock.h | 21 + include/sound/adsp_err.h | 21 + include/sound/apr_audio-v2.h | 9901 +++++++++++++ include/sound/apr_audio.h | 1931 +++ include/sound/audio_cal_utils.h | 102 + include/sound/audio_calibration.h | 41 + include/sound/audio_slimslave.h | 18 + include/sound/cpe_cmi.h | 492 + include/sound/cpe_core.h | 179 + include/sound/cpe_err.h | 166 + include/sound/msm-audio-effects-q6-v2.h | 55 + include/sound/msm-dai-q6-v2.h | 92 + include/sound/msm-dts-eagle.h | 148 + include/sound/msm-slim-dma.h | 44 + include/sound/q6adm-v2.h | 156 + include/sound/q6afe-v2.h | 366 + include/sound/q6asm-v2.h | 637 + include/sound/q6audio-v2.h | 36 + include/sound/q6core.h | 159 + include/sound/q6lsm.h | 281 + include/sound/voice_params.h | 14 + include/sound/voice_svc.h | 47 + include/uapi/linux/Kbuild | 18 + include/uapi/linux/avtimer.h | 10 + include/uapi/linux/msm_audio.h | 464 + include/uapi/linux/msm_audio_aac.h | 76 + include/uapi/linux/msm_audio_ac3.h | 41 + include/uapi/linux/msm_audio_alac.h | 24 + include/uapi/linux/msm_audio_amrnb.h | 34 + include/uapi/linux/msm_audio_amrwb.h | 18 + include/uapi/linux/msm_audio_amrwbplus.h | 18 + include/uapi/linux/msm_audio_ape.h | 26 + include/uapi/linux/msm_audio_calibration.h | 692 + include/uapi/linux/msm_audio_g711.h | 17 + include/uapi/linux/msm_audio_g711_dec.h | 16 + include/uapi/linux/msm_audio_mvs.h | 155 + include/uapi/linux/msm_audio_qcp.h | 37 + include/uapi/linux/msm_audio_sbc.h | 36 + include/uapi/linux/msm_audio_voicememo.h | 66 + include/uapi/linux/msm_audio_wma.h | 33 + include/uapi/linux/msm_audio_wmapro.h | 22 + include/uapi/sound/Kbuild | 7 + include/uapi/sound/audio_effects.h | 374 + include/uapi/sound/devdep_params.h | 69 + include/uapi/sound/lsm_params.h | 175 + include/uapi/sound/msmcal-hwdep.h | 35 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/msm/Kconfig | 236 + sound/soc/msm/Makefile | 35 + sound/soc/msm/device_event.h | 20 + sound/soc/msm/msm-audio-pinctrl.c | 316 + sound/soc/msm/msm-audio-pinctrl.h | 43 + sound/soc/msm/msm-cpe-lsm.c | 3143 ++++ sound/soc/msm/msm-dai-fe.c | 2555 ++++ sound/soc/msm/msm-pcm-hostless.c | 82 + sound/soc/msm/msm8996.c | 4003 ++++++ sound/soc/msm/msm8998.c | 6828 +++++++++ sound/soc/msm/msmfalcon-common.c | 2806 ++++ sound/soc/msm/msmfalcon-common.h | 103 + sound/soc/msm/msmfalcon-ext-dai-links.c | 1967 +++ sound/soc/msm/msmfalcon-external.c | 1765 +++ sound/soc/msm/msmfalcon-external.h | 50 + sound/soc/msm/msmfalcon-internal.c | 2970 ++++ sound/soc/msm/msmfalcon-internal.h | 32 + sound/soc/msm/qdsp6v2/Makefile | 21 + sound/soc/msm/qdsp6v2/adsp_err.c | 167 + sound/soc/msm/qdsp6v2/audio_cal_utils.c | 965 ++ sound/soc/msm/qdsp6v2/audio_calibration.c | 636 + sound/soc/msm/qdsp6v2/audio_slimslave.c | 177 + .../soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c | 1399 ++ sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c | 1714 +++ sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h | 36 + sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 948 +- sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c | 488 + sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 7895 ++++++++++ sound/soc/msm/qdsp6v2/msm-dai-slim.c | 659 + sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c | 394 + sound/soc/msm/qdsp6v2/msm-dolby-common.h | 266 + sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c | 1071 ++ sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h | 84 + sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c | 2299 +++ sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h | 121 + sound/soc/msm/qdsp6v2/msm-dts-eagle.c | 1659 +++ sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c | 358 + sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h | 39 + sound/soc/msm/qdsp6v2/msm-lsm-client.c | 1989 +++ sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c | 921 ++ sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h | 49 + sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c | 596 + sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c | 1553 ++ sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c | 790 + sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c | 845 ++ sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c | 1515 ++ sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h | 128 + .../soc/msm/qdsp6v2/msm-pcm-routing-devdep.c | 176 + .../soc/msm/qdsp6v2/msm-pcm-routing-devdep.h | 33 + sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 11931 ++++++++++++++++ sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h | 472 + sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c | 758 + sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h | 42 + sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c | 1705 +++ sound/soc/msm/qdsp6v2/msm-qti-pp-config.c | 1035 ++ sound/soc/msm/qdsp6v2/msm-qti-pp-config.h | 43 + sound/soc/msm/qdsp6v2/q6adm.c | 4407 ++++++ sound/soc/msm/qdsp6v2/q6afe.c | 6626 +++++++++ sound/soc/msm/qdsp6v2/q6asm.c | 8578 +++++++++++ sound/soc/msm/qdsp6v2/q6audio-v2.c | 807 ++ sound/soc/msm/qdsp6v2/q6core.c | 951 ++ sound/soc/msm/qdsp6v2/q6lsm.c | 1950 +++ sound/soc/msm/qdsp6v2/q6voice.c | 8425 +++++++++++ sound/soc/msm/qdsp6v2/q6voice.h | 1789 +++ sound/soc/msm/qdsp6v2/rtac.c | 1904 +++ 178 files changed, 150971 insertions(+), 139 deletions(-) create mode 100644 drivers/misc/qcom/Kconfig create mode 100644 drivers/misc/qcom/Makefile create mode 100644 drivers/misc/qcom/qdsp6v2/Makefile create mode 100644 drivers/misc/qcom/qdsp6v2/aac_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/amrnb_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/amrwb_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_aac.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_alac.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_amrnb.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_amrwb.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_ape.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_evrc.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_g711alaw.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_mp3.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_multi_aac.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_qcelp.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_utils.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_utils.h create mode 100644 drivers/misc/qcom/qdsp6v2/audio_utils_aio.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_utils_aio.h create mode 100644 drivers/misc/qcom/qdsp6v2/audio_wma.c create mode 100644 drivers/misc/qcom/qdsp6v2/audio_wmapro.c create mode 100644 drivers/misc/qcom/qdsp6v2/evrc_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/g711alaw_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/g711mlaw_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/q6audio_common.h create mode 100644 drivers/misc/qcom/qdsp6v2/q6audio_v2.c create mode 100644 drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c create mode 100644 drivers/misc/qcom/qdsp6v2/qcelp_in.c create mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/Makefile create mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c create mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h create mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/usf.c create mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c create mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h create mode 100644 drivers/soc/qcom/qdsp6v2/Makefile create mode 100644 drivers/soc/qcom/qdsp6v2/adsp-loader.c create mode 100644 drivers/soc/qcom/qdsp6v2/apr.c create mode 100644 drivers/soc/qcom/qdsp6v2/apr_tal.c create mode 100644 drivers/soc/qcom/qdsp6v2/apr_tal_glink.c create mode 100644 drivers/soc/qcom/qdsp6v2/apr_v2.c create mode 100644 drivers/soc/qcom/qdsp6v2/apr_v3.c create mode 100644 drivers/soc/qcom/qdsp6v2/audio_notifier.c create mode 100644 drivers/soc/qcom/qdsp6v2/audio_pdr.c create mode 100644 drivers/soc/qcom/qdsp6v2/audio_ssr.c create mode 100644 drivers/soc/qcom/qdsp6v2/msm_audio_ion.c create mode 100644 drivers/soc/qcom/qdsp6v2/voice_svc.c create mode 100644 include/dt-bindings/clock/qcom,audio-ext-clk.h create mode 100644 include/linux/msm_audio_ion.h create mode 100644 include/linux/qdsp6v2/apr.h create mode 100644 include/linux/qdsp6v2/apr_tal.h create mode 100644 include/linux/qdsp6v2/apr_us.h create mode 100644 include/linux/qdsp6v2/audio_notifier.h create mode 100644 include/linux/qdsp6v2/audio_pdr.h create mode 100644 include/linux/qdsp6v2/audio_ssr.h create mode 100644 include/linux/qdsp6v2/dsp_debug.h create mode 100644 include/linux/qdsp6v2/rtac.h create mode 100644 include/linux/qdsp6v2/usf.h create mode 100644 include/soc/qcom/liquid_dock.h create mode 100644 include/sound/adsp_err.h create mode 100644 include/sound/apr_audio-v2.h create mode 100644 include/sound/apr_audio.h create mode 100644 include/sound/audio_cal_utils.h create mode 100644 include/sound/audio_calibration.h create mode 100644 include/sound/audio_slimslave.h create mode 100644 include/sound/cpe_cmi.h create mode 100644 include/sound/cpe_core.h create mode 100644 include/sound/cpe_err.h create mode 100644 include/sound/msm-audio-effects-q6-v2.h create mode 100644 include/sound/msm-dai-q6-v2.h create mode 100644 include/sound/msm-dts-eagle.h create mode 100644 include/sound/msm-slim-dma.h create mode 100644 include/sound/q6adm-v2.h create mode 100644 include/sound/q6afe-v2.h create mode 100644 include/sound/q6asm-v2.h create mode 100644 include/sound/q6audio-v2.h create mode 100644 include/sound/q6core.h create mode 100644 include/sound/q6lsm.h create mode 100644 include/sound/voice_params.h create mode 100644 include/sound/voice_svc.h create mode 100644 include/uapi/linux/avtimer.h create mode 100644 include/uapi/linux/msm_audio.h create mode 100644 include/uapi/linux/msm_audio_aac.h create mode 100644 include/uapi/linux/msm_audio_ac3.h create mode 100644 include/uapi/linux/msm_audio_alac.h create mode 100644 include/uapi/linux/msm_audio_amrnb.h create mode 100644 include/uapi/linux/msm_audio_amrwb.h create mode 100644 include/uapi/linux/msm_audio_amrwbplus.h create mode 100644 include/uapi/linux/msm_audio_ape.h create mode 100644 include/uapi/linux/msm_audio_calibration.h create mode 100644 include/uapi/linux/msm_audio_g711.h create mode 100644 include/uapi/linux/msm_audio_g711_dec.h create mode 100644 include/uapi/linux/msm_audio_mvs.h create mode 100644 include/uapi/linux/msm_audio_qcp.h create mode 100644 include/uapi/linux/msm_audio_sbc.h create mode 100644 include/uapi/linux/msm_audio_voicememo.h create mode 100644 include/uapi/linux/msm_audio_wma.h create mode 100644 include/uapi/linux/msm_audio_wmapro.h create mode 100644 include/uapi/sound/audio_effects.h create mode 100644 include/uapi/sound/devdep_params.h create mode 100644 include/uapi/sound/lsm_params.h create mode 100644 include/uapi/sound/msmcal-hwdep.h create mode 100644 sound/soc/msm/Kconfig create mode 100644 sound/soc/msm/Makefile create mode 100644 sound/soc/msm/device_event.h create mode 100644 sound/soc/msm/msm-audio-pinctrl.c create mode 100644 sound/soc/msm/msm-audio-pinctrl.h create mode 100644 sound/soc/msm/msm-cpe-lsm.c create mode 100644 sound/soc/msm/msm-dai-fe.c create mode 100644 sound/soc/msm/msm-pcm-hostless.c create mode 100644 sound/soc/msm/msm8996.c create mode 100644 sound/soc/msm/msm8998.c create mode 100644 sound/soc/msm/msmfalcon-common.c create mode 100644 sound/soc/msm/msmfalcon-common.h create mode 100644 sound/soc/msm/msmfalcon-ext-dai-links.c create mode 100644 sound/soc/msm/msmfalcon-external.c create mode 100644 sound/soc/msm/msmfalcon-external.h create mode 100644 sound/soc/msm/msmfalcon-internal.c create mode 100644 sound/soc/msm/msmfalcon-internal.h create mode 100644 sound/soc/msm/qdsp6v2/Makefile create mode 100644 sound/soc/msm/qdsp6v2/adsp_err.c create mode 100644 sound/soc/msm/qdsp6v2/audio_cal_utils.c create mode 100644 sound/soc/msm/qdsp6v2/audio_calibration.c create mode 100644 sound/soc/msm/qdsp6v2/audio_slimslave.c create mode 100644 sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h create mode 100644 sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dai-slim.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dolby-common.h create mode 100644 sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h create mode 100644 sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c create mode 100644 sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h create mode 100644 sound/soc/msm/qdsp6v2/msm-dts-eagle.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c create mode 100644 sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h create mode 100644 sound/soc/msm/qdsp6v2/msm-lsm-client.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h create mode 100644 sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c create mode 100644 sound/soc/msm/qdsp6v2/msm-qti-pp-config.c create mode 100644 sound/soc/msm/qdsp6v2/msm-qti-pp-config.h create mode 100644 sound/soc/msm/qdsp6v2/q6adm.c create mode 100644 sound/soc/msm/qdsp6v2/q6afe.c create mode 100644 sound/soc/msm/qdsp6v2/q6asm.c create mode 100644 sound/soc/msm/qdsp6v2/q6audio-v2.c create mode 100644 sound/soc/msm/qdsp6v2/q6core.c create mode 100644 sound/soc/msm/qdsp6v2/q6lsm.c create mode 100644 sound/soc/msm/qdsp6v2/q6voice.c create mode 100644 sound/soc/msm/qdsp6v2/q6voice.h create mode 100644 sound/soc/msm/qdsp6v2/rtac.c diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 66ec9c585812..c33b79043a1f 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -1,5 +1,66 @@ Qualcomm technologies inc audio devices for ALSA sound SoC +* msm-pcm + +Required properties: + + - compatible : "qcom,msm-pcm-dsp" + + - qcom,msm-pcm-dsp-id : device node id + +* msm-pcm-low-latency + +Required properties: + + - compatible : "qcom,msm-pcm-dsp" + + - qcom,msm-pcm-dsp-id : device node id + + Optional properties + + - qcom,msm-pcm-low-latency : Flag indicating whether + the device node is of type low latency. + + - qcom,latency-level : Flag indicating whether the device node + is of type regular low latency or ultra + low latency. + regular : regular low latency stream + ultra : ultra low latency stream + ull-pp : ultra low latency stream with post-processing capability + +* msm-pcm-dsp-noirq + +Required properties: + + - compatible : "qcom,msm-pcm-dsp-noirq"; + + Optional properties + + - qcom,msm-pcm-low-latency : Flag indicating whether + the device node is of type low latency + + - qcom,latency-level : Flag indicating whether the device node + is of type low latency or ultra low latency + ultra : ultra low latency stream + ull-pp : ultra low latency stream with post-processing capability +* msm-pcm-routing + +Required properties: + + - compatible : "qcom,msm-pcm-routing" + +* msm-pcm-lpa + +Required properties: + + - compatible : "qcom,msm-pcm-lpa" + +* msm-compr-dsp + +Required properties: + + - compatible : "qcom,msm-compr-dsp" + * msm-compress-dsp Required properties: @@ -14,8 +75,2271 @@ Optional properties 8909 purpose.If the ADSP version is anything other than this we use new ADSP APIs. +* msm-voip-dsp + +Required properties: + + - compatible : "qcom,msm-voip-dsp" + +* msm-pcm-voice + +Required properties: + + - compatible : "qcom,msm-pcm-voice" + - qcom,destroy-cvd : Flag indicating whether to destroy cvd at + the end of call for low memory targets + - qcom,vote-bms : Flag indicating whether to vote for BMS during + the call start and stop + +* msm-voice-host-pcm + +Required properties: + + - compatible : "qcom,msm-voice-host-pcm" + +* msm-voice-svc + +Required properties: + + - compatible : "qcom,msm-voice-svc" + +* msm-stub-codec + +Required properties: + + - compatible : "qcom,msm-stub-codec" + +* msm-hdmi-dba-codec-rx + +Required properties: + + - compatible : "qcom,msm-hdmi-dba-codec-rx" + - qcom,dba-bridge-chip: String info to indicate which bridge-chip + is used for HDMI using DBA. + +* msm-dai-fe + +Required properties: + + - compatible : "qcom,msm-dai-fe" + +* msm-pcm-afe + +Required properties: + + - compatible : "qcom,msm-pcm-afe" + +* msm-pcm-dtmf + +Required properties: + + - compatible : "qcom,msm-pcm-dtmf" + - qcom,msm-pcm-dtmf : Enable DTMF driver in Audio. DTMF driver is + used for generation and detection of DTMF tones, when user is in + active voice call. APR commands are sent from DTMF driver to ADSP. + +* msm-dai-stub + +[First Level Nodes] + +Required properties: + + - compatible : "msm-dai-stub" + +[Second Level Nodes] + +Required properties: + + - compatible : "qcom,msm-dai-stub-dev" + - qcom,msm-dai-stub-dev-id : Stub dai port ID value is from 0 to 3. + This enables stub CPU dai in Audio. The stub dai is used when + there is no real backend in Audio. + +* msm-dai-q6-spdif + +Optional properties: + + - compatible : "msm-dai-q6-spdif" + +* msm-dai-q6-hdmi + +Required properties: + - compatible : "msm-dai-q6-hdmi" + - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID. + It is passed onto the dsp from the apps to form an audio + path to the HDMI device. Currently the only supported value + is 8, which indicates the rx path used for audio playback + on HDMI device. + +* msm-lsm-client + +Required properties: + + - compatible : "qcom,msm-lsm-client" + +* msm-pcm-loopback + +Required properties: + + - compatible : "qcom,msm-pcm-loopback" + +* msm-dai-q6 + +[First Level Nodes] + +Required properties: + + - compatible : "msm-dai-q6" + +Optional properties: + + - qcom,ext-spk-amp-supply : External speaker amplifier power supply. + - qcom,ext-spk-amp-gpio : External speaker amplifier enable signal. + +[Second Level Nodes] + +Required properties: + + - compatible : "qcom,msm-dai-q6-dev" + - qcom,msm-dai-q6-dev-id : The slimbus multi channel port ID + Value is from 16384 to 16397 + BT SCO port ID value from 12288 to 12289 + RT Proxy port ID values from 224 to 225 and 240 to + 241 + FM Rx and TX port ID values from 12292 to 12293 + incall record Rx and TX port ID values from 32771 to 32772 + inCall Music Delivery port ID is 32773 + incall Music 2 Delivery port ID is 32770 + +* msm-auxpcm + +Required properties: + + - compatible : "qcom,msm-auxpcm-dev" + + - qcom,msm-cpudai-auxpcm-mode: mode information. The first value is + for 8khz mode, the second is for + 16khz + 0 - for PCM + + - qcom,msm-cpudai-auxpcm-sync: sync information. The first value is + for 8khz mode, the second is for + 16khz + + - qcom,msm-cpudai-auxpcm-frame: No.of bytes per frame. The first + value is for 8khz mode, the second + is for 16khz + 5 - 256BPF + 4 - 128BPF + + - qcom,msm-cpudai-auxpcm-quant: Type of quantization. The first + value is for 8khz mode, the second + is for 16khz + 2 - Linear quantization + + - qcom,msm-cpudai-auxpcm-num-slots: Number of slots per mode in the + msm-cpudai-auxpcm-slot-mapping + array. + The first value is for 8khz mode, the + second is for 16khz. Max number of + slots supported by DSP is 4, anything + above 4 will be truncated to 4 when + sent to DSP. + + - qcom,msm-cpudai-auxpcm-slot-mapping: Array of slot numbers for multi + slot scenario. The first array + is for 8khz mode, the second is + for 16khz. The size of the array + is determined by the value in + qcom,msm-cpudai-auxpcm-num-slots + + - qcom,msm-cpudai-auxpcm-data: Data field - 0. The first value is + for 8khz mode, the second is for + 16khz + + - qcom,msm-cpudai-auxpcm-pcm-clk-rate: Clock rate for pcm - 2048000. The + first value is for 8khz mode, the + second is for 16KHz mode. When clock + rate is set to zero, then external + clock is assumed. + + - qcom,msm-auxpcm-interface: name of AUXPCM interface "primary" + indicates primary AUXPCM interface + "secondary" indicates secondary + AUXPCM interface +Optional properties: + +- pinctrl-names: Pinctrl state names for each pin + group configuration. +- pinctrl-x: Defines pinctrl state for each pin + group +- qcom,msm-cpudai-afe-clk-ver: Indicates version of AFE clock + interface to be used for enabling + PCM clock. If not defined, selects + default AFE clock interface. + +* msm-pcm-hostless + +Required properties: + + - compatible : "qcom,msm-pcm-hostless" + +* msm-ocmem-audio + +Required properties: + + - compatible : "qcom,msm-ocmem-audio" + + - qcom,msm_bus,name: Client name + + - qcom,msm_bus,num_cases: Total number of use cases + + - qcom,msm_bus,active_only: Context flag for requests in active + or dual (active & sleep) contex + + - qcom,msm_bus,num_paths: Total number of master-slave pairs + + - qcom,msm_bus,vectors: Arrays of unsigned integers + representing: + master-id, slave-id, arbitrated + bandwidth, + instantaneous bandwidth +* wcd9xxx_intc + +Required properties: + + - compatible : "qcom,wcd9xxx-irq" + + - interrupt-controller : Mark this device node as an + interrupt controller + + - #interrupt-cells : Should be 1 + + - interrupt-parent : Parent interrupt controller + + - qcom,gpio-connect Gpio that connects to parent + interrupt controller + +* audio-ext-clk + +Required properties: + + - compatible : "qcom,audio-ref-clk" + + - qcom,audio-ref-clk-gpio : PMIC or APQ gpio that will be + requested to enable reference + or external clock. + +Optional properties: + + - qcom,node_has_rpm_clock: Boolean property used to indicate + whether ref. clock can be enabled + with a gpio toggle or Kernel clock + API call. + + - clock-names: Name of the PMIC clock that needs + to be enabled for audio ref clock. + This clock is set as parent. + + - clocks: phandle reference to the parent + clock. + +* audio_slimslave + +Required properties: + + - compatible : "qcom,audio-slimslave" + + - elemental-addr: slimbus slave enumeration address. + +* msm-cpe-lsm + +Required properties: + + - compatible : "qcom,msm-cpe-lsm" + - qcom,msm-cpe-lsm-id : lsm afe port ID. CPE lsm driver uses + this property to find out the input afe port ID. Currently + only supported values are 1 and 3. + +* wcd_us_euro_gpio + +Required properties: + + - compatible : "qcom,msm-cdc-pinctrl" + +* msm-dai-slim + +Required properties: + + - compatible : "qcom,msm-dai-slim" + + - elemental-addr: slimbus slave enumeration address. + +* wcd_gpio_ctrl + +Required properties: + + - compatible : "qcom,msm-cdc-pinctrl" + + - qcom,cdc-rst-n-gpio : TLMM GPIO number + + - pinctrl-names: Pinctrl state names for each pin + group configuration. + - pinctrl-x: Defines pinctrl state for each pin + group. +* msm_cdc_pinctrl + +Required properties: + + - compatible : "qcom,msm-cdc-pinctrl" + + - pinctrl-names: Pinctrl state names for each pin + group configuration. + - pinctrl-x: Defines pinctrl state for each pin + group. + +* wcd_dsp_glink + +Required properties: + + - compatible : "qcom,wcd-dsp-glink" + Example: + qcom,msm-pcm { + compatible = "qcom,msm-pcm-dsp"; + qcom,msm-pcm-dsp-id = <0>; + }; + + qcom,msm-pcm-low-latency { + compatible = "qcom,msm-pcm-dsp"; + qcom,msm-pcm-dsp-id = <1>; + qcom,msm-pcm-low-latency; + }; + + qcom,msm-pcm-routing { + compatible = "qcom,msm-pcm-routing"; + }; + + qcom,msm-pcm-lpa { + compatible = "qcom,msm-pcm-lpa"; + }; + + qcom,msm-compr-dsp { + compatible = "qcom,msm-compr-dsp"; + }; + qcom,msm-compress-dsp { compatible = "qcom,msm-compress-dsp"; }; + + qcom,msm-voip-dsp { + compatible = "qcom,msm-voip-dsp"; + }; + + qcom,msm-pcm-voice { + compatible = "qcom,msm-pcm-voice"; + qcom,destroy-cvd; + }; + + qcom,msm-pcm-voice { + compatible = "qcom,msm-pcm-voice"; + qcom,vote-bms; + }; + + qcom,msm-voice-host-pcm { + compatible = "qcom,msm-voice-host-pcm"; + }; + + qcom,msm-stub-codec { + compatible = "qcom,msm-stub-codec"; + }; + + qcom,msm-dai-fe { + compatible = "qcom,msm-dai-fe"; + }; + + qcom,msm-pcm-dtmf { + compatible = "qcom,msm-pcm-dtmf"; + }; + + qcom,msm-dai-stub { + compatible = "qcom,msm-dai-stub"; + }; + + qcom,msm-dai-q6-spdif { + compatible = "qcom,msm-dai-q6-spdif"; + }; + + qcom,msm-dai-q6-hdmi { + compatible = "qcom,msm-dai-q6-hdmi"; + qcom,msm-dai-q6-dev-id = <8>; + }; + + dai_dp: qcom,msm-dai-q6-dp { + compatible = "qcom,msm-dai-q6-hdmi"; + qcom,msm-dai-q6-dev-id = <24608>; + }; + + qcom,msm-dai-q6 { + compatible = "qcom,msm-dai-q6"; + qcom,msm-dai-q6-sb-0-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16384>; + }; + + qcom,msm-dai-q6-sb-0-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16385>; + }; + + qcom,msm-dai-q6-sb-1-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16386>; + }; + + qcom,msm-dai-q6-sb-1-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16387>; + }; + + qcom,msm-dai-q6-sb-3-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16390>; + }; + + qcom,msm-dai-q6-sb-3-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16391>; + }; + + qcom,msm-dai-q6-sb-4-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16392>; + }; + + qcom,msm-dai-q6-sb-4-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16393>; + }; + + qcom,msm-dai-q6-sb-5-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16395>; + }; + + qcom,msm-dai-q6-sb-6-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16396>; + }; + + qcom,msm-dai-q6-sb-6-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16397>; + }; + + qcom,msm-dai-q6-bt-sco-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12288>; + }; + + qcom,msm-dai-q6-bt-sco-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12289>; + }; + + qcom,msm-dai-q6-int-fm-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12292>; + }; + + qcom,msm-dai-q6-int-fm-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12293>; + }; + + qcom,msm-dai-q6-be-afe-pcm-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <224>; + }; + + qcom,msm-dai-q6-be-afe-pcm-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <225>; + }; + + qcom,msm-dai-q6-afe-proxy-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <241>; + }; + + qcom,msm-dai-q6-afe-proxy-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <240>; + }; + + qcom,msm-dai-q6-incall-record-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32771>; + }; + + qcom,msm-dai-q6-incall-record-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32772>; + }; + + qcom,msm-dai-q6-incall-music-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32773>; + }; + + qcom,msm-dai-q6-incall-music-2-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32770>; + }; + }; + + qcom,msm-pri-auxpcm { + qcom,msm-cpudai-auxpcm-mode = <1>, <1>; + qcom,msm-cpudai-auxpcm-sync = <1>, <1>; + qcom,msm-cpudai-auxpcm-frame = <5>, <4>; + qcom,msm-cpudai-auxpcm-quant = <2>, <2>; + qcom,msm-cpudai-auxpcm-num-slots = <4>, <4>; + qcom,msm-cpudai-auxpcm-slot-mapping = <1 0 0 0>, <1 3 0 0>; + qcom,msm-cpudai-auxpcm-data = <0>, <0>; + qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>; + qcom,msm-auxpcm-interface = "primary"; + compatible = "qcom,msm-auxpcm-dev"; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&pri_aux_pcm_active &pri_aux_pcm_din_active>; + pinctrl-1 = <&pri_aux_pcm_sleep &pri_aux_pcm_din_sleep>; + }; + + qcom,msm-pcm-hostless { + compatible = "qcom,msm-pcm-hostless"; + }; + + qcom,msm-ocmem-audio { + compatible = "qcom,msm-ocmem-audio"; + qcom,msm_bus,name = "audio-ocmem"; + qcom,msm_bus,num_cases = <2>; + qcom,msm_bus,active_only = <0>; + qcom,msm_bus,num_paths = <1>; + qcom,msm_bus,vectors = + <11 604 0 0>, + <11 604 32505856 325058560>; + }; + + wcd9xxx_intc: wcd9xxx-irq { + compatible = "qcom,wcd9xxx-irq"; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&msmgpio>; + interrupts = <72 0>; + interrupt-names = "cdc-int"; + }; + + clock_audio: audio_ext_clk { + compatible = "qcom,audio-ref-clk"; + qcom,audio-ref-clk-gpios = <&pm8994_gpios 15 0>; + clock-names = "osr_clk"; + clocks = <&clock_rpm clk_div_clk1>; + qcom,node_has_rpm_clock; + #clock-cells = <1>; + pinctrl-names = "sleep", "active"; + pinctrl-0 = <&spkr_i2s_clk_sleep>; + pinctrl-1 = <&spkr_i2s_clk_active>; + }; + + audio_slimslave { + compatible = "qcom,audio-slimslave"; + elemental-addr = [ff ff ff ff 17 02]; + }; + + msm_dai_slim { + compatible = "qcom,msm_dai_slim"; + elemental-addr = [ff ff ff fe 17 02]; + }; + + wcd_gpio_ctrl { + compatible = "qcom,msm-cdc-pinctrl"; + qcom,cdc-rst-n-gpio = <&tlmm 64 0>; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&cdc_reset_active>; + pinctrl-1 = <&cdc_reset_sleep>; + }; + + msm_cdc_pinctrl { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&cdc_reset_active>; + pinctrl-1 = <&cdc_reset_sleep>; + }; + + wcd_dsp_glink { + compatible = "qcom,wcd-dsp-glink"; + }; + + +* MSM8916 ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8x16-audio-codec" +- qcom,model : The user-visible name of this sound card. +- qcom,msm-snd-card-id : This id is used to recognize the sound card number +- qcom,msm-codec-type : This property is used to recognize the codec type + internal or external. +- qcom,msm-hs-micbias-type : This property is used to recognize the headset + micbias type, internal or external. +- qcom,msm-ext-pa : This property is used to inform machine driver about + the connection of external PA over available MI2S interfaces, + following values can be given to this property. + primary -> Primary MI2S interface + secondary -> Secondary MI2S interface + tertiary -> Tertiary MI2S interface + quaternary -> Quaternary MI2S interface +- qcom,msm-mclk-freq : This property is used to inform machine driver about +mclk frequency needs to be configured for internal and external PA. +- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,audio-routing : A list of the connections between audio components. +- pinctrl-names : Pincntrl entries to configure the PDM gpio lines and + cross connection switch gpio accordingly +- pinctrl-0 : This explains the active state of the PDM gpio lines +- pinctrl-1 : This explains the suspend state of the PDM gpio lines +- pinctrl-2 : This explains the active state of the cross connection + gpio lines +- pinctrl-3 : This explains the suspend state of the cross connection + gpio lines +- qcom,tapan-mclk-clk-freq : Tapan mclk Freq in Hz. +- qcom,prim-auxpcm-gpio-clk : GPIO on which Primary AUXPCM clk signal is coming. +- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming. +- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming. +- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming. +- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port +- qcom,tapan-codec-9302: Indicates that this device node is for WCD9302 audio + codec. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". + +Optional Properties: +- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming. + +Example: + + msm_dig_codec: qcom,msm-int-codec { + compatible = "qcom,msm_int_core_codec"; + qcom,dig-cdc-base-addr = <0xc0f0000>; + }; + + sound { + compatible = "qcom,msm8x16-audio-codec"; + qcom,model = "msm8x16-snd-card"; + qcom,msm-snd-card-id = <0>; + qcom,msm-codec-type = "internal"; + qcom,msm-ext-pa = <0>; + qcom,msm-mclk-freq = <12288000>; + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-hs-micbias-type = "internal"; + qcom,cdc-us-euro-gpios = <&msmgpio 120 0>; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External"; + pinctrl-names = "cdc_pdm_lines_act", + "cdc_pdm_lines_sus", + "cross_conn_det_act", + "cross_conn_det_sus"; + pinctrl-0 = <&cdc_pdm_lines_act>; + pinctrl-1 = <&cdc_pdm_lines_sus>; + pinctrl-2 = <&cross_conn_det_act>; + pinctrl-3 = <&cross_conn_det_sus>; + qcom,tapan-mclk-clk-freq = <9600000>; + qcom,prim-auxpcm-gpio-clk = <&msm_gpio 63 0>; + qcom,prim-auxpcm-gpio-sync = <&msm_gpio 64 0>; + qcom,prim-auxpcm-gpio-din = <&msm_gpio 65 0>; + qcom,prim-auxpcm-gpio-dout = <&msm_gpio 66 0>; + qcom,prim-auxpcm-gpio-set = "prim-gpio-prim"; + qcom,tapan-codec-9302; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", + "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa"; + asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>, <&dai_dp>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>, + <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8", + "msm-dai-q6-dp.24608", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", + "msm-dai-q6-dev.224", "msm-dai-q6-dev.225", + "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", + "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", + "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770"; + asoc-codec = <&stub>, <&pm8916_tombak_dig>; + asoc-codec-names = "msm-stub-codec.1", "tombak_codec"; + }; + +* MSM8974 ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8974-audio-taiko" +- qcom,model : The user-visible name of this sound card. +- reg : Offset and length of the register region(s) for MI2S/PCM MUX +- reg-names : Register region name(s) referenced in reg above + Required register resource entries are: + "lpaif_pri_mode_muxsel": Physical address of MUX to select between + Primary PCM and Primary MI2S + "lpaif_sec_mode_muxsel": Physical address of MUX to select between + Secondary PCM and Secondary MI2S + "lpaif_tert_mode_muxsel": Physical address of MUX to select between + Primary PCM and Tertiary MI2S + "lpaif_quat_mode_muxsel": Physical address of MUX to select between + Secondary PCM and Quarternary MI2S +- qcom,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. +- qcom,cdc-mclk-gpios : GPIO on which mclk signal is coming. +- qcom,taiko-mclk-clk-freq : Taiko mclk Freq in Hz. currently only 9600000Hz + is supported. +- qcom,prim-auxpcm-gpio-clk : GPIO on which Primary AUXPCM clk signal is coming. +- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming. +- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming. +- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming. +- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port + Possible Values: + prim-gpio-prim : Primary AUXPCM shares GPIOs with Primary MI2S + prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S +- qcom,sec-auxpcm-gpio-clk : GPIO on which Secondary AUXPCM clk signal is coming. +- qcom,sec-auxpcm-gpio-sync : GPIO on which Secondary AUXPCM SYNC signal is coming. +- qcom,sec-auxpcm-gpio-din : GPIO on which Secondary AUXPCM DIN signal is coming. +- qcom,sec-auxpcm-gpio-dout : GPIO on which Secondary AUXPCM DOUT signal is coming. +- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +Optional properties: +- qcom,hdmi-audio-rx: specifies if HDMI audio support is enabled or not. +- qcom,ext-ult-spk-amp-gpio : GPIO for enabling of speaker path amplifier. + +- qcom,ext-ult-lo-amp-gpio: GPIO to enable external ultrasound lineout + amplifier. + +- qcom,headset-jack-type-NO: Adjust GPIO level based on the headset jack type. +- qcom,tapan-codec-9302: Indicates that this device node is for WCD9302 audio + codec. +- qcom,mbhc-bias-internal: Flag to indicate if internal micbias should be used + for headset detection. +- qcom,dock-plug-det-irq: Interrupt line to detect Docking/Undocking of Liquid + device +- qcom,ext-spk-rear-panel-irq: Interrupt line to detect rear panel speakers + jack for Dragon Board. +- qcom,ext-spk-front-panel-irq: Interrupt line to detect front panel speakers + jack for Dragon Board. +- qcom,ext-mic-front-panel-irq: Interrupt line to detect front panel microphone + jack for Dragon Board. +- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware. + Possible Values: + 4-pole-jack : Jack on the hardware is 4-pole. + 5-pole-jack : Jack on the hardware is 5-pole. + 6-pole-jack : Jack on the hardware is 6-pole. + +* APQ8074 ASoC Machine driver + +Required properties: +- compatible : "qcom,apq8074-audio-taiko" + +Example: + +sound { + compatible = "qcom,msm8974-audio-taiko"; + qcom,model = "msm8974-taiko-snd-card"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "HEADPHONE", "LDO_H", + "Ext Spk Bottom Pos", "LINEOUT1", + "Ext Spk Bottom Neg", "LINEOUT3", + "Ext Spk Top Pos", "LINEOUT2", + "Ext Spk Top Neg", "LINEOUT4", + "AMIC1", "MIC BIAS1 Internal1", + "MIC BIAS1 Internal1", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC3", "MIC BIAS3 Internal1", + "MIC BIAS3 Internal1", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS1 Internal2", + "MIC BIAS1 Internal2", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC2", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic2", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3", + "DMIC4", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic4", + "DMIC5", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic5", + "DMIC6", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic6"; + + qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>; + qcom,taiko-mclk-clk-freq = <9600000>; + qcom,us-euro-gpios = <&pm8941_gpios 20 0>; + + qcom,hdmi-audio-rx; + qcom,ext-ult-lo-amp-gpio = <&pm8941_gpios 6 0>; + + qcom,ext-mclk-gpio = <&msmgpio 47 0>; + qcom,dock-plug-det-irq = <&pm8841_mpps 2 0>; + qcom,prim-auxpcm-gpio-clk = <&msmgpio 65 0>; + qcom,prim-auxpcm-gpio-sync = <&msmgpio 66 0>; + qcom,prim-auxpcm-gpio-din = <&msmgpio 67 0>; + qcom,prim-auxpcm-gpio-dout = <&msmgpio 68 0>; + qcom,prim-auxpcm-gpio-set = "prim-gpio-prim"; + qcom,sec-auxpcm-gpio-clk = <&msmgpio 79 0>; + qcom,sec-auxpcm-gpio-sync = <&msmgpio 80 0>; + qcom,sec-auxpcm-gpio-din = <&msmgpio 81 0>; + qcom,sec-auxpcm-gpio-dout = <&msmgpio 82 0>; + qcom,mbhc-audio-jack-type = "4-pole-jack"; +}; + +* msm-dai-mi2s + +[First Level Nodes] + +Required properties: + + - compatible : "msm-dai-mi2s" + + [Second Level Nodes] + +Required properties: + + - compatible : "qcom,msm-dai-q6-mi2s" + - qcom,msm-dai-q6-mi2s-dev-id: MSM or MDM can use Slimbus or I2S interface to + transfer data to (WCD9XXX) codec. + If slimbus interface is used then "msm-dai-q6" + needs to be filled with correct data for + slimbus interface. + The sections "msm-dai-mi2s" is used by MDM or + MSM to use I2S interface with codec. + This section is used by CPU driver in ASOC MSM + to configure MI2S interface. MSM internally + has multiple MI2S namely Primary, Secondary, + Tertiary and Quaternary MI2S. + They are represented with id 0, 1, 2, 3 + respectively. + The field "qcom,msm-dai-q6-mi2s-dev-id" + represents which of the MI2S block is used. + These MI2S are connected to I2S interface. + + - qcom,msm-mi2s-rx-lines: Each MI2S interface in MSM has one or more SD + lines. These lines are used for data transfer + between codec and MSM. + This element in indicates which output RX lines + are used in the MI2S interface. + + - qcom,msm-mi2s-tx-lines: Each MI2S interface in MSM has one or more SD + lines. These lines are used for data transfer + between codec and MSM. + This element in indicates which input TX lines + are used in the MI2S interface. + +Optional properties: + +- pinctrl-names: Pinctrl state names for each pin group + configuration. +- pinctrl-x: Defines pinctrl state for each pin group + +Example: + +qcom,msm-dai-mi2s { + compatible = "qcom,msm-dai-mi2s"; + qcom,msm-dai-q6-mi2s-prim { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <0>; + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&tert_mi2s_active &tert_mi2s_sd0_active>; + pinctrl-1 = <&tert_mi2s_sleep &tert_mi2s_sd0_sleep>; + }; +}; + +* msm-adsp-loader + +Required properties: + - compatible : "qcom,adsp-loader" + - qcom,adsp-state: + It is possible that some MSM use PIL to load the ADSP image. While + other MSM may use SBL to load the ADSP image at boot. Audio APR needs + state of ADSP to register and enable APR to be used for sending commands + to ADSP. so adsp-state represents the state of ADSP to ADSP loader. + Value of 0 indicates ADSP loader needs to use PIL and value of 2 means + ADSP image is already loaded by SBL. + +Optional properties: + - qcom,proc-img-to-load; + This property can be used to override default ADSP + loading by PIL. Based on string input, different proc is + loaded. Right now we are adding option "modem" + for 8916 purpose. Default image will be "adsp" which + will load LPASS Q6 for other targets as expected. + "adsp" option need not be explicitly mentioned in + DTSI file, as it is default option. + +Example: + +qcom,msm-adsp-loader { + compatible = "qcom,adsp-loader"; + qcom,adsp-state = <2>; + qcom,proc-img-to-load = "modem"; +}; + +* msm-audio-ion + +Required properties: + - compatible : "qcom,msm-audio-ion" + +Optional properties: + - qcom,smmu-version: + version ID to provide info regarding smmu version + used in chipset. If ARM SMMU HW - use id value as 1, + If QSMMU HW - use id value as 2. + + - qcom,smmu-enabled: + It is possible that some MSM have SMMU in ADSP. While other MSM use + no SMMU. Audio lib introduce wrapper for ION APIs. The wrapper needs + presence of SMMU in ADSP to handle ION APIs differently. + Presence of this property means ADSP has SMMU in it. + - iommus: + A phandle parsed by smmu driver. Number of entries will vary across + targets. + +Example: + +qcom,msm-audio-ion { + compatible = "qcom,msm-audio-ion; + qcom,smmu-enabled; +}; + +* msm-dai-tdm + +[First Level Nodes] + +Required properties: + + - compatible : "qcom,msm-dai-tdm" + - qcom,msm-cpudai-tdm-group-id: ID of the group device. TDM interface + supports up to 8 groups: + Primary RX: 37120 + Primary TX: 37121 + Secondary RX: 37136 + Secondary TX: 37137 + Tertiary RX: 37152 + Tertiary TX: 37153 + Quaternary RX: 37168 + Quaternary TX: 37169 + + - qcom,msm-cpudai-tdm-group-num-ports: Number of ports in + msm-cpudai-tdm-group-port-id array. + Max number of ports supported by DSP is 8. + + - qcom,msm-cpudai-tdm-group-port-id: Array of TDM port IDs of the group. + The size of the array is determined by + the value in msm-cpudai-tdm-group-num-ports. + Each group supports up to 8 ports: + Primary RX: 36864, 36866, 36868, 36870, + 36872, 36874, 36876, 36878 + Primary TX: 36865, 36867, 36869, 36871, + 36873, 36875, 36877, 36879 + Secondary RX: 36880, 36882, 36884, 36886, + 36888, 36890, 36892, 36894 + Secondary TX: 36881, 36883, 36885, 36887, + 36889, 36891, 36893, 36895 + Tertiary RX: 36896, 36898, 36900, 36902, + 36904, 36906, 36908, 36910 + Tertiary TX: 36897, 36899, 36901, 36903, + 36905, 36907, 36909, 36911 + Quaternary RX: 36912, 36914, 36916, 36918, + 36920, 36922, 36924, 36926 + Quaternary TX: 36913, 36915, 36917, 36919, + 36921, 36923, 36925, 36927 + + - qcom,msm-cpudai-tdm-clk-rate: Clock rate for tdm - 12288000. + When clock rate is set to zero, + then external clock is assumed. + + [Second Level Nodes] + +Required properties: + + - compatible : "qcom,msm-dai-q6-tdm" + - qcom,msm-dai-q6-mi2s-dev-id: TDM port ID. + + - qcom,msm-cpudai-tdm-sync-mode: Synchronization setting. + 0 - Short sync bit mode + 1 - Long sync mode + 2 - Short sync slot mode + + - qcom,msm-cpudai-tdm-sync-src: Synchronization source. + 0 - External source + 1 - Internal source + + - qcom,msm-cpudai-tdm-data-out: Data out signal to drive with other masters. + 0 - Disable + 1 - Enable + + - qcom,msm-cpudai-tdm-invert-sync: Invert the sync. + 0 - Normal + 1 - Invert + + - qcom,msm-cpudai-tdm-data-delay: Number of bit clock to delay data + with respect to sync edge. + 0 - 0 bit clock cycle + 1 - 1 bit clock cycle + 2 - 2 bit clock cycle + + - qcom,msm-cpudai-tdm-data-align: Indicate how data is packed + within the slot. For example, 32 slot width in case of + sample bit width is 24. + 0 - MSB + 1 - LSB + +Optional properties: + + - qcom,msm-cpudai-tdm-header-start-offset: TDM Custom header start offset + in bytes from this sub-frame. The bytes is counted from 0. + 0 is mapped to the 1st byte in or out of + the digital serial data line this sub-frame belong to. + Supported value: 0, 4, 8. + + - qcom,msm-cpudai-tdm-header-width: Header width per frame followed. + 2 bytes for MOST/TDM case. + Supported value: 2. + + - qcom,msm-cpudai-tdm-header-num-frame-repeat: Number of header followed. + Supported value: 8. + + - pinctrl-names: Pinctrl state names for each pin group + configuration. + + - pinctrl-x: Defines pinctrl state for each pin group. + +Example: + + qcom,msm-dai-tdm-quat-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37168>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36912>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_tdm_active &quat_tdm_dout_active>; + pinctrl-1 = <&quat_tdm_sleep &quat_tdm_dout_sleep>; + dai_quat_tdm_rx_0: qcom,msm-dai-q6-tdm-quat-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36912>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <0>; + qcom,msm-cpudai-tdm-data-align = <0>; + qcom,msm-cpudai-tdm-header-start-offset = <0>; + qcom,msm-cpudai-tdm-header-width = <2>; + qcom,msm-cpudai-tdm-header-num-frame-repeat = <8>; + }; + }; + +* MSM8996 ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8996-asoc-snd-tomtom" for tomtom codec and + node is "sound" and "qcom,msm8996-asoc-snd-tasha" + for tasha codec and node is "sound-9335" +- qcom,model : The user-visible name of this sound card. +- qcom,tomtom-mclk-clk-freq : MCLK frequency value for tomtom codec + and node is "sound" +- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec + and node is "sound-9335" +- qcom,audio-routing : A list of the connections between audio components. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +Optional properties: +- qcom,ext-ult-spk-amp-gpio : GPIO to enable ultrasound emitter amp. +- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware. + Possible Values: + 4-pole-jack : Jack on the hardware is 4-pole. + 5-pole-jack : Jack on the hardware is 5-pole. + 6-pole-jack : Jack on the hardware is 6-pole. +- clock-names : clock name defined for external clock. +- clocks : external clock defined for codec clock. +- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers. +- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset. +- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target +- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target +- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device + +Example: + + sound { + compatible = "qcom,msm8996-asoc-snd"; + qcom,model = "msm8996-tomtom-snd-card"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "AIF4 MAD", "MCLK", + "ultrasound amp", "LINEOUT1", + "ultrasound amp", "LINEOUT3", + "AMIC1", "MIC BIAS1 Internal1", + "MIC BIAS1 Internal1", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC3", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC2", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic2", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3", + "DMIC4", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic4", + "DMIC5", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic5", + "DMIC6", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic6"; + + clock-names = "osr_clk"; + clocks = <&clock_rpm clk_div_clk1>; + qcom,mbhc-audio-jack-type = "6-pole-jack"; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>, <&cpe3>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", + "msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm", + "msm-compr-dsp", "msm-cpe-lsm.3"; + asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, + <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, <&afe_pcm_rx>, + <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, + <&incall_music_rx>, <&incall_music2_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", + "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.2", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.16395", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + qcom,wsa-max-devs = <2>; + qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>, + <&wsa881x_213>, <&wsa881x_214>; + qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft", + "SpkrRight", "SpkrLeft"; + }; + +* MSM8952 ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8952-audio-codec" +- qcom,model : The user-visible name of this sound card. +- reg : Offset and length of the register region(s) for MI2S/PCM MUX +- reg-names : Register region name(s) referenced in reg above + Required register resource entries are: + "csr_gp_io_mux_mic_ctl": Physical address of MUX that controls + controls LPA IF tertiary, quad, PCM0, Digital Codec + and Secondary TLMM mux setting for mic path operation. + "csr_gp_io_mux_spkr_ctl": Physical address of MUX that controls + IF primary, secondary, Digital Codec and Primary TLMM + setting for speaker path operation. + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel": Physical address of MUX + that controls the mux between LPA IF Quad and PCM0 + path to secondary TLMM +- qcom,msm-hs-micbias-type : This property is used to recognize the headset + micbias type, internal or external. +- qcom,msm-ext-pa : This property is used to inform machine driver about + the connection of external PA over available MI2S interfaces, + following values can be given to this property. + primary -> Primary MI2S interface + secondary -> Secondary MI2S interface + tertiary -> Tertiary MI2S interface + quaternary -> Quaternary MI2S interface +- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,audio-routing : A list of the connections between audio components. +- qcom,msm-gpios : Lists down all the gpio sets that are supported. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets +mentioned in qcom,msm-gpios. +- pinctrl-names : The combinations of gpio sets from above that are supported in +the flavor. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. + +Optional properties: +- qcom,prim-auxpcm-gpio-clk : GPIO on which Primary AUXPCM clk signal is coming. +- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming. +- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming. +- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming. +- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port +- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- qcom,msm-micbias1-ext-cap : Boolean. Enable micbias1 external +capacitor mode. +- qcom,msm-micbias2-ext-cap : Boolean. Enable micbias2 external +capacitor mode. +- qcom,msm-spk-ext-pa : GPIO which enables external speaker pa. + +To Configure External Audio Switch +- qcom,msm-ext-audio-switch : GPIO which controls external switch that switches + audio path between headset and speakers. +- ext-switch-vdd-supply : Power supply that control external audio switch +- qcom,ext-switch-vdd-voltage : Minimum and maximum voltage in uV to set for + power supply. +- qcom,ext-switch-vdd-op-mode : Maxmum # of uA current the switch will draw + from the power supply. +Example: + qcom,msm-ext-audio-switch = <&msm_gpio 2 0>; - gpio # and active_state + ext-switch-vdd-supply = <&pm8950_l13>; - Power Rail + qcom,ext-switch-vdd-voltage = <3075000 3075000>; - Min, Max uV voltage + qcom,ext-switch-vdd-op-mode = <5000>; - Operational current uA + Additional needs to add two additional qcom,audio-routings + "HEADPHONE", "VDD_EXT_AUDIO_SWITCH" + "SPK_OUT", "VDD_EXT_AUDIO_SWITCH" + +- qcom,msm-mclk-freq : This property is used to inform machine driver about +mclk frequency needs to be configured for internal and external PA. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +- asoc-wsa-codec-names: This property contains list of wsa codec names. The names + should comply with the wsa nodes configurations. +- asoc-wsa-codec-prefixes: This property contains list of wsa codec prefixes. +- msm-vdd-wsa-switch-supply: WSA codec supply's regulator device tree node. +- qcom,msm-vdd-wsa-switch-voltage: WSA codec supply's voltage level in mV. +- qcom,msm-vdd-wsa-switch-current: WSA codec max current level in mA. + +Example: + sound { + compatible = "qcom,msm8952-audio-codec"; + qcom,model = "msm8952-snd-card"; + reg = <0xc051000 0x4>, + <0xc051004 0x4>, + <0xc055000 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"; + qcom,msm-ext-pa = "primary"; + qcom,msm-mclk-freq = <9600000>; + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-hs-micbias-type = "internal"; + qcom,msm-micbias1-ext-cap; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "SPK_RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External"; + qcom,msm-gpios = + "pri_i2s", + "us_eu_gpio"; + qcom,pinctrl-names = + "all_off", + "pri_i2s_act", + "us_eu_gpio_act", + "pri_i2s_us_eu_gpio_act"; + pinctrl-names = + "all_off", + "pri_i2s_act", + "us_eu_gpio_act", + "pri_i2s_us_eu_gpio_act"; + pinctrl-0 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_sus>; + pinctrl-1 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_sus>; + pinctrl-2 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_act>; + pinctrl-3 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_act>; + qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>; + qcom,prim-auxpcm-gpio-clk = <&msm_gpio 63 0>; + qcom,prim-auxpcm-gpio-sync = <&msm_gpio 64 0>; + qcom,prim-auxpcm-gpio-din = <&msm_gpio 65 0>; + qcom,prim-auxpcm-gpio-dout = <&msm_gpio 66 0>; + qcom,prim-auxpcm-gpio-set = "prim-gpio-prim"; + qcom,tapan-codec-9302; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", + "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa"; + asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>, + <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", + "msm-dai-q6-dev.224", "msm-dai-q6-dev.225", + "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", + "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", + "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770"; + asoc-codec = <&stub>, <&pm8916_tombak_dig>; + asoc-codec-names = "msm-stub-codec.1", "tombak_codec"; + asoc-wsa-codec-names = "wsa881x-i2c-codec.8-000f"; + asoc-wsa-codec-prefixes = "SpkrMono"; + }; + +* MSMFALCON ASoC Machine driver + +Required properties: +- compatible : "qcom,msmfalcon-asoc-snd" +- qcom,model : The user-visible name of this sound card. +- qcom,msm-hs-micbias-type : This property is used to recognize the headset + micbias type, internal or external. +- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,audio-routing : A list of the connections between audio components. +- qcom,msm-gpios : Lists down all the gpio sets that are supported. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets +mentioned in qcom,msm-gpios. +- pinctrl-names : The combinations of gpio sets from above that are supported in +the flavor. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. + +Optional properties: +- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- qcom,msm-micbias1-ext-cap : Boolean. Enable micbias1 external +capacitor mode. +- qcom,msm-micbias2-ext-cap : Boolean. Enable micbias2 external +capacitor mode. +- qcom,msm-spk-ext-pa : GPIO which enables external speaker pa. +- qcom,msm-mclk-freq : This property is used to inform machine driver about +mclk frequency needs to be configured for internal and external PA. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target +- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target +- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device + +Example: + sound { + compatible = "qcom,msmfalcon-asoc-snd"; + qcom,model = "msmfalcon-snd-card"; + qcom,msm-mclk-freq = <9600000>; + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-hs-micbias-type = "internal"; + qcom,msm-micbias1-ext-cap; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "SPK_RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External"; + qcom,msm-gpios = + "int_pdm", + "us_eu_gpio"; + qcom,pinctrl-names = + "all_off", + "int_pdm_act", + "us_eu_gpio_act", + "int_pdm_us_eu_gpio_act"; + pinctrl-names = + "all_off", + "int_pdm_act", + "us_eu_gpio_act", + "int_pdm_us_eu_gpio_act"; + pinctrl-0 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_sus>; + pinctrl-1 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_sus>; + pinctrl-2 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_act>; + pinctrl-3 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_act>; + qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", + "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa"; + asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>, + <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", + "msm-dai-q6-dev.224", "msm-dai-q6-dev.225", + "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", + "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", + "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + qcom,wsa-max-devs = <2>; + qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>, + <&wsa881x_213>, <&wsa881x_214>; + qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft", + "SpkrRight", "SpkrLeft"; + }; + +* MSM8952 Slimbus ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8952-audio-slimbus-codec" +- qcom,model : The user-visible name of this sound card. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets +mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs, +this would list all the 2^N combinations. +- pinctrl-names : The combinations of gpio sets from above that are supported in +the flavor. This can be sometimes same as qcom, pinctrl-names i.e with 2^N +combinations or will have less incase if some combination is not supported. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. +- reg : Offset and length of the register region(s) for MI2S/PCM MUX +- reg-names : Register region name(s) referenced in reg above + Required register resource entries are: + "csr_gp_io_mux_mic_ctl": Physical address of MUX that controls + controls LPA IF tertiary, quad, PCM0, Digital Codec + and Secondary TLMM mux setting for mic path operation. + "csr_gp_io_mux_spkr_ctl": Physical address of MUX that controls + IF primary, secondary, Digital Codec and Primary TLMM + setting for speaker path operation. +- qcom,cdc-mclk-gpios : GPIO on which mclk signal is coming. +- clock-names : clock name defined for external clock. +- qcom,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. + +Optional Properties: +- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- qcom,cdc-vdd-spkr-gpios : GPIO which controls PA for VDD speaker +- qcom,headset-jack-type-NC: Set if the headset jack type is NC (Normally Closed) +- qcom,tomtom-mclk-clk-freq : Tapan mclk Freq in Hz. currently only 9600000Hz + is supported. +- qcom,msm-ext-pa : This property is used to inform machine driver about + the connection of external PA over available MI2S interfaces, + following values can be given to this property. + primary -> Primary MI2S interface + secondary -> Secondary MI2S interface + tertiary -> Tertiary MI2S interface + quaternary -> Quaternary MI2S interface +- qcom,mi2s-audio-intf: This property is used to inform machine driver + if mi2s backend dailink has to be added as part of the sound card dai-links. +- qcom,auxpcm-audio-intf: This property is used to inform machine driver + if auxpcm backend dailink has to be added as part of the sound card dai-links. +- qcom,msm-mi2s-master: This property is used to inform machine driver + if MSM is the clock master of mi2s. 1 means master and 0 means slave. The + first entry is primary mi2s; the second entry is secondary mi2s, and so on. +- reg: This property provides the AUX PCM/MI2S mux select register addresses + and size. +- reg_names: This property provides the name of the AUX PCM/MI2S mux select + registers so the machine driver can retrieve the addresses. The order of the + names has to match the order of the registers in "reg" property. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +- asoc-wsa-codec-names: This property contains list of wsa codec names. The names + should comply with the wsa nodes configurations. +- asoc-wsa-codec-prefixes: This property contains list of wsa codec prefixes. + +Example: + sound { + compatible = "qcom,msm8952-audio-slim-codec"; + qcom,model = "msm8952-tomtom-snd-card"; + reg = <0xc051000 0x4>, + <0xc051004 0x4>, + <0xc055000 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"; + qcom,msm-ext-pa = "primary"; + qcom,mi2s-audio-intf; + qcom,auxpcm-audio-intf; + qcom,msm-mi2s-master = <1>, <0>, <1>, <1>; + reg = <0x1711a000 0x4>, + <0x1711b000 0x4>, + <0x1711c000 0x4>, + <0x1711d000 0x4>; + reg-names = "lpaif_pri_mode_muxsel", + "lpaif_sec_mode_muxsel", + "lpaif_tert_mode_muxsel", + "lpaif_quat_mode_muxsel"; + qcom,msm-mclk-freq = <9600000>; + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-hs-micbias-type = "internal"; + qcom,msm-micbias1-ext-cap; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "SPK_RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External"; + qcom,msm-gpios = + "slim", + "us_eu_gpio"; + qcom,pinctrl-names = + "all_off", + "slim_act", + "us_eu_gpio_act", + "slim_us_eu_gpio_act"; + pinctrl-names = + "all_off", + "slim_act", + "us_eu_gpio_act", + "slim_us_eu_gpio_act"; + pinctrl-0 = <&cdc_slim_lines_sus &cross_conn_det_sus>; + pinctrl-1 = <&cdc_slim_lines_act &cross_conn_det_sus>; + pinctrl-2 = <&cdc_slim_lines_sus &cross_conn_det_act>; + pinctrl-3 = <&cdc_slim_lines_act &cross_conn_det_act>; + qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>; + qcom,headset-jack-type-NC; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "SPK_OUT", "MCLK", + "AMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC4", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCRight Headset Mic", + "AMIC5", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC2", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic2", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3", + "DMIC4", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic4"; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", + "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa"; + asoc-cpu = <&dai_hdmi>, <&dai_mi2s0>, <&dai_mi2s1>, + <&dai_mi2s2>, <&dai_mi2s3>, <&sb_0_rx>, <&sb_0_tx>, + <&sb_1_rx>, <&sb_1_tx>, <&sb_3_rx>, <&sb_3_tx>, + <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, <&bt_sco_rx>, + <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, <&afe_pcm_rx>, + <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>; + asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.0", + "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", + "msm-dai-q6-mi2s.3", "msm-dai-q6-dev.16384", + "msm-dai-q6-dev.16385", "msm-dai-q6-dev.16386", + "msm-dai-q6-dev.16387", "msm-dai-q6-dev.16390", + "msm-dai-q6-dev.16391", "msm-dai-q6-dev.16392", + "msm-dai-q6-dev.16393", "msm-dai-q6-dev.16395", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", + "msm-dai-q6-dev.224", "msm-dai-q6-dev.225", + "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", + "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", + "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + asoc-wsa-codec-names = "wsa881x.20170212"; + asoc-wsa-codec-prefixes = "SpkrLeft"; + }; + +* MDM9607 ASoC Machine driver + +Required properties: +- compatible : "qcom,mdm9607-audio-tomtom" +- qcom,model : The user-visible name of this sound card. +- qcom,audio-routing : A list of the connections between audio components. +Each entry is a pair of strings, the first being the connection's sink, +the second being the connection's source. +- qcom,tomtom-mclk-clk-freq : Master clock value given to codec. Some WCD9XXX +codec can run at different mclk values. Mclk value can be 9.6MHz or 12.288MHz. +- pinctrl-names : pinctrl state names for each pin group configuration. +- pinctrl-x : defines pinctrl state for each pin group +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". + +Example: + +sound { + compatible = "qcom,mdm9607-audio-tomtom"; + qcom,model = "mdm9607-tomtom-i2s-snd-card"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "AMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC3", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3"; + + qcom,tomtom-mclk-clk-freq = <12288000>; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&hostless>, <&afe>, <&routing>, + <&pcm_dtmf>, <&host_pcm>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-pcm-hostless", "msm-pcm-afe", + "msm-pcm-routing", "msm-pcm-dtmf", "msm-voice-host-pcm"; + asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&dtmf_tx>, + <&rx_capture_tx>, <&rx_playback_rx>, + <&tx_capture_tx>, <&tx_playback_rx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, + <&afe_proxy_tx>, <&incall_record_rx>, + <&incall_record_tx>, <&incall_music_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-mi2s.0", + "msm-dai-stub-dev.4", "msm-dai-stub-dev.5", + "msm-dai-stub-dev.6", "msm-dai-stub-dev.7", + "msm-dai-stub-dev.8", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + }; + +* MDMCALIFORNIUM ASoC Machine driver + +- compatible : "qcom,mdm-audio-tasha" for tasha codec and + node is "sound" +- qcom,model : The user-visible name of this sound card. +- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec + and node is "sound-9335" +- qcom,audio-routing : A list of the connections between audio components. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +Optional properties: +- clock-names : clock name defined for external clock. +- clocks : external clock defined for codec clock. +- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers. +- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset. + +Example: + + sound { + compatible = "qcom,mdm-audio-tasha"; + qcom,model = "mdm-tasha-i2s-snd-card"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "AIF4 MAD", "MCLK", + "ultrasound amp", "LINEOUT1", + "ultrasound amp", "LINEOUT3", + "AMIC1", "MIC BIAS1 Internal1", + "MIC BIAS1 Internal1", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC3", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC2", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic2", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3", + "DMIC4", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic4", + "DMIC5", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic5", + "DMIC6", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic6"; + + qcom,tasha-mclk-clk-freq = <12288000>; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&hostless>, <&afe>, <&routing>, + <&pcm_dtmf>, <&host_pcm>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-pcm-hostless", "msm-pcm-afe", + "msm-pcm-routing", "msm-pcm-dtmf", "msm-voice-host-pcm"; + asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&dtmf_tx>, + <&rx_capture_tx>, <&rx_playback_rx>, + <&tx_capture_tx>, <&tx_playback_rx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, + <&afe_proxy_tx>, <&incall_record_rx>, + <&incall_record_tx>, <&incall_music_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-mi2s.0", + "msm-dai-stub-dev.4", "msm-dai-stub-dev.5", + "msm-dai-stub-dev.6", "msm-dai-stub-dev.7", + "msm-dai-stub-dev.8", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + qcom,aux-codec = <&stub_codec>; + }; + +* APQ8096 Automotive ASoC Machine driver + +Required properties: +- compatible : "qcom,apq8096-asoc-snd-auto" for auto codec and + node is "sound-auto", + "qcom,apq8096-asoc-snd-adp-agave" for adp agave codec and + node is "sound-adp-agave", + "qcom,apq8096-asoc-snd-adp-mmxf" for adp mmxf codec and + node is "sound-adp-mmxf". +- qcom,model : The user-visible name of this sound card. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". + +Example: + + sound-auto { + compatible = "qcom,apq8096-asoc-snd-auto"; + qcom,model = "apq8096-auto-snd-card"; + + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&compr>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-compr-dsp"; + asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, + <&dai_mi2s>, <&dai_mi2s_quat>, + <&afe_pcm_rx>, <&afe_pcm_tx>, + <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, + <&incall_music_rx>, <&incall_music2_rx>, + <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, + <&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>, + <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>, + <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>, + <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>, + <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>, + <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>, + <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", + "msm-dai-q6-hdmi.8", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-dev.224", "msm-dai-q6-dev.225", + "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", + "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", + "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", + "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", + "msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902", + "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899", + "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903", + "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914", + "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918", + "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915", + "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + }; + +* MSMFALCON ASoC Slimbus Machine driver + +Required properties: +- compatible : "qcom,msmfalcon-asoc-snd-tasha" for tasha codec, + "qcom,msmfalcon-asoc-snd-tavil" for tavil codec. +- qcom,model : The user-visible name of this sound card. +- qcom,msm-mclk-freq : MCLK frequency value for external codec +- qcom,msm-gpios : Lists down all the gpio sets that are supported. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets +mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs, +this would list all the 2^N combinations. +- pinctrl-names : The combinations of gpio sets from above that are supported in +the flavor. This can be sometimes same as qcom, pinctrl-names i.e with 2^N +combinations or will have less incase if some combination is not supported. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. +- qcom,audio-routing : A list of the connections between audio components. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +Optional properties: +- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- clock-names : clock name defined for external clock. +- clocks : external clock defined for codec clock. +- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target +- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target +- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device + +Example: + + sound-9335 { + compatible = "qcom,msmfalcon-asoc-snd-tasha"; + qcom,model = "msmfalcon-tasha-snd-card"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "AIF4 MAD", "MCLK", + "ultrasound amp", "LINEOUT1", + "ultrasound amp", "LINEOUT3", + "AMIC1", "MIC BIAS1 Internal1", + "MIC BIAS1 Internal1", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC3", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC2", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic2", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3", + "DMIC4", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic4", + "DMIC5", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic5", + "DMIC6", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic6"; + + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-mclk-freq = <9600000>; + qcom,msm-gpios = + "slim", + "us_eu_gpio"; + qcom,pinctrl-names = + "all_off", + "slim_act", + "us_eu_gpio_act", + "slim_us_eu_gpio_act"; + pinctrl-names = + "all_off", + "slim_act", + "us_eu_gpio_act", + "slim_us_eu_gpio_act"; + pinctrl-0 = <&cdc_slim_lines_sus &cross_conn_det_sus>; + pinctrl-1 = <&cdc_slim_lines_act &cross_conn_det_sus>; + pinctrl-2 = <&cdc_slim_lines_sus &cross_conn_det_act>; + pinctrl-3 = <&cdc_slim_lines_act &cross_conn_det_act>; + qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-cpe-lsm", + "msm-compr-dsp"; + asoc-cpu = <&dai_hdmi>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, + <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, + <&afe_proxy_tx>, <&incall_record_rx>, + <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>, <&sb_5_rx>; + asoc-cpu-names = "msm-dai-q6-hdmi.8", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.16395", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + qcom,wsa-max-devs = <2>; + qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>, + <&wsa881x_213>, <&wsa881x_214>; + qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft", + "SpkrRight", "SpkrLeft"; + }; + +* MSM8998 ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8998-asoc-snd-tasha" for tasha codec, + "qcom,msm8998-asoc-snd-tavil" for tavil codec. +- qcom,model : The user-visible name of this sound card. +- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec +- qcom,tavil-mclk-clk-freq : MCLK frequency value for tavil codec +- qcom,audio-routing : A list of the connections between audio components. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +Optional properties: +- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware. + Possible Values: + 4-pole-jack : Jack on the hardware is 4-pole. + 5-pole-jack : Jack on the hardware is 5-pole. + 6-pole-jack : Jack on the hardware is 6-pole. +- clock-names : clock name defined for external clock. +- clocks : external clock defined for codec clock. +- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers. +- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset. +- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target +- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target +- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device +- qcom,wcn-btfm : Property to specify if WCN BT/FM chip is used for the target + +Example: + + sound-9335 { + compatible = "qcom,msm8998-asoc-snd"; + qcom,model = "msm8998-tasha-snd-card"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "LDO_H", "MCLK", + "AIF4 MAD", "MCLK", + "ultrasound amp", "LINEOUT1", + "ultrasound amp", "LINEOUT3", + "AMIC1", "MIC BIAS1 Internal1", + "MIC BIAS1 Internal1", "Handset Mic", + "AMIC2", "MIC BIAS2 External", + "MIC BIAS2 External", "Headset Mic", + "AMIC3", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS2 External", + "MIC BIAS2 External", "ANCLeft Headset Mic", + "DMIC1", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic1", + "DMIC2", "MIC BIAS1 External", + "MIC BIAS1 External", "Digital Mic2", + "DMIC3", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic3", + "DMIC4", "MIC BIAS3 External", + "MIC BIAS3 External", "Digital Mic4", + "DMIC5", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic5", + "DMIC6", "MIC BIAS4 External", + "MIC BIAS4 External", "Digital Mic6"; + + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,tasha-mclk-clk-freq = <9600000>; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-cpe-lsm", + "msm-compr-dsp"; + asoc-cpu = <&dai_hdmi>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, + <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, + <&afe_proxy_tx>, <&incall_record_rx>, + <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>, <&sb_5_rx>; + asoc-cpu-names = "msm-dai-q6-hdmi.8", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.16395", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + qcom,wsa-max-devs = <2>; + qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>, + <&wsa881x_213>, <&wsa881x_214>; + qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft", + "SpkrRight", "SpkrLeft"; + }; + +* MSMSTUB ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8998-asoc-snd-stub" +- qcom,model : The user-visible name of this sound card. +- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +Optional properties: +- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target + +Example: + + sound_msm:sound-9335 { + compatible = "qcom,msm8998-asoc-snd"; + qcom,model = "msm8998-stub-snd-card"; + + qcom,tasha-mclk-clk-freq = <9600000>; + asoc-platform = <&pcm0>; + asoc-platform-names = "msm-pcm-dsp.0"; + asoc-cpu = <&sb_0_rx>, <&sb_0_tx>; + asoc-cpu-names = "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + qcom,wsa-max-devs = <0>; + }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 407cf290c031..f513ed4a2364 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -786,6 +786,7 @@ source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" +source "drivers/misc/qcom/Kconfig" source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6c2fde769771..7ea17ec69e8c 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PANEL) += panel.o obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o +obj-y += qcom/ obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig new file mode 100644 index 000000000000..9c73960f01ff --- /dev/null +++ b/drivers/misc/qcom/Kconfig @@ -0,0 +1,20 @@ +config MSM_QDSP6V2_CODECS + bool "Audio QDSP6V2 APR support" + depends on MSM_SMD + select SND_SOC_QDSP6V2 + help + Enable Audio codecs with APR IPC protocol support between + application processor and QDSP6 for B-family. APR is + used by audio driver to configure QDSP6's + ASM, ADM and AFE. + +config MSM_ULTRASOUND + bool "QDSP6V2 HW Ultrasound support" + select SND_SOC_QDSP6V2 + help + Enable HW Ultrasound support in QDSP6V2. + QDSP6V2 can support HW encoder & decoder and + ultrasound processing. It will enable + ultrasound data paths between + HW and services, calculating input events + upon the ultrasound data. diff --git a/drivers/misc/qcom/Makefile b/drivers/misc/qcom/Makefile new file mode 100644 index 000000000000..120bdddcbc84 --- /dev/null +++ b/drivers/misc/qcom/Makefile @@ -0,0 +1 @@ +obj-y += qdsp6v2/ diff --git a/drivers/misc/qcom/qdsp6v2/Makefile b/drivers/misc/qcom/qdsp6v2/Makefile new file mode 100644 index 000000000000..90a123adbb7f --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o g711mlaw_in.o g711alaw_in.o audio_utils.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_alac.o audio_ape.o audio_utils_aio.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_g711mlaw.o audio_g711alaw.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o audio_hwacc_effects.o +obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/ diff --git a/drivers/misc/qcom/qdsp6v2/aac_in.c b/drivers/misc/qcom/qdsp6v2/aac_in.c new file mode 100644 index 000000000000..fb1876410dc8 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/aac_in.c @@ -0,0 +1,701 @@ +/* + * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 5 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5)) + +#define AAC_FORMAT_ADTS 65535 + +static long aac_in_ioctl_shared(struct file *file, unsigned int cmd, void *arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t aac_mode = AAC_ENC_MODE_AAC_LC; + + enc_cfg = audio->enc_cfg; + aac_config = audio->codec_cfg; + /* ENCODE CFG (after new set of API's are published )bharath*/ + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + + if (audio->opened) { + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + } else { + if (audio->feedback == NON_TUNNEL_MODE) { + pr_debug("%s: starting in non_tunnel mode", + __func__); + rc = q6asm_open_read_write(audio->ac, + FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:open read write failed\n", + __func__); + break; + } + } + if (audio->feedback == TUNNEL_MODE) { + pr_debug("%s: starting in tunnel mode", + __func__); + rc = q6asm_open_read(audio->ac, + FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:open read failed\n", + __func__); + break; + } + } + audio->stopped = 0; + } + + pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__, + aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag); + if (aac_config->sbr_ps_on_flag) + aac_mode = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_mode = AAC_ENC_MODE_AAC_P; + else + aac_mode = AAC_ENC_MODE_AAC_LC; + + rc = q6asm_enc_cfg_blk_aac(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate, + enc_cfg->channels, + enc_cfg->bit_rate, + aac_mode, + enc_cfg->stream_format); + if (rc < 0) { + pr_err("%s:session id %d: cmd media format block", + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s:session id %d: media format block", + "failed\n", __func__, audio->ac->session); + break; + } + } + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure", + "failed rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed", + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config *cfg; + struct msm_audio_aac_enc_config *enc_cfg; + + cfg = (struct msm_audio_aac_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, "AUDIO_GET_AAC_CONFIG"); + rc = -EINVAL; + break; + } + memset(cfg, 0, sizeof(*cfg)); + enc_cfg = audio->enc_cfg; + if (enc_cfg->channels == CH_MODE_MONO) + cfg->channels = 1; + else + cfg->channels = 2; + + cfg->sample_rate = enc_cfg->sample_rate; + cfg->bit_rate = enc_cfg->bit_rate; + switch (enc_cfg->stream_format) { + case 0x00: + cfg->stream_format = AUDIO_AAC_FORMAT_ADTS; + break; + case 0x01: + cfg->stream_format = AUDIO_AAC_FORMAT_LOAS; + break; + case 0x02: + cfg->stream_format = AUDIO_AAC_FORMAT_ADIF; + break; + default: + case 0x03: + cfg->stream_format = AUDIO_AAC_FORMAT_RAW; + } + pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d", + "bitrate=%d\n", __func__, audio->ac->session, + cfg->stream_format, cfg->sample_rate, cfg->bit_rate); + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config *cfg; + struct msm_audio_aac_enc_config *enc_cfg; + uint32_t min_bitrate, max_bitrate; + + cfg = (struct msm_audio_aac_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + "AUDIO_SET_AAC_ENC_CONFIG", __func__); + rc = -EINVAL; + break; + } + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__, + audio->ac->session, cfg->stream_format); + + switch (cfg->stream_format) { + case AUDIO_AAC_FORMAT_ADTS: + enc_cfg->stream_format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + enc_cfg->stream_format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + enc_cfg->stream_format = 0x02; + break; + case AUDIO_AAC_FORMAT_RAW: + enc_cfg->stream_format = 0x03; + break; + default: + pr_err("%s:session id %d: unsupported AAC format %d\n", + __func__, audio->ac->session, + cfg->stream_format); + rc = -EINVAL; + break; + } + + if (cfg->channels == 1) { + cfg->channels = CH_MODE_MONO; + } else if (cfg->channels == 2) { + cfg->channels = CH_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + + min_bitrate = ((cfg->sample_rate)*(cfg->channels))/2; + /* This calculation should be based on AAC mode. But we cannot + * get AAC mode in this setconfig. min_bitrate's logical max + * value is 24000. So if min_bitrate is higher than 24000, + * choose 24000. + */ + if (min_bitrate > 24000) + min_bitrate = 24000; + max_bitrate = 6*(cfg->sample_rate)*(cfg->channels); + if (max_bitrate > 192000) + max_bitrate = 192000; + if ((cfg->bit_rate < min_bitrate) || + (cfg->bit_rate > max_bitrate)) { + pr_err("%s: bitrate permissible: max=%d, min=%d\n", + __func__, max_bitrate, min_bitrate); + pr_err("%s: ERROR in setting bitrate = %d\n", + __func__, cfg->bit_rate); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg->sample_rate; + enc_cfg->channels = cfg->channels; + enc_cfg->bit_rate = cfg->bit_rate; + pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x", + "bitrate=0x%x, format(adts/raw) = %d\n", + __func__, audio->ac->session, enc_cfg->sample_rate, + enc_cfg->channels, enc_cfg->bit_rate, + enc_cfg->stream_format); + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_cfg; + struct msm_audio_aac_config *audio_aac_cfg; + struct msm_audio_aac_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + audio_aac_cfg = audio->codec_cfg; + aac_cfg = (struct msm_audio_aac_config *)arg; + + if (aac_cfg == NULL) { + pr_err("%s: NULL config pointer %s\n", + __func__, "AUDIO_SET_AAC_CONFIG"); + rc = -EINVAL; + break; + } + pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d sbr_ps_flag = %d\n", + __func__, audio->ac->session, aac_cfg->sbr_on_flag, + aac_cfg->sbr_ps_on_flag); + audio_aac_cfg->sbr_on_flag = aac_cfg->sbr_on_flag; + audio_aac_cfg->sbr_ps_on_flag = aac_cfg->sbr_ps_on_flag; + if ((audio_aac_cfg->sbr_on_flag == 1) || + (audio_aac_cfg->sbr_ps_on_flag == 1)) { + if (enc_cfg->sample_rate < 24000) { + pr_err("%s: ERROR in setting samplerate = %d", + "\n", __func__, enc_cfg->sample_rate); + rc = -EINVAL; + break; + } + } + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long aac_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = aac_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) { + pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, &audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_cfg; + + if (copy_from_user(&aac_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_SET_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = aac_in_ioctl_shared(file, cmd, &aac_cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd=%d\n", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_aac_enc_config32 { + u32 channels; + u32 sample_rate; + u32 bit_rate; + u32 stream_format; +}; + +struct msm_audio_aac_config32 { + s16 format; + u16 audio_object; + u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */ + u16 aac_section_data_resilience_flag; + u16 aac_scalefactor_data_resilience_flag; + u16 aac_spectral_data_resilience_flag; + u16 sbr_on_flag; + u16 sbr_ps_on_flag; + u16 dual_mono_mode; + u16 channel_configuration; + u16 sample_rate; +}; + +enum { + AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32), + AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32), + AUDIO_SET_AAC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config32), + AUDIO_GET_AAC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config32) +}; + +static long aac_in_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = aac_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AAC_ENC_CONFIG_32: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config32 cfg_32; + + memset(&cfg_32, 0, sizeof(cfg_32)); + + cmd = AUDIO_GET_AAC_ENC_CONFIG; + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) { + pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG_32 failed. Rc= %d\n", + __func__, rc); + break; + } + cfg_32.channels = cfg.channels; + cfg_32.sample_rate = cfg.sample_rate; + cfg_32.bit_rate = cfg.bit_rate; + cfg_32.stream_format = cfg.stream_format; + if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AAC_ENC_CONFIG_32: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config32 cfg_32; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.channels = cfg_32.channels; + cfg.sample_rate = cfg_32.sample_rate; + cfg.bit_rate = cfg_32.bit_rate; + cfg.stream_format = cfg_32.stream_format; + /* The command should be converted from 32 bit to normal + * before the shared ioctl is called as shared ioctl + * can process only normal commands + */ + cmd = AUDIO_SET_AAC_ENC_CONFIG; + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG_32 failed. rc=%d\n", + __func__, rc); + break; + } + case AUDIO_GET_AAC_CONFIG_32: { + struct msm_audio_aac_config *aac_config; + struct msm_audio_aac_config32 aac_config_32; + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + aac_config_32.format = aac_config->format; + aac_config_32.audio_object = aac_config->audio_object; + aac_config_32.ep_config = aac_config->ep_config; + aac_config_32.aac_section_data_resilience_flag = + aac_config->aac_section_data_resilience_flag; + aac_config_32.aac_scalefactor_data_resilience_flag = + aac_config->aac_scalefactor_data_resilience_flag; + aac_config_32.aac_spectral_data_resilience_flag = + aac_config->aac_spectral_data_resilience_flag; + aac_config_32.sbr_on_flag = aac_config->sbr_on_flag; + aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag; + aac_config_32.dual_mono_mode = aac_config->dual_mono_mode; + aac_config_32.channel_configuration = + aac_config->channel_configuration; + aac_config_32.sample_rate = aac_config->sample_rate; + + if (copy_to_user((void *)arg, &aac_config_32, + sizeof(aac_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG_32: { + struct msm_audio_aac_config aac_cfg; + struct msm_audio_aac_config32 aac_cfg_32; + + if (copy_from_user(&aac_cfg_32, (void *)arg, + sizeof(aac_cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + aac_cfg.format = aac_cfg_32.format; + aac_cfg.audio_object = aac_cfg_32.audio_object; + aac_cfg.ep_config = aac_cfg_32.ep_config; + aac_cfg.aac_section_data_resilience_flag = + aac_cfg_32.aac_section_data_resilience_flag; + aac_cfg.aac_scalefactor_data_resilience_flag = + aac_cfg_32.aac_scalefactor_data_resilience_flag; + aac_cfg.aac_spectral_data_resilience_flag = + aac_cfg_32.aac_spectral_data_resilience_flag; + aac_cfg.sbr_on_flag = aac_cfg_32.sbr_on_flag; + aac_cfg.sbr_ps_on_flag = aac_cfg_32.sbr_ps_on_flag; + aac_cfg.dual_mono_mode = aac_cfg_32.dual_mono_mode; + aac_cfg.channel_configuration = + aac_cfg_32.channel_configuration; + aac_cfg.sample_rate = aac_cfg_32.sample_rate; + + cmd = AUDIO_SET_AAC_CONFIG; + rc = aac_in_ioctl_shared(file, cmd, &aac_cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d\n", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define aac_in_compat_ioctl NULL +#endif + +static int aac_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 1536; + audio->max_frames_per_buf = 5; + enc_cfg->sample_rate = 8000; + enc_cfg->channels = 1; + enc_cfg->bit_rate = 16000; + enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */ + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + aac_config->format = AUDIO_AAC_FORMAT_ADTS; + aac_config->audio_object = AUDIO_AAC_OBJECT_LC; + aac_config->sbr_on_flag = 0; + aac_config->sbr_ps_on_flag = 0; + aac_config->channel_configuration = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for", + "audio client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open aac encoder in tunnel mode */ + audio->buf_cfg.frames_per_buf = 0x01; + + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC, + FORMAT_LINEAR_PCM); + + if (rc < 0) { + pr_err("%s:session id %d: NT Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x01; + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:session id %d: Tunnel Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration", + "failed rc=%d\n", __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x00; + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = aac_in_compat_ioctl; + audio->enc_ioctl = aac_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = aac_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_in_fops, +}; + +static int __init aac_in_init(void) +{ + return misc_register(&audio_aac_in_misc); +} +device_initcall(aac_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/drivers/misc/qcom/qdsp6v2/amrnb_in.c new file mode 100644 index 000000000000..d76509d8d322 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c @@ -0,0 +1,405 @@ +/* Copyright (c) 2010-2012, 2014, 2016-2017 The Linux Foundation. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10)) + +static long amrnb_in_ioctl_shared(struct file *file, + unsigned int cmd, void *arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrnb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrnb media format block", + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block", + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed", + "rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed", + "rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 *cfg; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + + cfg = (struct msm_audio_amrnb_enc_config_v2 *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, + "AUDIO_SET_AMRNB_ENC_CONFIG_V2"); + rc = -EINVAL; + break; + } + + enc_cfg = audio->enc_cfg; + if (cfg->band_mode > 8 || + cfg->band_mode < 1) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* AMR NB encoder accepts values between 0-7 + * while openmax provides value between 1-8 + * as per spec + */ + enc_cfg->band_mode = (cfg->band_mode - 1); + enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0); + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long amrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrnb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) { + pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = amrnb_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s: AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd=%d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_amrnb_enc_config_v2_32 { + u32 band_mode; + u32 dtx_enable; + u32 frame_format; +}; + +enum { + AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+2), + struct msm_audio_amrnb_enc_config_v2_32), + AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), + struct msm_audio_amrnb_enc_config_v2_32) +}; + +static long amrnb_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrnb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2_32: { + struct msm_audio_amrnb_enc_config_v2 *amrnb_config; + struct msm_audio_amrnb_enc_config_v2_32 amrnb_config_32; + + memset(&amrnb_config_32, 0, sizeof(amrnb_config_32)); + + amrnb_config = + (struct msm_audio_amrnb_enc_config_v2 *)audio->enc_cfg; + amrnb_config_32.band_mode = amrnb_config->band_mode; + amrnb_config_32.dtx_enable = amrnb_config->dtx_enable; + amrnb_config_32.frame_format = amrnb_config->frame_format; + + if (copy_to_user((void *)arg, &amrnb_config_32, + sizeof(amrnb_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2_32: { + struct msm_audio_amrnb_enc_config_v2_32 cfg_32; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cmd = AUDIO_SET_AMRNB_ENC_CONFIG_V2; + rc = amrnb_in_ioctl_shared(file, cmd, &cfg_32); + if (rc) + pr_err("%s:AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define amrnb_in_compat_ioctl NULL +#endif + +static int amrnb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 7; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio", + "client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrnb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration", + "failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = amrnb_in_compat_ioctl; + audio->enc_ioctl = amrnb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrnb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_in_fops, +}; + +static int __init amrnb_in_init(void) +{ + return misc_register(&audio_amrnb_in_misc); +} + +device_initcall(amrnb_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c new file mode 100644 index 000000000000..541a6c07c96d --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c @@ -0,0 +1,400 @@ +/* Copyright (c) 2011-2012, 2014, 2016-2017 The Linux Foundation. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10)) + +static long amrwb_in_ioctl_shared(struct file *file, + unsigned int cmd, void *arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrwb_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrwb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrwb media format block", + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block", + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed", + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed", + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG: { + struct msm_audio_amrwb_enc_config *cfg; + struct msm_audio_amrwb_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + cfg = (struct msm_audio_amrwb_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, "AUDIO_SET_AMRWB_ENC_CONFIG"); + rc = -EINVAL; + break; + } + + if (cfg->band_mode > 8) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* ToDo: AMR WB encoder accepts values between 0-8 + * while openmax provides value between 9-17 + * as per spec + */ + enc_cfg->band_mode = cfg->band_mode; + enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0); + /* Currently DSP does not support different frameformat */ + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long amrwb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrwb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRWB_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrwb_enc_config))) + pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG: { + struct msm_audio_amrwb_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = amrwb_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_amrwb_enc_config_32 { + u32 band_mode; + u32 dtx_enable; + u32 frame_format; +}; + +enum { + AUDIO_GET_AMRWB_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), + struct msm_audio_amrwb_enc_config_32), + AUDIO_SET_AMRWB_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), + struct msm_audio_amrwb_enc_config_32) +}; + +static long amrwb_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrwb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRWB_ENC_CONFIG_32: { + struct msm_audio_amrwb_enc_config *amrwb_config; + struct msm_audio_amrwb_enc_config_32 amrwb_config_32; + + memset(&amrwb_config_32, 0, sizeof(amrwb_config_32)); + + amrwb_config = + (struct msm_audio_amrwb_enc_config *)audio->enc_cfg; + amrwb_config_32.band_mode = amrwb_config->band_mode; + amrwb_config_32.dtx_enable = amrwb_config->dtx_enable; + amrwb_config_32.frame_format = amrwb_config->frame_format; + + if (copy_to_user((void *)arg, &amrwb_config_32, + sizeof(struct msm_audio_amrwb_enc_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG_32: { + struct msm_audio_amrwb_enc_config cfg_32; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cmd = AUDIO_SET_AMRWB_ENC_CONFIG; + rc = amrwb_in_ioctl_shared(file, cmd, &cfg_32); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define amrwb_in_compat_ioctl NULL +#endif + +static int amrwb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrwb_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 8; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 16000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s:audio[%pK]: Could not allocate memory for audio", + "client\n", __func__, audio); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrwb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration", + "failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = amrwb_in_compat_ioctl; + audio->enc_ioctl = amrwb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrwb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_amrwb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb_in", + .fops = &audio_in_fops, +}; + +static int __init amrwb_in_init(void) +{ + return misc_register(&audio_amrwb_in_misc); +} + +device_initcall(amrwb_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/drivers/misc/qcom/qdsp6v2/audio_aac.c new file mode 100644 index 000000000000..0f371fd1aa48 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_aac.c @@ -0,0 +1,474 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 +#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out)) + +static struct miscdevice audio_aac_misc; +static struct ws_mgr audio_aac_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + if (audio->feedback == TUNNEL_MODE) { + aac_cfg.sample_rate = aac_config->sample_rate; + aac_cfg.ch_cfg = aac_config->channel_configuration; + } else { + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + } + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + rc = enable_volume_ramp(audio); + if (rc < 0) { + pr_err("%s: Failed to enable volume ramp\n", + __func__); + } + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + uint16_t sce_left = 1, sce_right = 2; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + aac_config = (struct msm_audio_aac_config *)arg; + if (aac_config == NULL) { + pr_err("%s: Invalid config pointer\n", __func__); + rc = -EINVAL; + break; + } + memcpy(audio->codec_cfg, aac_config, + sizeof(struct msm_audio_aac_config)); + /* PL_PR is 0 only need to check PL_SR */ + if (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR) { + pr_err("%s:Invalid dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: modify dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s:asm cmd dualmono failed rc=%d\n", + __func__, rc); + } + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_config; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(&aac_config, (void *)arg, + sizeof(aac_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s[%pK]:Failed in utils_ioctl: %d\n", + __func__, audio, rc); + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_aac_config32 { + s16 format; + u16 audio_object; + u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */ + u16 aac_section_data_resilience_flag; + u16 aac_scalefactor_data_resilience_flag; + u16 aac_spectral_data_resilience_flag; + u16 sbr_on_flag; + u16 sbr_ps_on_flag; + u16 dual_mono_mode; + u16 channel_configuration; + u16 sample_rate; +}; + +enum { + AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32), + AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG_32: { + struct msm_audio_aac_config *aac_config; + struct msm_audio_aac_config32 aac_config_32; + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + aac_config_32.format = aac_config->format; + aac_config_32.audio_object = aac_config->audio_object; + aac_config_32.ep_config = aac_config->ep_config; + aac_config_32.aac_section_data_resilience_flag = + aac_config->aac_section_data_resilience_flag; + aac_config_32.aac_scalefactor_data_resilience_flag = + aac_config->aac_scalefactor_data_resilience_flag; + aac_config_32.aac_spectral_data_resilience_flag = + aac_config->aac_spectral_data_resilience_flag; + aac_config_32.sbr_on_flag = aac_config->sbr_on_flag; + aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag; + aac_config_32.dual_mono_mode = aac_config->dual_mono_mode; + aac_config_32.channel_configuration = + aac_config->channel_configuration; + aac_config_32.sample_rate = aac_config->sample_rate; + + if (copy_to_user((void *)arg, &aac_config_32, + sizeof(aac_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG_32: { + struct msm_audio_aac_config aac_config; + struct msm_audio_aac_config32 aac_config_32; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(&aac_config_32, (void *)arg, + sizeof(aac_config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + aac_config.format = aac_config_32.format; + aac_config.audio_object = aac_config_32.audio_object; + aac_config.ep_config = aac_config_32.ep_config; + aac_config.aac_section_data_resilience_flag = + aac_config_32.aac_section_data_resilience_flag; + aac_config.aac_scalefactor_data_resilience_flag = + aac_config_32.aac_scalefactor_data_resilience_flag; + aac_config.aac_spectral_data_resilience_flag = + aac_config_32.aac_spectral_data_resilience_flag; + aac_config.sbr_on_flag = aac_config_32.sbr_on_flag; + aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag; + aac_config.dual_mono_mode = aac_config_32.dual_mono_mode; + aac_config.channel_configuration = + aac_config_32.channel_configuration; + aac_config.sample_rate = aac_config_32.sample_rate; + + cmd = AUDIO_SET_AAC_CONFIG; + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("%s[%pK]:Failed in utils_ioctl: %d\n", + __func__, audio, rc); + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC; + audio->miscdevice = &audio_aac_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_aac_ws_mgr; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + * audio->buf_cfg.frames_per_buf = 0x01; + */ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_aac_init(void) +{ + int ret = misc_register(&audio_aac_misc); + + if (ret == 0) + device_init_wakeup(audio_aac_misc.this_device, true); + audio_aac_ws_mgr.ref_cnt = 0; + mutex_init(&audio_aac_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_aac_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_alac.c b/drivers/misc/qcom/qdsp6v2/audio_alac.c new file mode 100644 index 000000000000..d0b86c684808 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_alac.c @@ -0,0 +1,435 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_alac_misc; +static struct ws_mgr audio_alac_ws_mgr; + +static const struct file_operations audio_alac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; + +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_alac_debug_fops); +} + +static int alac_channel_map(u8 *channel_mapping, uint32_t channels); + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_alac_cfg alac_cfg; + struct msm_audio_alac_config *alac_config; + u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (alac_channel_map(channel_mapping, + audio->pcm_cfg.channel_count)) { + pr_err("%s: setting channel map failed %d\n", + __func__, audio->pcm_cfg.channel_count); + } + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /*bits per sample*/ + false, false, channel_mapping); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + alac_config = (struct msm_audio_alac_config *)audio->codec_cfg; + alac_cfg.frame_length = alac_config->frameLength; + alac_cfg.compatible_version = alac_config->compatVersion; + alac_cfg.bit_depth = alac_config->bitDepth; + alac_cfg.pb = alac_config->pb; + alac_cfg.mb = alac_config->mb; + alac_cfg.kb = alac_config->kb; + alac_cfg.num_channels = alac_config->channelCount; + alac_cfg.max_run = alac_config->maxRun; + alac_cfg.max_frame_bytes = alac_config->maxSize; + alac_cfg.avg_bit_rate = alac_config->averageBitRate; + alac_cfg.sample_rate = alac_config->sampleRate; + alac_cfg.channel_layout_tag = alac_config->channelLayout; + pr_debug("%s: frame_length %d compatible_version %d bit_depth %d pb %d mb %d kb %d num_channels %d max_run %d max_frame_bytes %d avg_bit_rate %d sample_rate %d channel_layout_tag %d\n", + __func__, alac_config->frameLength, + alac_config->compatVersion, + alac_config->bitDepth, alac_config->pb, + alac_config->mb, alac_config->kb, + alac_config->channelCount, alac_config->maxRun, + alac_config->maxSize, + alac_config->averageBitRate, + alac_config->sampleRate, + alac_config->channelLayout); + /* Configure Media format block */ + rc = q6asm_media_format_block_alac(audio->ac, &alac_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_ALAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_alac_config))) { + pr_err("%s:copy_to_user for AUDIO_GET_ALAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_ALAC_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_alac_config))) { + pr_err("%s:copy_from_user for AUDIO_SET_ALAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_alac_config_32 { + u32 frameLength; + u8 compatVersion; + u8 bitDepth; + u8 pb; + u8 mb; + u8 kb; + u8 channelCount; + u16 maxRun; + u32 maxSize; + u32 averageBitRate; + u32 sampleRate; + u32 channelLayout; +}; + +enum { + AUDIO_GET_ALAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config_32), + AUDIO_SET_ALAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_ALAC_CONFIG_32: { + struct msm_audio_alac_config *alac_config; + struct msm_audio_alac_config_32 alac_config_32; + + memset(&alac_config_32, 0, sizeof(alac_config_32)); + + alac_config = (struct msm_audio_alac_config *)audio->codec_cfg; + alac_config_32.frameLength = alac_config->frameLength; + alac_config_32.compatVersion = + alac_config->compatVersion; + alac_config_32.bitDepth = alac_config->bitDepth; + alac_config_32.pb = alac_config->pb; + alac_config_32.mb = alac_config->mb; + alac_config_32.kb = alac_config->kb; + alac_config_32.channelCount = alac_config->channelCount; + alac_config_32.maxRun = alac_config->maxRun; + alac_config_32.maxSize = alac_config->maxSize; + alac_config_32.averageBitRate = alac_config->averageBitRate; + alac_config_32.sampleRate = alac_config->sampleRate; + alac_config_32.channelLayout = alac_config->channelLayout; + + if (copy_to_user((void *)arg, &alac_config_32, + sizeof(alac_config_32))) { + pr_err("%s: copy_to_user for GET_ALAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_ALAC_CONFIG_32: { + struct msm_audio_alac_config *alac_config; + struct msm_audio_alac_config_32 alac_config_32; + + if (copy_from_user(&alac_config_32, (void *)arg, + sizeof(alac_config_32))) { + pr_err("%s: copy_from_user for SET_ALAC_CONFIG_32 failed\n" + , __func__); + rc = -EFAULT; + break; + } + alac_config = (struct msm_audio_alac_config *)audio->codec_cfg; + alac_config->frameLength = alac_config_32.frameLength; + alac_config->compatVersion = + alac_config_32.compatVersion; + alac_config->bitDepth = alac_config_32.bitDepth; + alac_config->pb = alac_config_32.pb; + alac_config->mb = alac_config_32.mb; + alac_config->kb = alac_config_32.kb; + alac_config->channelCount = alac_config_32.channelCount; + alac_config->maxRun = alac_config_32.maxRun; + alac_config->maxSize = alac_config_32.maxSize; + alac_config->averageBitRate = alac_config_32.averageBitRate; + alac_config->sampleRate = alac_config_32.sampleRate; + alac_config->channelLayout = alac_config_32.channelLayout; + + break; + } + default: { + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_alac_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_alac_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_alac_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_alac_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_ALAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open ALAC decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_ALAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_alac_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + pr_debug("%s:alacdec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static int alac_channel_map(u8 *channel_mapping, uint32_t channels) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_CS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_LFE; + } else if (channels == 7) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_CS; + lchannel_mapping[6] = PCM_CHANNEL_LFE; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FLC; + lchannel_mapping[2] = PCM_CHANNEL_FRC; + lchannel_mapping[3] = PCM_CHANNEL_FL; + lchannel_mapping[4] = PCM_CHANNEL_FR; + lchannel_mapping[5] = PCM_CHANNEL_LS; + lchannel_mapping[6] = PCM_CHANNEL_RS; + lchannel_mapping[7] = PCM_CHANNEL_LFE; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +static const struct file_operations audio_alac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_alac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_alac", + .fops = &audio_alac_fops, +}; + +static int __init audio_alac_init(void) +{ + int ret = misc_register(&audio_alac_misc); + + if (ret == 0) + device_init_wakeup(audio_alac_misc.this_device, true); + audio_alac_ws_mgr.ref_cnt = 0; + mutex_init(&audio_alac_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_alac_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c new file mode 100644 index 000000000000..950098be9b95 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c @@ -0,0 +1,226 @@ +/* amrnb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_amrnb_misc; +static struct ws_mgr audio_amrnb_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrnb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + } + return rc; +} + + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrnb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_amrnb_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_amrnb_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRNB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_amrnb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_amrnb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl, +}; + +static struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +static int __init audio_amrnb_init(void) +{ + int ret = misc_register(&audio_amrnb_misc); + + if (ret == 0) + device_init_wakeup(audio_amrnb_misc.this_device, true); + audio_amrnb_ws_mgr.ref_cnt = 0; + mutex_init(&audio_amrnb_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_amrnb_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c new file mode 100644 index 000000000000..cb5db0d61ecd --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c @@ -0,0 +1,231 @@ +/* amrwb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_amrwb_misc; +static struct ws_mgr audio_amrwb_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrwb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_amrwb_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_amrwb_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRWB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_amrwb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_amrwb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl, +}; + +static struct miscdevice audio_amrwb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb", + .fops = &audio_amrwb_fops, +}; + +static int __init audio_amrwb_init(void) +{ + int ret = misc_register(&audio_amrwb_misc); + + if (ret == 0) + device_init_wakeup(audio_amrwb_misc.this_device, true); + audio_amrwb_ws_mgr.ref_cnt = 0; + mutex_init(&audio_amrwb_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_amrwb_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c new file mode 100644 index 000000000000..2132591b4f95 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c @@ -0,0 +1,397 @@ +/* amr-wbplus audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_amrwbplus_misc; +static struct ws_mgr audio_amrwbplus_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwbplus_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +static void config_debug_fs(struct q6audio_aio *audio) +{ + if (audio != NULL) { + char name[sizeof("msm_amrwbplus_") + 5]; + + snprintf(name, sizeof(name), "msm_amrwbplus_%04x", + audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_amrwbplus_debug_fops); + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + } +} +#else +static void config_debug_fs(struct q6audio_aio *audio) +{ +} +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct asm_amrwbplus_cfg q6_amrwbplus_cfg; + struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config; + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_err("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + amrwbplus_drv_config = + (struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg; + + q6_amrwbplus_cfg.size_bytes = + amrwbplus_drv_config->size_bytes; + q6_amrwbplus_cfg.version = + amrwbplus_drv_config->version; + q6_amrwbplus_cfg.num_channels = + amrwbplus_drv_config->num_channels; + q6_amrwbplus_cfg.amr_band_mode = + amrwbplus_drv_config->amr_band_mode; + q6_amrwbplus_cfg.amr_dtx_mode = + amrwbplus_drv_config->amr_dtx_mode; + q6_amrwbplus_cfg.amr_frame_fmt = + amrwbplus_drv_config->amr_frame_fmt; + q6_amrwbplus_cfg.amr_lsf_idx = + amrwbplus_drv_config->amr_lsf_idx; + + rc = q6asm_media_format_block_amrwbplus(audio->ac, + &q6_amrwbplus_cfg); + if (rc < 0) { + pr_err("q6asm_media_format_block_amrwb+ failed...\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AMRWBPLUS_CONFIG_V2: { + if ((audio) && (arg) && (audio->codec_cfg)) { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_amrwbplus_config_v2))) { + rc = -EFAULT; + pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2 failed\n", + __func__); + break; + } + } else { + pr_err("%s: wb+ config v2 invalid parameters\n" + , __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AMRWBPLUS_CONFIG_V2: { + if ((audio) && (arg) && (audio->codec_cfg)) { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_amrwbplus_config_v2))) { + rc = -EFAULT; + pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2 failed\n", + __func__); + break; + } + } else { + pr_err("%s: wb+ config invalid parameters\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + break; + } + } + return rc; +} +#ifdef CONFIG_COMPAT +struct msm_audio_amrwbplus_config_v2_32 { + u32 size_bytes; + u32 version; + u32 num_channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_fmt; + u32 amr_lsf_idx; +}; + +enum { + AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+2), + struct msm_audio_amrwbplus_config_v2_32), + AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), + struct msm_audio_amrwbplus_config_v2_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AMRWBPLUS_CONFIG_V2_32: { + if (audio && arg && (audio->codec_cfg)) { + struct msm_audio_amrwbplus_config_v2 *amrwbplus_config; + struct msm_audio_amrwbplus_config_v2_32 + amrwbplus_config_32; + + memset(&amrwbplus_config_32, 0, + sizeof(amrwbplus_config_32)); + + amrwbplus_config = + (struct msm_audio_amrwbplus_config_v2 *) + audio->codec_cfg; + amrwbplus_config_32.size_bytes = + amrwbplus_config->size_bytes; + amrwbplus_config_32.version = + amrwbplus_config->version; + amrwbplus_config_32.num_channels = + amrwbplus_config->num_channels; + amrwbplus_config_32.amr_band_mode = + amrwbplus_config->amr_band_mode; + amrwbplus_config_32.amr_dtx_mode = + amrwbplus_config->amr_dtx_mode; + amrwbplus_config_32.amr_frame_fmt = + amrwbplus_config->amr_frame_fmt; + amrwbplus_config_32.amr_lsf_idx = + amrwbplus_config->amr_lsf_idx; + + if (copy_to_user((void *)arg, &amrwbplus_config_32, + sizeof(amrwbplus_config_32))) { + rc = -EFAULT; + pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 failed\n" + , __func__); + } + } else { + pr_err("%s: wb+ Get config v2 invalid parameters\n" + , __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRWBPLUS_CONFIG_V2_32: { + if ((audio) && (arg) && (audio->codec_cfg)) { + struct msm_audio_amrwbplus_config_v2 *amrwbplus_config; + struct msm_audio_amrwbplus_config_v2_32 + amrwbplus_config_32; + + if (copy_from_user(&amrwbplus_config_32, (void *)arg, + sizeof(struct msm_audio_amrwbplus_config_v2_32))) { + rc = -EFAULT; + pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 failed\n" + , __func__); + break; + } + amrwbplus_config = + (struct msm_audio_amrwbplus_config_v2 *) + audio->codec_cfg; + amrwbplus_config->size_bytes = + amrwbplus_config_32.size_bytes; + amrwbplus_config->version = + amrwbplus_config_32.version; + amrwbplus_config->num_channels = + amrwbplus_config_32.num_channels; + amrwbplus_config->amr_band_mode = + amrwbplus_config_32.amr_band_mode; + amrwbplus_config->amr_dtx_mode = + amrwbplus_config_32.amr_dtx_mode; + amrwbplus_config->amr_frame_fmt = + amrwbplus_config_32.amr_frame_fmt; + amrwbplus_config->amr_lsf_idx = + amrwbplus_config_32.amr_lsf_idx; + } else { + pr_err("%s: wb+ config invalid parameters\n", + __func__); + rc = -EFAULT; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = + kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_amrwbplus_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_amrwbplus_ws_mgr; + + audio->ac = + q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMR_WB_PLUS); + if (rc < 0) { + pr_err("amrwbplus NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS); + if (rc < 0) { + pr_err("wb+ T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("audio_amrwbplus Not supported mode\n"); + rc = -EACCES; + goto fail; + } + + config_debug_fs(audio); + pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwbplus_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_amrwbplus_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwbplus", + .fops = &audio_amrwbplus_fops, +}; + +static int __init audio_amrwbplus_init(void) +{ + int ret = misc_register(&audio_amrwbplus_misc); + + if (ret == 0) + device_init_wakeup(audio_amrwbplus_misc.this_device, true); + audio_amrwbplus_ws_mgr.ref_cnt = 0; + mutex_init(&audio_amrwbplus_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_amrwbplus_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_ape.c b/drivers/misc/qcom/qdsp6v2/audio_ape.c new file mode 100644 index 000000000000..d7dc0648aeda --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_ape.c @@ -0,0 +1,359 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_ape_misc; +static struct ws_mgr audio_ape_ws_mgr; + +static const struct file_operations audio_ape_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_ape_debug_fops); +} + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_ape_cfg ape_cfg; + struct msm_audio_ape_config *ape_config; + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + ape_config = (struct msm_audio_ape_config *)audio->codec_cfg; + ape_cfg.compatible_version = ape_config->compatibleVersion; + ape_cfg.compression_level = ape_config->compressionLevel; + ape_cfg.format_flags = ape_config->formatFlags; + ape_cfg.blocks_per_frame = ape_config->blocksPerFrame; + ape_cfg.final_frame_blocks = ape_config->finalFrameBlocks; + ape_cfg.total_frames = ape_config->totalFrames; + ape_cfg.bits_per_sample = ape_config->bitsPerSample; + ape_cfg.num_channels = ape_config->numChannels; + ape_cfg.sample_rate = ape_config->sampleRate; + ape_cfg.seek_table_present = ape_config->seekTablePresent; + pr_debug("%s: compatibleVersion %d compressionLevel %d formatFlags %d blocksPerFrame %d finalFrameBlocks %d totalFrames %d bitsPerSample %d numChannels %d sampleRate %d seekTablePresent %d\n", + __func__, ape_config->compatibleVersion, + ape_config->compressionLevel, + ape_config->formatFlags, + ape_config->blocksPerFrame, + ape_config->finalFrameBlocks, + ape_config->totalFrames, + ape_config->bitsPerSample, + ape_config->numChannels, + ape_config->sampleRate, + ape_config->seekTablePresent); + /* Configure Media format block */ + rc = q6asm_media_format_block_ape(audio->ac, &ape_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_APE_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_ape_config))) { + pr_err("%s:copy_to_user for AUDIO_GET_APE_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_APE_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_ape_config))) { + pr_err("%s:copy_from_user for AUDIO_SET_APE_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_ape_config_32 { + u16 compatibleVersion; + u16 compressionLevel; + u32 formatFlags; + u32 blocksPerFrame; + u32 finalFrameBlocks; + u32 totalFrames; + u16 bitsPerSample; + u16 numChannels; + u32 sampleRate; + u32 seekTablePresent; + +}; + +enum { + AUDIO_GET_APE_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config_32), + AUDIO_SET_APE_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_APE_CONFIG_32: { + struct msm_audio_ape_config *ape_config; + struct msm_audio_ape_config_32 ape_config_32; + + memset(&ape_config_32, 0, sizeof(ape_config_32)); + + ape_config = (struct msm_audio_ape_config *)audio->codec_cfg; + ape_config_32.compatibleVersion = ape_config->compatibleVersion; + ape_config_32.compressionLevel = + ape_config->compressionLevel; + ape_config_32.formatFlags = ape_config->formatFlags; + ape_config_32.blocksPerFrame = ape_config->blocksPerFrame; + ape_config_32.finalFrameBlocks = ape_config->finalFrameBlocks; + ape_config_32.totalFrames = ape_config->totalFrames; + ape_config_32.bitsPerSample = ape_config->bitsPerSample; + ape_config_32.numChannels = ape_config->numChannels; + ape_config_32.sampleRate = ape_config->sampleRate; + ape_config_32.seekTablePresent = ape_config->seekTablePresent; + + if (copy_to_user((void *)arg, &ape_config_32, + sizeof(ape_config_32))) { + pr_err("%s: copy_to_user for GET_APE_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_APE_CONFIG_32: { + struct msm_audio_ape_config *ape_config; + struct msm_audio_ape_config_32 ape_config_32; + + if (copy_from_user(&ape_config_32, (void *)arg, + sizeof(ape_config_32))) { + pr_err("%s: copy_from_user for SET_APE_CONFIG_32 failed\n" + , __func__); + rc = -EFAULT; + break; + } + ape_config = (struct msm_audio_ape_config *)audio->codec_cfg; + ape_config->compatibleVersion = ape_config_32.compatibleVersion; + ape_config->compressionLevel = + ape_config_32.compressionLevel; + ape_config->formatFlags = ape_config_32.formatFlags; + ape_config->blocksPerFrame = ape_config_32.blocksPerFrame; + ape_config->finalFrameBlocks = ape_config_32.finalFrameBlocks; + ape_config->totalFrames = ape_config_32.totalFrames; + ape_config->bitsPerSample = ape_config_32.bitsPerSample; + ape_config->numChannels = ape_config_32.numChannels; + ape_config->sampleRate = ape_config_32.sampleRate; + ape_config->seekTablePresent = ape_config_32.seekTablePresent; + + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_ape_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_ape_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_ape_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_ape_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_APE); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open APE decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_APE); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_ape_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + pr_debug("%s:apedec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_ape_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_ape_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_ape", + .fops = &audio_ape_fops, +}; + +static int __init audio_ape_init(void) +{ + int ret = misc_register(&audio_ape_misc); + + if (ret == 0) + device_init_wakeup(audio_ape_misc.this_device, true); + audio_ape_ws_mgr.ref_cnt = 0; + mutex_init(&audio_ape_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_ape_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/drivers/misc/qcom/qdsp6v2/audio_evrc.c new file mode 100644 index 000000000000..87762319b013 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_evrc.c @@ -0,0 +1,184 @@ +/* evrc audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + +static struct miscdevice audio_evrc_misc; +static struct ws_mgr audio_evrc_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_evrc_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_evrc_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_evrc_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_evrc_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_EVRC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_evrc_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_evrc_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +static int __init audio_evrc_init(void) +{ + int ret = misc_register(&audio_evrc_misc); + + if (ret == 0) + device_init_wakeup(audio_evrc_misc.this_device, true); + audio_evrc_ws_mgr.ref_cnt = 0; + mutex_init(&audio_evrc_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_evrc_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c b/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c new file mode 100644 index 000000000000..24f87e4bd1a6 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c @@ -0,0 +1,396 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_g711alaw_misc; +static struct ws_mgr audio_g711_ws_mgr; + +static const struct file_operations audio_g711_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; + +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_g711_debug_fops); +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels); + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_g711_dec_cfg g711_dec_cfg; + struct msm_audio_g711_dec_config *g711_dec_config; + u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg)); + + if (g711_channel_map(channel_mapping, + audio->pcm_cfg.channel_count)) { + pr_err("%s: setting channel map failed %d\n", + __func__, audio->pcm_cfg.channel_count); + } + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /*bits per sample*/ + false, false, channel_mapping); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_cfg.sample_rate = g711_dec_config->sample_rate; + /* Configure Media format block */ + rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("%s: cmd media format block failed rc=%d\n", + __func__, rc); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("%s: AUDIO_START success enable[%d]\n", + __func__, audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + default: { + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_dec_config_32 { + u32 sample_rate; +}; + +enum { + AUDIO_SET_G711_DEC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32), + AUDIO_GET_G711_DEC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config_32.sample_rate = g711_dec_config->sample_rate; + + if (copy_to_user((void *)arg, &g711_dec_config_32, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + if (copy_from_user(&g711_dec_config_32, (void *)arg, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config->sample_rate = g711_dec_config_32.sample_rate; + + break; + } + default: { + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_g711_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (!audio) + return -ENOMEM; + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_g711alaw_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_g711_ws_mgr; + + init_waitqueue_head(&audio->event_wait); + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ /*foramt:G711_ALAW*/ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_G711_ALAW_FS); + if (rc < 0) { + pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open G711 decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_G711_ALAW_FS); + if (rc < 0) { + pr_err("%s: T mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("%s: %d mode is not supported mode\n", + __func__, file->f_mode); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("%s: debugfs_create_file failed\n", __func__); + pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_CS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_LFE; + } else if (channels == 7) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_CS; + lchannel_mapping[6] = PCM_CHANNEL_LFE; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FLC; + lchannel_mapping[2] = PCM_CHANNEL_FRC; + lchannel_mapping[3] = PCM_CHANNEL_FL; + lchannel_mapping[4] = PCM_CHANNEL_FR; + lchannel_mapping[5] = PCM_CHANNEL_LS; + lchannel_mapping[6] = PCM_CHANNEL_RS; + lchannel_mapping[7] = PCM_CHANNEL_LFE; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +static const struct file_operations audio_g711_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .compat_ioctl = audio_compat_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_g711alaw_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711alaw", + .fops = &audio_g711_fops, +}; + +static int __init audio_g711alaw_init(void) +{ + int ret = misc_register(&audio_g711alaw_misc); + + if (ret == 0) + device_init_wakeup(audio_g711alaw_misc.this_device, true); + audio_g711_ws_mgr.ref_cnt = 0; + mutex_init(&audio_g711_ws_mgr.ws_lock); + + return ret; +} +static void __exit audio_g711alaw_exit(void) +{ + misc_deregister(&audio_g711alaw_misc); + mutex_destroy(&audio_g711_ws_mgr.ws_lock); +} + +device_initcall(audio_g711alaw_init); +__exitcall(audio_g711alaw_exit); diff --git a/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c b/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c new file mode 100644 index 000000000000..10d368011931 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c @@ -0,0 +1,396 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_g711mlaw_misc; +static struct ws_mgr audio_g711_ws_mgr; + +static const struct file_operations audio_g711_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; + +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_g711_debug_fops); +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels); + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_g711_dec_cfg g711_dec_cfg; + struct msm_audio_g711_dec_config *g711_dec_config; + u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg)); + + if (g711_channel_map(channel_mapping, + audio->pcm_cfg.channel_count)) { + pr_err("%s: setting channel map failed %d\n", + __func__, audio->pcm_cfg.channel_count); + } + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /*bits per sample*/ + false, false, channel_mapping); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_cfg.sample_rate = g711_dec_config->sample_rate; + /* Configure Media format block */ + rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("%s: cmd media format block failed rc=%d\n", + __func__, rc); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("%s: AUDIO_START success enable[%d]\n", + __func__, audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: AUDIO_GET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: AUDIO_SET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + default: { + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_dec_config_32 { + u32 sample_rate; +}; + +enum { + AUDIO_SET_G711_DEC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32), + AUDIO_GET_G711_DEC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config_32.sample_rate = g711_dec_config->sample_rate; + + if (copy_to_user((void *)arg, &g711_dec_config_32, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + if (copy_from_user(&g711_dec_config_32, (void *)arg, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config->sample_rate = g711_dec_config_32.sample_rate; + + break; + } + default: { + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_g711_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (!audio) + return -ENOMEM; + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_g711mlaw_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_g711_ws_mgr; + + init_waitqueue_head(&audio->event_wait); + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ /*foramt:G711_ALAW*/ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_G711_MLAW_FS); + if (rc < 0) { + pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open G711 decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_G711_MLAW_FS); + if (rc < 0) { + pr_err("%s: T mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("%s: %d mode is not supported\n", __func__, + file->f_mode); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("%s: debugfs_create_file failed\n", __func__); + pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_CS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_LFE; + } else if (channels == 7) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_CS; + lchannel_mapping[6] = PCM_CHANNEL_LFE; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FLC; + lchannel_mapping[2] = PCM_CHANNEL_FRC; + lchannel_mapping[3] = PCM_CHANNEL_FL; + lchannel_mapping[4] = PCM_CHANNEL_FR; + lchannel_mapping[5] = PCM_CHANNEL_LS; + lchannel_mapping[6] = PCM_CHANNEL_RS; + lchannel_mapping[7] = PCM_CHANNEL_LFE; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +static const struct file_operations audio_g711_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .compat_ioctl = audio_compat_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_g711mlaw_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711mlaw", + .fops = &audio_g711_fops, +}; + +static int __init audio_g711mlaw_init(void) +{ + int ret = misc_register(&audio_g711mlaw_misc); + + if (ret == 0) + device_init_wakeup(audio_g711mlaw_misc.this_device, true); + audio_g711_ws_mgr.ref_cnt = 0; + mutex_init(&audio_g711_ws_mgr.ws_lock); + + return ret; +} + +static void __exit audio_g711mlaw_exit(void) +{ + misc_deregister(&audio_g711mlaw_misc); + mutex_destroy(&audio_g711_ws_mgr.ws_lock); +} + +device_initcall(audio_g711mlaw_init); +__exitcall(audio_g711mlaw_exit); diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c new file mode 100644 index 000000000000..c1d792b413f7 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c @@ -0,0 +1,787 @@ +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "q6audio_common.h" +#include "audio_utils_aio.h" +#include +#include + +#define MAX_CHANNELS_SUPPORTED 8 +#define WAIT_TIMEDOUT_DURATION_SECS 1 + +struct q6audio_effects { + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_hwacc_effects_config config; + + atomic_t in_count; + atomic_t out_count; + + int opened; + int started; + int buf_alloc; + struct msm_nt_eff_all_config audio_effects; +}; + +static void audio_effects_init_pp(struct audio_client *ac) +{ + int ret = 0; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + + if (!ac) { + pr_err("%s: audio client null to init pp\n", __func__); + return; + } + switch (ac->topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: + + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume1 Param failed ret=%d\n", + __func__, ret); + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_2); + if (ret < 0) + pr_err("%s: Send SoftVolume2 Param failed ret=%d\n", + __func__, ret); + + msm_dts_eagle_init_master_module(ac); + + break; + default: + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + break; + } +} + +static void audio_effects_deinit_pp(struct audio_client *ac) +{ + if (!ac) { + pr_err("%s: audio client null to deinit pp\n", __func__); + return; + } + switch (ac->topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: + msm_dts_eagle_deinit_master_module(ac); + break; + default: + break; + } +} + +static void audio_effects_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_effects *effects; + + if (!payload || !priv) { + pr_err("%s: invalid data to handle events, payload: %pK, priv: %pK\n", + __func__, payload, priv); + return; + } + + effects = (struct q6audio_effects *)priv; + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: { + atomic_inc(&effects->out_count); + wake_up(&effects->write_wait); + break; + } + case ASM_DATA_EVENT_READ_DONE_V2: { + atomic_inc(&effects->in_count); + wake_up(&effects->read_wait); + break; + } + case APR_BASIC_RSP_RESULT: { + pr_debug("%s: APR_BASIC_RSP_RESULT Cmd[0x%x] Status[0x%x]\n", + __func__, payload[0], payload[1]); + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + pr_debug("ASM_SESSION_CMD_RUN_V2\n"); + break; + default: + pr_debug("%s: Payload = [0x%x] stat[0x%x]\n", + __func__, payload[0], payload[1]); + break; + } + break; + } + default: + pr_debug("%s: Unhandled Event 0x%x token = 0x%x\n", + __func__, opcode, token); + break; + } +} + +static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s: AUDIO_START\n", __func__); + + rc = q6asm_open_read_write_v2(effects->ac, + FORMAT_LINEAR_PCM, + FORMAT_MULTI_CHANNEL_LINEAR_PCM, + effects->config.meta_mode_enabled, + effects->config.output.bits_per_sample, + true /*overwrite topology*/, + ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER); + if (rc < 0) { + pr_err("%s: Open failed for hw accelerated effects:rc=%d\n", + __func__, rc); + rc = -EINVAL; + goto ioctl_fail; + } + effects->opened = 1; + + pr_debug("%s: dec buf size: %d, num_buf: %d, enc buf size: %d, num_buf: %d\n", + __func__, effects->config.output.buf_size, + effects->config.output.num_buf, + effects->config.input.buf_size, + effects->config.input.num_buf); + rc = q6asm_audio_client_buf_alloc_contiguous(IN, effects->ac, + effects->config.output.buf_size, + effects->config.output.num_buf); + if (rc < 0) { + pr_err("%s: Write buffer Allocation failed rc = %d\n", + __func__, rc); + rc = -ENOMEM; + goto ioctl_fail; + } + atomic_set(&effects->in_count, effects->config.input.num_buf); + rc = q6asm_audio_client_buf_alloc_contiguous(OUT, effects->ac, + effects->config.input.buf_size, + effects->config.input.num_buf); + if (rc < 0) { + pr_err("%s: Read buffer Allocation failed rc = %d\n", + __func__, rc); + rc = -ENOMEM; + goto readbuf_fail; + } + atomic_set(&effects->out_count, effects->config.output.num_buf); + effects->buf_alloc = 1; + + pr_debug("%s: enc: sample_rate: %d, num_channels: %d\n", + __func__, effects->config.input.sample_rate, + effects->config.input.num_channels); + rc = q6asm_enc_cfg_blk_pcm(effects->ac, + effects->config.input.sample_rate, + effects->config.input.num_channels); + if (rc < 0) { + pr_err("%s: pcm read block config failed\n", __func__); + rc = -EINVAL; + goto cfg_fail; + } + pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n", + __func__, effects->config.output.sample_rate, + effects->config.output.num_channels, + effects->config.output.bits_per_sample); + rc = q6asm_media_format_block_pcm_format_support( + effects->ac, effects->config.output.sample_rate, + effects->config.output.num_channels, + effects->config.output.bits_per_sample); + if (rc < 0) { + pr_err("%s: pcm write format block config failed\n", + __func__); + rc = -EINVAL; + goto cfg_fail; + } + + audio_effects_init_pp(effects->ac); + + rc = q6asm_run(effects->ac, 0x00, 0x00, 0x00); + if (!rc) + effects->started = 1; + else { + effects->started = 0; + pr_err("%s: ASM run state failed\n", __func__); + } + break; + } + case AUDIO_EFFECTS_WRITE: { + char *bufptr = NULL; + uint32_t idx = 0; + uint32_t size = 0; + + if (!effects->started) { + rc = -EFAULT; + goto ioctl_fail; + } + + rc = wait_event_timeout(effects->write_wait, + atomic_read(&effects->out_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) { + pr_err("%s: write wait_event_timeout\n", __func__); + rc = -EFAULT; + goto ioctl_fail; + } + if (!atomic_read(&effects->out_count)) { + pr_err("%s: pcm stopped out_count 0\n", __func__); + rc = -EFAULT; + goto ioctl_fail; + } + + bufptr = q6asm_is_cpu_buf_avail(IN, effects->ac, &size, &idx); + if (bufptr) { + if ((effects->config.buf_cfg.output_len > size) || + copy_from_user(bufptr, (void *)arg, + effects->config.buf_cfg.output_len)) { + rc = -EFAULT; + goto ioctl_fail; + } + rc = q6asm_write(effects->ac, + effects->config.buf_cfg.output_len, + 0, 0, NO_TIMESTAMP); + if (rc < 0) { + rc = -EFAULT; + goto ioctl_fail; + } + atomic_dec(&effects->out_count); + } else { + pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n", + __func__); + } + break; + } + case AUDIO_EFFECTS_READ: { + char *bufptr = NULL; + uint32_t idx = 0; + uint32_t size = 0; + + if (!effects->started) { + rc = -EFAULT; + goto ioctl_fail; + } + + atomic_set(&effects->in_count, 0); + + q6asm_read_v2(effects->ac, effects->config.buf_cfg.input_len); + /* Read might fail initially, don't error out */ + if (rc < 0) + pr_err("%s: read failed\n", __func__); + + rc = wait_event_timeout(effects->read_wait, + atomic_read(&effects->in_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) { + pr_err("%s: read wait_event_timeout\n", __func__); + rc = -EFAULT; + goto ioctl_fail; + } + if (!atomic_read(&effects->in_count)) { + pr_err("%s: pcm stopped in_count 0\n", __func__); + rc = -EFAULT; + goto ioctl_fail; + } + + bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx); + if (bufptr) { + if (!((void *)arg)) { + rc = -EFAULT; + goto ioctl_fail; + } + if ((effects->config.buf_cfg.input_len > size) || + copy_to_user((void *)arg, bufptr, + effects->config.buf_cfg.input_len)) { + rc = -EFAULT; + goto ioctl_fail; + } + } + break; + } + default: + pr_err("%s: Invalid effects config module\n", __func__); + rc = -EINVAL; + break; + } +ioctl_fail: + return rc; +readbuf_fail: + q6asm_audio_client_buf_free_contiguous(IN, + effects->ac); + return rc; +cfg_fail: + q6asm_audio_client_buf_free_contiguous(IN, + effects->ac); + q6asm_audio_client_buf_free_contiguous(OUT, + effects->ac); + effects->buf_alloc = 0; + return rc; +} + +static long audio_effects_set_pp_param(struct q6audio_effects *effects, + long *values) +{ + int rc = 0; + int effects_module = values[0]; + + switch (effects_module) { + case VIRTUALIZER_MODULE: + pr_debug("%s: VIRTUALIZER_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_virtualizer_handler( + effects->ac, + &(effects->audio_effects.virtualizer), + (long *)&values[1]); + break; + case REVERB_MODULE: + pr_debug("%s: REVERB_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_reverb_handler(effects->ac, + &(effects->audio_effects.reverb), + (long *)&values[1]); + break; + case BASS_BOOST_MODULE: + pr_debug("%s: BASS_BOOST_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_bass_boost_handler( + effects->ac, + &(effects->audio_effects.bass_boost), + (long *)&values[1]); + break; + case PBE_MODULE: + pr_debug("%s: PBE_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_pbe_handler( + effects->ac, + &(effects->audio_effects.pbe), + (long *)&values[1]); + break; + case EQ_MODULE: + pr_debug("%s: EQ_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_popless_eq_handler( + effects->ac, + &(effects->audio_effects.equalizer), + (long *)&values[1]); + break; + case SOFT_VOLUME_MODULE: + pr_debug("%s: SA PLUS VOLUME_MODULE\n", __func__); + msm_audio_effects_volume_handler_v2(effects->ac, + &(effects->audio_effects.saplus_vol), + (long *)&values[1], SOFT_VOLUME_INSTANCE_1); + break; + case SOFT_VOLUME2_MODULE: + pr_debug("%s: TOPOLOGY SWITCH VOLUME MODULE\n", + __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_volume_handler_v2(effects->ac, + &(effects->audio_effects.topo_switch_vol), + (long *)&values[1], SOFT_VOLUME_INSTANCE_2); + break; + case DTS_EAGLE_MODULE_ENABLE: + pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) { + /* + * HPX->OFF: first disable HPX and then + * enable SA+ + * HPX->ON: first disable SA+ and then + * enable HPX + */ + bool hpx_state = (bool)values[1]; + + if (hpx_state) + msm_audio_effects_enable_extn(effects->ac, + &(effects->audio_effects), + false); + msm_dts_eagle_enable_asm(effects->ac, + hpx_state, + AUDPROC_MODULE_ID_DTS_HPX_PREMIX); + msm_dts_eagle_enable_asm(effects->ac, + hpx_state, + AUDPROC_MODULE_ID_DTS_HPX_POSTMIX); + if (!hpx_state) + msm_audio_effects_enable_extn(effects->ac, + &(effects->audio_effects), + true); + } + break; + default: + pr_err("%s: Invalid effects config module\n", __func__); + rc = -EINVAL; + } + return rc; +} + +static long audio_effects_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0; + long argvalues[MAX_PP_PARAMS_SZ] = {0}; + + switch (cmd) { + case AUDIO_SET_EFFECTS_CONFIG: { + pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__); + memset(&effects->config, 0, sizeof(effects->config)); + if (copy_from_user(&effects->config, (void *)arg, + sizeof(effects->config))) { + pr_err("%s: copy from user for AUDIO_SET_EFFECTS_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n", + __func__, effects->config.output.buf_size, + effects->config.output.num_buf, + effects->config.output.sample_rate, + effects->config.output.num_channels); + pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n", + __func__, effects->config.input.buf_size, + effects->config.input.num_buf, + effects->config.input.sample_rate, + effects->config.input.num_channels); + break; + } + case AUDIO_EFFECTS_SET_BUF_LEN: { + if (copy_from_user(&effects->config.buf_cfg, (void *)arg, + sizeof(effects->config.buf_cfg))) { + pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n", + __func__); + rc = -EFAULT; + } + pr_debug("%s: write buf len: %d, read buf len: %d\n", + __func__, effects->config.buf_cfg.output_len, + effects->config.buf_cfg.input_len); + break; + } + case AUDIO_EFFECTS_GET_BUF_AVAIL: { + struct msm_hwacc_buf_avail buf_avail; + + buf_avail.input_num_avail = atomic_read(&effects->in_count); + buf_avail.output_num_avail = atomic_read(&effects->out_count); + pr_debug("%s: write buf avail: %d, read buf avail: %d\n", + __func__, buf_avail.output_num_avail, + buf_avail.input_num_avail); + if (copy_to_user((void *)arg, &buf_avail, + sizeof(buf_avail))) { + pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_EFFECTS_SET_PP_PARAMS: { + if (copy_from_user(argvalues, (void *)arg, + MAX_PP_PARAMS_SZ*sizeof(long))) { + pr_err("%s: copy from user for pp params failed\n", + __func__); + return -EFAULT; + } + rc = audio_effects_set_pp_param(effects, argvalues); + break; + } + default: + pr_debug("%s: Calling shared ioctl\n", __func__); + rc = audio_effects_shared_ioctl(file, cmd, arg); + break; + } + if (rc) + pr_err("%s: cmd 0x%x failed\n", __func__, cmd); + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_hwacc_data_config32 { + __u32 buf_size; + __u32 num_buf; + __u32 num_channels; + __u8 channel_map[MAX_CHANNELS_SUPPORTED]; + __u32 sample_rate; + __u32 bits_per_sample; +}; + +struct msm_hwacc_buf_cfg32 { + __u32 input_len; + __u32 output_len; +}; + +struct msm_hwacc_buf_avail32 { + __u32 input_num_avail; + __u32 output_num_avail; +}; + +struct msm_hwacc_effects_config32 { + struct msm_hwacc_data_config32 input; + struct msm_hwacc_data_config32 output; + struct msm_hwacc_buf_cfg32 buf_cfg; + __u32 meta_mode_enabled; + __u32 overwrite_topology; + __s32 topology; +}; + +enum { + AUDIO_SET_EFFECTS_CONFIG32 = _IOW(AUDIO_IOCTL_MAGIC, 99, + struct msm_hwacc_effects_config32), + AUDIO_EFFECTS_SET_BUF_LEN32 = _IOW(AUDIO_IOCTL_MAGIC, 100, + struct msm_hwacc_buf_cfg32), + AUDIO_EFFECTS_GET_BUF_AVAIL32 = _IOW(AUDIO_IOCTL_MAGIC, 101, + struct msm_hwacc_buf_avail32), + AUDIO_EFFECTS_WRITE32 = _IOW(AUDIO_IOCTL_MAGIC, 102, compat_uptr_t), + AUDIO_EFFECTS_READ32 = _IOWR(AUDIO_IOCTL_MAGIC, 103, compat_uptr_t), + AUDIO_EFFECTS_SET_PP_PARAMS32 = _IOW(AUDIO_IOCTL_MAGIC, 104, + compat_uptr_t), + AUDIO_START32 = _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int), +}; + +static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0, i; + + switch (cmd) { + case AUDIO_SET_EFFECTS_CONFIG32: { + struct msm_hwacc_effects_config32 config32; + struct msm_hwacc_effects_config *config = &effects->config; + + memset(&effects->config, 0, sizeof(effects->config)); + if (copy_from_user(&config32, (void *)arg, + sizeof(config32))) { + pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + config->input.buf_size = config32.input.buf_size; + config->input.num_buf = config32.input.num_buf; + config->input.num_channels = config32.input.num_channels; + config->input.sample_rate = config32.input.sample_rate; + config->input.bits_per_sample = config32.input.bits_per_sample; + config->input.buf_size = config32.input.buf_size; + for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++) + config->input.channel_map[i] = + config32.input.channel_map[i]; + config->output.buf_size = config32.output.buf_size; + config->output.num_buf = config32.output.num_buf; + config->output.num_channels = config32.output.num_channels; + config->output.sample_rate = config32.output.sample_rate; + config->output.bits_per_sample = + config32.output.bits_per_sample; + config->output.buf_size = config32.output.buf_size; + for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++) + config->output.channel_map[i] = + config32.output.channel_map[i]; + config->buf_cfg.input_len = config32.buf_cfg.input_len; + config->buf_cfg.output_len = config32.buf_cfg.output_len; + config->meta_mode_enabled = config32.meta_mode_enabled; + config->overwrite_topology = config32.overwrite_topology; + config->topology = config32.topology; + pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n", + __func__, effects->config.output.buf_size, + effects->config.output.num_buf, + effects->config.output.sample_rate, + effects->config.output.num_channels); + pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n", + __func__, effects->config.input.buf_size, + effects->config.input.num_buf, + effects->config.input.sample_rate, + effects->config.input.num_channels); + break; + } + case AUDIO_EFFECTS_SET_BUF_LEN32: { + struct msm_hwacc_buf_cfg32 buf_cfg32; + struct msm_hwacc_effects_config *config = &effects->config; + + if (copy_from_user(&buf_cfg32, (void *)arg, + sizeof(buf_cfg32))) { + pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n", + __func__); + rc = -EFAULT; + break; + } + config->buf_cfg.input_len = buf_cfg32.input_len; + config->buf_cfg.output_len = buf_cfg32.output_len; + pr_debug("%s: write buf len: %d, read buf len: %d\n", + __func__, effects->config.buf_cfg.output_len, + effects->config.buf_cfg.input_len); + break; + } + case AUDIO_EFFECTS_GET_BUF_AVAIL32: { + struct msm_hwacc_buf_avail32 buf_avail; + + memset(&buf_avail, 0, sizeof(buf_avail)); + + buf_avail.input_num_avail = atomic_read(&effects->in_count); + buf_avail.output_num_avail = atomic_read(&effects->out_count); + pr_debug("%s: write buf avail: %d, read buf avail: %d\n", + __func__, buf_avail.output_num_avail, + buf_avail.input_num_avail); + if (copy_to_user((void *)arg, &buf_avail, + sizeof(buf_avail))) { + pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_EFFECTS_SET_PP_PARAMS32: { + long argvalues[MAX_PP_PARAMS_SZ] = {0}; + int argvalues32[MAX_PP_PARAMS_SZ] = {0}; + + if (copy_from_user(argvalues32, (void *)arg, + MAX_PP_PARAMS_SZ*sizeof(int))) { + pr_err("%s: copy from user failed for pp params\n", + __func__); + return -EFAULT; + } + for (i = 0; i < MAX_PP_PARAMS_SZ; i++) + argvalues[i] = argvalues32[i]; + + rc = audio_effects_set_pp_param(effects, argvalues); + break; + } + case AUDIO_START32: { + rc = audio_effects_shared_ioctl(file, AUDIO_START, arg); + break; + } + case AUDIO_EFFECTS_WRITE32: { + rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_WRITE, arg); + break; + } + case AUDIO_EFFECTS_READ32: { + rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_READ, arg); + break; + } + default: + pr_debug("%s: unhandled ioctl\n", __func__); + rc = -EINVAL; + break; + } + return rc; +} +#endif + +static int audio_effects_release(struct inode *inode, struct file *file) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0; + + if (!effects) { + pr_err("%s: effect is NULL\n", __func__); + return -EINVAL; + } + if (effects->opened) { + rc = wait_event_timeout(effects->write_wait, + atomic_read(&effects->out_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) + pr_err("%s: write wait_event_timeout failed\n", + __func__); + rc = wait_event_timeout(effects->read_wait, + atomic_read(&effects->in_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) + pr_err("%s: read wait_event_timeout failed\n", + __func__); + rc = q6asm_cmd(effects->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s[%pK]:Failed to close the session rc=%d\n", + __func__, effects, rc); + effects->opened = 0; + effects->started = 0; + + audio_effects_deinit_pp(effects->ac); + } + + if (effects->buf_alloc) { + q6asm_audio_client_buf_free_contiguous(IN, effects->ac); + q6asm_audio_client_buf_free_contiguous(OUT, effects->ac); + } + q6asm_audio_client_free(effects->ac); + + kfree(effects); + + pr_debug("%s: close session success\n", __func__); + return rc; +} + +static int audio_effects_open(struct inode *inode, struct file *file) +{ + struct q6audio_effects *effects; + int rc = 0; + + effects = kzalloc(sizeof(struct q6audio_effects), GFP_KERNEL); + if (!effects) + return -ENOMEM; + + effects->ac = q6asm_audio_client_alloc( + (app_cb)audio_effects_event_handler, + (void *)effects); + if (!effects->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(effects); + return -ENOMEM; + } + + init_waitqueue_head(&effects->read_wait); + init_waitqueue_head(&effects->write_wait); + + effects->opened = 0; + effects->started = 0; + effects->buf_alloc = 0; + file->private_data = effects; + pr_debug("%s: open session success\n", __func__); + return rc; +} + +static const struct file_operations audio_effects_fops = { + .owner = THIS_MODULE, + .open = audio_effects_open, + .release = audio_effects_release, + .unlocked_ioctl = audio_effects_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_effects_compat_ioctl, +#endif +}; + +struct miscdevice audio_effects_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_hweffects", + .fops = &audio_effects_fops, +}; + +static int __init audio_effects_init(void) +{ + return misc_register(&audio_effects_misc); +} + +device_initcall(audio_effects_init); +MODULE_DESCRIPTION("Audio hardware accelerated effects driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/drivers/misc/qcom/qdsp6v2/audio_mp3.c new file mode 100644 index 000000000000..0b10c7a83677 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_mp3.c @@ -0,0 +1,188 @@ +/* mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + +static struct miscdevice audio_mp3_misc; +static struct ws_mgr audio_mp3_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_mp3_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + rc = enable_volume_ramp(audio); + if (rc < 0) { + pr_err("%s: Failed to enable volume ramp\n", + __func__); + } + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_mp3_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_mp3_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_mp3_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MP3); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open MP3 decoder, expected frames is always 1 + * audio->buf_cfg.frames_per_buf = 0x01; + */ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MP3); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_mp3_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_mp3_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +static int __init audio_mp3_init(void) +{ + int ret = misc_register(&audio_mp3_misc); + + if (ret == 0) + device_init_wakeup(audio_mp3_misc.this_device, true); + audio_mp3_ws_mgr.ref_cnt = 0; + mutex_init(&audio_mp3_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_mp3_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c new file mode 100644 index 000000000000..01c3dc5ada58 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c @@ -0,0 +1,523 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 + + +/* Default number of pre-allocated event packets */ +#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out)) +static struct miscdevice audio_multiaac_misc; +static struct ws_mgr audio_multiaac_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (audio->feedback == TUNNEL_MODE) { + aac_cfg.sample_rate = aac_config->sample_rate; + aac_cfg.ch_cfg = aac_config->channel_configuration; + } else { + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + } + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_native(audio->ac, + aac_cfg.sample_rate, + aac_cfg.ch_cfg); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = q6asm_set_encdec_chan_map(audio->ac, 2); + if (rc < 0) { + pr_err("%s: cmd set encdec_chan_map failed\n", + __func__); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + uint16_t sce_left = 1, sce_right = 2; + + if (arg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + memcpy(audio->codec_cfg, arg, + sizeof(struct msm_audio_aac_config)); + aac_config = audio->codec_cfg; + if (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR) { + pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n", + __func__, aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n", + __func__, aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s: asm cmd dualmono failed rc=%d\n", + __func__, rc); + } break; + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + u32 *mix_coeff = (u32 *)arg; + + if (!arg) { + pr_err("%s: Invalid param for %s\n", + __func__, "AUDIO_SET_AAC_MIX_CONFIG"); + rc = -EINVAL; + break; + } + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__); + pr_debug("%s, value of coeff = %d", + __func__, *mix_coeff); + q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff); + if (rc < 0) + pr_err("%s asm aac_sel_mix_coef failed rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n" + , __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_config; + + if (copy_from_user(&aac_config, (void *)arg, + sizeof(aac_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n" + , __func__); + rc = -EFAULT; + } + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + u32 mix_config; + + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__); + if (copy_from_user(&mix_config, (void *)arg, + sizeof(u32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = audio_ioctl_shared(file, cmd, &mix_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("Calling utils ioctl\n"); + rc = audio->codec_ioctl(file, cmd, arg); + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_aac_config32 { + s16 format; + u16 audio_object; + u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */ + u16 aac_section_data_resilience_flag; + u16 aac_scalefactor_data_resilience_flag; + u16 aac_spectral_data_resilience_flag; + u16 sbr_on_flag; + u16 sbr_ps_on_flag; + u16 dual_mono_mode; + u16 channel_configuration; + u16 sample_rate; +}; + +enum { + AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32), + AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32), +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG_32: { + struct msm_audio_aac_config *aac_config; + struct msm_audio_aac_config32 aac_config_32; + + memset(&aac_config_32, 0, sizeof(aac_config_32)); + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + aac_config_32.format = aac_config->format; + aac_config_32.audio_object = aac_config->audio_object; + aac_config_32.ep_config = aac_config->ep_config; + aac_config_32.aac_section_data_resilience_flag = + aac_config->aac_section_data_resilience_flag; + aac_config_32.aac_scalefactor_data_resilience_flag = + aac_config->aac_scalefactor_data_resilience_flag; + aac_config_32.aac_spectral_data_resilience_flag = + aac_config->aac_spectral_data_resilience_flag; + aac_config_32.sbr_on_flag = aac_config->sbr_on_flag; + aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag; + aac_config_32.dual_mono_mode = aac_config->dual_mono_mode; + aac_config_32.channel_configuration = + aac_config->channel_configuration; + aac_config_32.sample_rate = aac_config->sample_rate; + + if (copy_to_user((void *)arg, &aac_config_32, + sizeof(aac_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG_32: { + struct msm_audio_aac_config aac_config; + struct msm_audio_aac_config32 aac_config_32; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(&aac_config_32, (void *)arg, + sizeof(aac_config_32))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed", + __func__); + rc = -EFAULT; + break; + } + aac_config.format = aac_config_32.format; + aac_config.audio_object = aac_config_32.audio_object; + aac_config.ep_config = aac_config_32.ep_config; + aac_config.aac_section_data_resilience_flag = + aac_config_32.aac_section_data_resilience_flag; + aac_config.aac_scalefactor_data_resilience_flag = + aac_config_32.aac_scalefactor_data_resilience_flag; + aac_config.aac_spectral_data_resilience_flag = + aac_config_32.aac_spectral_data_resilience_flag; + aac_config.sbr_on_flag = aac_config_32.sbr_on_flag; + aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag; + aac_config.dual_mono_mode = aac_config_32.dual_mono_mode; + aac_config.channel_configuration = + aac_config_32.channel_configuration; + aac_config.sample_rate = aac_config_32.sample_rate; + + cmd = AUDIO_SET_AAC_CONFIG; + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + u32 mix_config; + + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG\n", __func__); + if (copy_from_user(&mix_config, (void *)arg, + sizeof(u32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n" + , __func__); + rc = -EFAULT; + break; + } + rc = audio_ioctl_shared(file, cmd, &mix_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("Calling utils ioctl\n"); + rc = audio->codec_compat_ioctl(file, cmd, arg); + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_multi_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + + aac_config = audio->codec_cfg; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM; + audio->miscdevice = &audio_multiaac_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_multiaac_ws_mgr; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + * audio->buf_cfg.frames_per_buf = 0x01; + */ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_multi_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n", + __func__, audio->feedback, audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_multiaac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_multi_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_aac_init(void) +{ + int ret = misc_register(&audio_multiaac_misc); + + if (ret == 0) + device_init_wakeup(audio_multiaac_misc.this_device, true); + audio_multiaac_ws_mgr.ref_cnt = 0; + mutex_init(&audio_multiaac_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_aac_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c new file mode 100644 index 000000000000..8f2511c40b03 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c @@ -0,0 +1,191 @@ +/* qcelp(v13k) audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + +#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in)) + +static struct miscdevice audio_qcelp_misc; +static struct ws_mgr audio_qcelp_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_qcelp_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_qcelp_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + audio->miscdevice = &audio_qcelp_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_qcelp_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_V13K); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_qcelp_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_qcelp_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +static int __init audio_qcelp_init(void) +{ + int ret = misc_register(&audio_qcelp_misc); + + if (ret == 0) + device_init_wakeup(audio_qcelp_misc.this_device, true); + audio_qcelp_ws_mgr.ref_cnt = 0; + mutex_init(&audio_qcelp_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_qcelp_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c new file mode 100644 index 000000000000..15ee9f51a7d8 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c @@ -0,0 +1,954 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* + * Define maximum buffer size. Below values are chosen considering the higher + * values used among all native drivers. + */ +#define MAX_FRAME_SIZE 1536 +#define MAX_FRAMES 5 +#define META_SIZE (sizeof(struct meta_out_dsp)) +#define MAX_BUFFER_SIZE (1 + ((MAX_FRAME_SIZE + META_SIZE) * MAX_FRAMES)) + +static int audio_in_pause(struct q6audio_in *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + + return rc; +} + +static int audio_in_flush(struct q6audio_in *audio) +{ + int rc; + + pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session); + /* Flush if session running */ + if (audio->enabled) { + /* Implicitly issue a pause to the encoder before flushing */ + rc = audio_in_pause(audio); + if (rc < 0) { + pr_err("%s:session id %d: pause cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) { + pr_err("%s:session id %d: flush cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + /* 2nd arg: 0 -> run immediately + * 3rd arg: 0 -> msw_ts, + * 4th arg: 0 ->lsw_ts + */ + q6asm_run(audio->ac, 0x00, 0x00, 0x00); + pr_debug("Rerun the session\n"); + } + audio->rflush = 1; + audio->wflush = 1; + memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + /* get read_lock to ensure no more waiting read thread */ + mutex_lock(&audio->read_lock); + audio->rflush = 0; + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + /* get write_lock to ensure no more waiting write thread */ + mutex_lock(&audio->write_lock); + audio->wflush = 0; + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: in_bytes %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_bytes)); + pr_debug("%s:session id %d: in_samples %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + atomic_set(&audio->out_count, 0); + return 0; +} + +/* must be called with audio->lock held */ +int audio_in_enable(struct q6audio_in *audio) +{ + if (audio->enabled) + return 0; + + /* 2nd arg: 0 -> run immediately + * 3rd arg: 0 -> msw_ts, + * 4th arg: 0 ->lsw_ts + */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +/* must be called with audio->lock held */ +int audio_in_disable(struct q6audio_in *audio) +{ + int rc = 0; + + if (!audio->stopped) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n", + __func__, audio->ac->session, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s:session id %d: Failed to close the session rc=%d\n", + __func__, audio->ac->session, + rc); + audio->stopped = 1; + memset(audio->out_frame_info, 0, + sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + } + pr_debug("%s:session id %d: enabled[%d]\n", __func__, + audio->ac->session, audio->enabled); + return rc; +} + +int audio_in_buf_alloc(struct q6audio_in *audio) +{ + int rc = 0; + + switch (audio->buf_alloc) { + case NO_BUF_ALLOC: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, + audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_IN: + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_OUT: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + break; + default: + pr_debug("%s:session id %d: buf[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + } + + return rc; +} + +int audio_in_set_config(struct file *file, + struct msm_audio_config *cfg) +{ + int rc = 0; + struct q6audio_in *audio = file->private_data; + + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s:session id %d: Not sufficient permission to change the record mode\n", + __func__, audio->ac->session); + rc = -EACCES; + goto ret; + } + if ((cfg->buffer_count > PCM_BUF_COUNT) || + (cfg->buffer_count == 1)) + cfg->buffer_count = PCM_BUF_COUNT; + + audio->pcm_cfg.buffer_count = cfg->buffer_count; + audio->pcm_cfg.buffer_size = cfg->buffer_size; + audio->pcm_cfg.channel_count = cfg->channel_count; + audio->pcm_cfg.sample_rate = cfg->sample_rate; + if (audio->opened && audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, audio->ac->session); + rc = -ENOMEM; + goto ret; + } + } + audio->buf_alloc |= BUF_ALLOC_IN; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__, + audio->ac->session, audio->pcm_cfg.buffer_count, + audio->pcm_cfg.buffer_size); +ret: + return rc; +} +/* ------------------- device --------------------- */ +static long audio_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_FLUSH: { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + rc = audio_in_flush(audio); + if (rc < 0) + pr_err("%s:session id %d: Flush Fail rc=%d\n", + __func__, audio->ac->session, rc); + else { /* Register back the flushed read buffer with DSP */ + int cnt = 0; + + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + pr_debug("register the read buffer\n"); + } + break; + } + case AUDIO_PAUSE: { + pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__, + audio->ac->session); + if (audio->enabled) + audio_in_pause(audio); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(u16))) { + pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n", + __func__); + rc = -EFAULT; + } + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + memset(&stats, 0, sizeof(stats)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: + case AUDIO_PAUSE: + case AUDIO_GET_SESSION_ID: + rc = audio_in_ioctl_shared(file, cmd, arg); + break; + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, cfg.buffer_size, + cfg.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n" + , __func__); + rc = -EFAULT; + break; + } + /* Minimum single frame size, + * but with in maximum frames number + */ + if ((cfg.buffer_size < (audio->min_frame_size + + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + if (cfg.buffer_size > MAX_BUFFER_SIZE) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + if (audio->opened) { + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + } + audio->buf_alloc |= BUF_ALLOC_OUT; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size + */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_CONFIG: { + if (copy_to_user((void *)arg, &audio->pcm_cfg, + sizeof(struct msm_audio_config))) + rc = -EFAULT; + break; + + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = audio_in_set_config(file, &cfg); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_stats32 { + u32 byte_count; + u32 sample_count; + u32 unused[2]; +}; + +struct msm_audio_stream_config32 { + u32 buffer_size; + u32 buffer_count; +}; + +struct msm_audio_config32 { + u32 buffer_size; + u32 buffer_count; + u32 channel_count; + u32 sample_rate; + u32 type; + u32 meta_field; + u32 bits; + u32 unused[3]; +}; + +struct msm_audio_buf_cfg32 { + u32 meta_info_enable; + u32 frames_per_buf; +}; + +enum { + AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3, + struct msm_audio_config32), + AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4, + struct msm_audio_config32), + AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5, + struct msm_audio_stats32), + AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80, + struct msm_audio_stream_config32), + AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81, + struct msm_audio_stream_config32), + AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94, + struct msm_audio_buf_cfg32), + AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93, + struct msm_audio_buf_cfg32), +}; + +long audio_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS_32) { + struct msm_audio_stats32 stats_32; + + memset(&stats_32, 0, sizeof(stats_32)); + stats_32.byte_count = atomic_read(&audio->in_bytes); + stats_32.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats_32, sizeof(stats_32))) { + pr_err("%s: copy_to_user failed for AUDIO_GET_STATS_32\n", + __func__); + return -EFAULT; + } + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: + case AUDIO_PAUSE: + case AUDIO_GET_SESSION_ID: + rc = audio_in_ioctl_shared(file, cmd, arg); + break; + case AUDIO_GET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg_32; + + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.buffer_size = audio->str_cfg.buffer_size; + cfg_32.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) { + pr_err("%s: Copy to user failed\n", __func__); + rc = -EFAULT; + } + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + cfg_32.buffer_size, + cfg_32.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg_32; + struct msm_audio_stream_config cfg; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.buffer_size = cfg_32.buffer_size; + cfg.buffer_count = cfg_32.buffer_count; + /* Minimum single frame size, + * but with in maximum frames number + */ + if ((cfg.buffer_size < (audio->min_frame_size + + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + if (audio->opened) { + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s: session id %d:\n", + __func__, audio->ac->session); + pr_err("Buffer Alloc failed rc=%d\n", rc); + rc = -ENOMEM; + break; + } + } + audio->buf_alloc |= BUF_ALLOC_OUT; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_SET_BUF_CFG_32: { + struct msm_audio_buf_cfg32 cfg_32; + struct msm_audio_buf_cfg cfg; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_BUG_CFG_32 failed", + __func__); + rc = -EFAULT; + break; + } + cfg.meta_info_enable = cfg_32.meta_info_enable; + cfg.frames_per_buf = cfg_32.frames_per_buf; + + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size + */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG_32: { + struct msm_audio_buf_cfg32 cfg_32; + + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable; + cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf; + + if (copy_to_user((void *)arg, &cfg_32, + sizeof(struct msm_audio_buf_cfg32))) { + pr_err("%s: Copy to user failed\n", __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_GET_CONFIG_32: { + struct msm_audio_config32 cfg_32; + + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.buffer_size = audio->pcm_cfg.buffer_size; + cfg_32.buffer_count = audio->pcm_cfg.buffer_count; + cfg_32.channel_count = audio->pcm_cfg.channel_count; + cfg_32.sample_rate = audio->pcm_cfg.sample_rate; + cfg_32.type = audio->pcm_cfg.type; + cfg_32.meta_field = audio->pcm_cfg.meta_field; + cfg_32.bits = audio->pcm_cfg.bits; + + if (copy_to_user((void *)arg, &cfg_32, + sizeof(struct msm_audio_config32))) { + pr_err("%s: Copy to user failed\n", __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_CONFIG_32: { + struct msm_audio_config32 cfg_32; + struct msm_audio_config cfg; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.buffer_size = cfg_32.buffer_size; + cfg.buffer_count = cfg_32.buffer_count; + cfg.channel_count = cfg_32.channel_count; + cfg.sample_rate = cfg_32.sample_rate; + cfg.type = cfg_32.type; + cfg.meta_field = cfg_32.meta_field; + cfg.bits = cfg_32.bits; + rc = audio_in_set_config(file, &cfg); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_compat_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} +#endif + +ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + unsigned char *data; + uint32_t offset = 0; + uint32_t size = 0; + int rc = 0; + uint32_t idx; + struct meta_out_dsp meta; + uint32_t bytes_to_copy = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + (sizeof(unsigned char) + + (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf))); + + memset(&meta, 0, sizeof(meta)); + pr_debug("%s:session id %d: read - %zd\n", __func__, audio->ac->session, + count); + if (audio->reset_event) + return -ENETRESET; + + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->read_wait, + ((atomic_read(&audio->out_count) > 0) || + (audio->stopped) || + audio->rflush || audio->eos_rsp || + audio->event_abort)); + + if (audio->event_abort) { + rc = -EIO; + break; + } + + + if (rc < 0) + break; + + if ((audio->stopped && !(atomic_read(&audio->out_count))) || + audio->rflush) { + pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read", + __func__, + audio->ac->session); + rc = 0;/* End of File */ + break; + } + if (!(atomic_read(&audio->out_count)) && + (audio->eos_rsp == 1) && + (count >= (sizeof(unsigned char) + + sizeof(struct meta_out_dsp)))) { + unsigned char num_of_frames; + + pr_info("%s:session id %d: eos %d at output\n", + __func__, audio->ac->session, audio->eos_rsp); + if (buf != start) + break; + num_of_frames = 0xFF; + if (copy_to_user(buf, &num_of_frames, + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + buf += sizeof(unsigned char); + meta.frame_size = 0xFFFF; + meta.encoded_pcm_samples = 0xFFFF; + meta.msw_ts = 0x00; + meta.lsw_ts = 0x00; + meta.nflags = AUD_EOS_SET; + audio->eos_rsp = 0; + if (copy_to_user(buf, &meta, sizeof(meta))) { + rc = -EFAULT; + break; + } + buf += sizeof(meta); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac, + &size, &idx); + if ((count >= (size + mfield_size)) && data) { + if (audio->buf_cfg.meta_info_enable) { + if (copy_to_user(buf, + &audio->out_frame_info[idx][0], + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + bytes_to_copy = + (size + audio->out_frame_info[idx][1]); + /* Number of frames information copied */ + buf += sizeof(unsigned char); + count -= sizeof(unsigned char); + } else { + offset = audio->out_frame_info[idx][1]; + bytes_to_copy = size; + } + + pr_debug("%s:session id %d: offset=%d nr of frames= %d\n", + __func__, audio->ac->session, + audio->out_frame_info[idx][1], + audio->out_frame_info[idx][0]); + + if (copy_to_user(buf, &data[offset], bytes_to_copy)) { + rc = -EFAULT; + break; + } + count -= bytes_to_copy; + buf += bytes_to_copy; + } else { + pr_err("%s:session id %d: short read data[%pK] bytesavail[%d]bytesrequest[%zd]\n", + __func__, + audio->ac->session, + data, size, count); + } + atomic_dec(&audio->out_count); + q6asm_read(audio->ac); + break; + } + mutex_unlock(&audio->read_lock); + + pr_debug("%s:session id %d: read: %zd bytes\n", __func__, + audio->ac->session, (buf-start)); + if (buf > start) + return buf - start; + return rc; +} + +static int extract_meta_info(char *buf, unsigned long *msw_ts, + unsigned long *lsw_ts, unsigned int *flags) +{ + struct meta_in *meta = (struct meta_in *)buf; + *msw_ts = meta->ntimestamp.highpart; + *lsw_ts = meta->ntimestamp.lowpart; + *flags = meta->nflags; + return 0; +} + +ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + size_t xfer = 0; + char *cpy_ptr; + int rc = 0; + unsigned char *data; + uint32_t size = 0; + uint32_t idx = 0; + uint32_t nflags = 0; + unsigned long msw_ts = 0; + unsigned long lsw_ts = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + sizeof(struct meta_in); + + pr_debug("%s:session id %d: to write[%zd]\n", __func__, + audio->ac->session, count); + if (audio->reset_event) + return -ENETRESET; + + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->write_lock); + + while (count > 0) { + rc = wait_event_interruptible(audio->write_wait, + ((atomic_read(&audio->in_count) > 0) || + (audio->stopped) || + (audio->wflush) || (audio->event_abort))); + + if (audio->event_abort) { + rc = -EIO; + break; + } + + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + pr_debug("%s: session id %d: stop or flush\n", __func__, + audio->ac->session); + rc = -EBUSY; + break; + } + /* if no PCM data, might have only eos buffer + * such case do not hold cpu buffer + */ + if ((buf == start) && (count == mfield_size)) { + char eos_buf[sizeof(struct meta_in)]; + /* Processing beginning of user buffer */ + if (copy_from_user(eos_buf, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(eos_buf, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + /* send the EOS and return */ + pr_debug("%s:session id %d: send EOS 0x%8x\n", + __func__, + audio->ac->session, nflags); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac, + &size, &idx); + if (!data) { + pr_debug("%s:session id %d: No buf available\n", + __func__, audio->ac->session); + continue; + } + cpy_ptr = data; + if (audio->buf_cfg.meta_info_enable) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + count -= mfield_size; + } else { + pr_debug("%s:session id %d: continuous buffer\n", + __func__, audio->ac->session); + } + } + xfer = (count > (audio->pcm_cfg.buffer_size)) ? + (audio->pcm_cfg.buffer_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00); + if (rc < 0) { + rc = -EFAULT; + break; + } + atomic_dec(&audio->in_count); + count -= xfer; + buf += xfer; + } + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%pK] start[0x%pK]\n", + __func__, audio->ac->session, + nflags, buf, start); + if (nflags & AUD_EOS_SET) { + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_info("%s:session id %d: eos %d at input\n", __func__, + audio->ac->session, audio->eos_rsp); + } + pr_debug("%s:session id %d: Written %zd Avail Buf[%d]", __func__, + audio->ac->session, (buf - start - mfield_size), + atomic_read(&audio->in_count)); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +int audio_in_release(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = file->private_data; + + pr_info("%s: session id %d\n", __func__, audio->ac->session); + mutex_lock(&audio->lock); + audio_in_disable(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.h b/drivers/misc/qcom/qdsp6v2/audio_utils.h new file mode 100644 index 000000000000..f5517d833621 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2010-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include "q6audio_common.h" + +#define FRAME_NUM (8) + +#define PCM_BUF_COUNT (2) + +#define AUD_EOS_SET 0x01 +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define NO_BUF_ALLOC 0x00 +#define BUF_ALLOC_IN 0x01 +#define BUF_ALLOC_OUT 0x02 +#define BUF_ALLOC_INOUT 0x03 +#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095)) + +struct timestamp { + u32 lowpart; + u32 highpart; +} __packed; + +struct meta_in { + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct meta_out { + unsigned char num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +struct q6audio_in { + spinlock_t dsp_lock; + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_audio_stream_config str_cfg; + void *enc_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + /* number of buffers available to read/write */ + atomic_t in_count; + atomic_t out_count; + + /* first idx: num of frames per buf, second idx: offset to frame */ + uint32_t out_frame_info[FRAME_NUM][2]; + int eos_rsp; + int opened; + int enabled; + int stopped; + int event_abort; + int feedback; /* Flag indicates whether used + * in Non Tunnel mode + */ + int rflush; + int wflush; + int buf_alloc; + uint16_t min_frame_size; + uint16_t max_frames_per_buf; + bool reset_event; + long (*enc_ioctl)(struct file *, unsigned int, unsigned long); + long (*enc_compat_ioctl)(struct file *, unsigned int, unsigned long); +}; + +int audio_in_enable(struct q6audio_in *audio); +int audio_in_disable(struct q6audio_in *audio); +int audio_in_buf_alloc(struct q6audio_in *audio); +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long audio_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +#else +#define audio_in_compat_ioctl NULL +#endif +ssize_t audio_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos); +ssize_t audio_in_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +int audio_in_release(struct inode *inode, struct file *file); +int audio_in_set_config(struct file *file, struct msm_audio_config *cfg); diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c new file mode 100644 index 000000000000..dea063ddd0d0 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -0,0 +1,2132 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils_aio.h" +#ifdef CONFIG_USE_DEV_CTRL_VOLUME +#include +#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ +#ifdef CONFIG_DEBUG_FS +int audio_aio_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +ssize_t audio_aio_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct q6audio_aio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", list_empty(&audio->out_queue)); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} +#endif + +static long audio_aio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#ifdef CONFIG_COMPAT +static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#else +#define audio_aio_compat_ioctl NULL +#endif +int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *eos_buf = buf_node->kvaddr; + + pr_debug("%s[%pK]:insert_eos_buf\n", __func__, audio); + eos_buf->num_of_frames = 0xFFFFFFFF; + eos_buf->meta_out_dsp[0].offset_to_frame = 0x0; + eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET; + return sizeof(struct dec_meta_out) + + sizeof(eos_buf->meta_out_dsp[0]); +} + +/* Routine which updates read buffers of driver/dsp, + * for flush operation as DSP output might not have proper + * value set + */ +static int insert_meta_data_flush(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + + meta_data->num_of_frames = 0x0; + meta_data->meta_out_dsp[0].offset_to_frame = 0x0; + meta_data->meta_out_dsp[0].nflags = 0x0; + return sizeof(struct dec_meta_out) + + sizeof(meta_data->meta_out_dsp[0]); +} + +static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr, + unsigned long len, + struct audio_aio_ion_region **region) +{ + struct audio_aio_ion_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registered + * ion buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + pr_err("%s[%pK]:multiple hits for vaddr %pK, len %ld\n", + __func__, audio, addr, len); + list_for_each_entry(region_elt, &audio->ion_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_err("\t%s[%pK]:%pK, %ld --> %pK\n", + __func__, audio, + region_elt->vaddr, + region_elt->len, + ®ion_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr, + unsigned long len, int ref_up, void **kvaddr) +{ + struct audio_aio_ion_region *region; + phys_addr_t paddr; + int ret; + + ret = audio_aio_ion_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_err("%s[%pK]:lookup (%pK, %ld) failed\n", + __func__, audio, addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + pr_debug("%s[%pK]:found region %pK ref_cnt %d\n", + __func__, audio, region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + /* provide kernel virtual address for accessing meta information */ + if (kvaddr) + *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr)); + return paddr; +} + +static int audio_aio_pause(struct q6audio_aio *audio) +{ + int rc = -EINVAL; + + pr_debug("%s[%pK], enabled = %d\n", __func__, audio, + audio->enabled); + if (audio->enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s[%pK]: pause cmd failed rc=%d\n", + __func__, audio, rc); + + if (rc == 0) { + /* Send suspend only if pause was successful */ + rc = q6asm_cmd(audio->ac, CMD_SUSPEND); + if (rc < 0) + pr_err("%s[%pK]: suspend cmd failed rc=%d\n", + __func__, audio, rc); + } else + pr_err("%s[%pK]: not sending suspend since pause failed\n", + __func__, audio); + + } else + pr_err("%s[%pK]: Driver not enabled\n", __func__, audio); + return rc; +} + +static int audio_aio_flush(struct q6audio_aio *audio) +{ + int rc = 0; + + if (audio->enabled) { + /* Implicitly issue a pause to the decoder before flushing if + * it is not in pause state + */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audio_aio_pause(audio); + if (rc < 0) + pr_err("%s[%pK}: pause cmd failed rc=%d\n", + __func__, audio, + rc); + else + audio->drv_status |= ADRV_STATUS_PAUSE; + } + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_err("%s[%pK]: flush cmd failed rc=%d\n", + __func__, audio, rc); + /* Not in stop state, reenable the stream */ + if (audio->stopped == 0) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%pK]:audio re-enable failed\n", + __func__, audio); + else { + audio->enabled = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + } + pr_debug("%s[%pK]:in_bytes %d\n", + __func__, audio, atomic_read(&audio->in_bytes)); + pr_debug("%s[%pK]:in_samples %d\n", + __func__, audio, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return rc; +} + +static int audio_aio_outport_flush(struct q6audio_aio *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH); + if (rc < 0) + pr_err("%s[%pK}: output port flush cmd failed rc=%d\n", + __func__, audio, rc); + return rc; +} + +/* Write buffer to DSP / Handle Ack from DSP */ +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *used_buf; + + /* No active flush in progress */ + if (audio->wflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (list_empty(&audio->out_queue)) { + pr_warn("%s: ignore unexpected event from dsp\n", __func__); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return; + } + used_buf = list_first_entry(&audio->out_queue, + struct audio_aio_buffer_node, list); + if (token == used_buf->token) { + list_del(&used_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_debug("%s[%pK]:consumed buffer\n", __func__, audio); + event_payload.aio_buf = used_buf->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(used_buf); + if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s[%pK]: list is empty, reached EOS in Tunnel\n", + __func__, audio); + wake_up(&audio->write_wait); + } + } else { + pr_err("%s[%pK]:expected=%x ret=%x\n", + __func__, audio, used_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* ------------------- device --------------------- */ +void audio_aio_async_out_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + pr_debug("%s[%pK}\n", __func__, audio); + /* EOS followed by flush, EOS response not guranteed, free EOS i/p + * buffer + */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) { + pr_debug("%s[%pK]: EOS followed by flush received,acknowledge", + "eos i/p buffer immediately\n", __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload, 0, + sizeof(union msm_audio_event_payload)); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload); + kfree(buf_node); + pr_debug("%s[%pK]: Propagate WRITE_DONE during flush\n", + __func__, audio); + } +} + +void audio_aio_async_in_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + pr_debug("%s[%pK]\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + /* Forcefull send o/p eos buffer after flush, if no eos response + * received by dsp even after sending eos command + */ + if ((audio->eos_rsp != 1) && audio->eos_flag) { + pr_debug("%s[%pK]: send eos on o/p buffer during flush\n", + __func__, audio); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + audio->eos_flag = 0; + } else { + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_meta_data_flush(audio, buf_node); + } + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload); + kfree(buf_node); + pr_debug("%s[%pK]: Propagate READ_DONE during flush\n", + __func__, audio); + } +} + +int audio_aio_enable(struct q6audio_aio *audio) +{ + /* 2nd arg: 0 -> run immediately + * 3rd arg: 0 -> msw_ts, + * 4th arg: 0 ->lsw_ts + */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +int audio_aio_disable(struct q6audio_aio *audio) +{ + int rc = 0; + + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s[%pK]: inbytes[%d] insamples[%d]\n", __func__, + audio, atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + /* Close the session */ + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s[%pK]:Failed to close the session rc=%d\n", + __func__, audio, rc); + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + pr_debug("%s[%pK]:enabled[%d]\n", __func__, audio, audio->enabled); + return rc; +} + +void audio_aio_reset_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + list_del(®ion->list); + msm_audio_ion_free_legacy(audio->client, region->handle); + kfree(region); + } +} + +void audio_aio_reset_event_queue(struct q6audio_aio *audio) +{ + unsigned long flags; + struct audio_aio_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); +} + +static void audio_aio_unmap_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%pK]:\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + if (region != NULL) { + pr_debug("%s[%pK]: phy_address = 0x%pK\n", + __func__, audio, ®ion->paddr); + rc = q6asm_memory_unmap(audio->ac, + region->paddr, IN); + if (rc < 0) + pr_err("%s[%pK]: memory unmap failed\n", + __func__, audio); + } + } +} + +#ifdef CONFIG_USE_DEV_CTRL_VOLUME + +static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct q6audio_aio *audio = (struct q6audio_aio *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->volume = evt_payload->session_vol; + pr_debug("%s[%pK]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n", + __func__, audio, audio->volume, audio->enabled); + if (audio->enabled == 1) { + if (audio->ac) { + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s[%pK]: Send Volume command failed rc=%d\n", + __func__, audio, rc); + } + } + } + break; + default: + pr_err("%s[%pK]:ERROR:wrong event\n", __func__, audio); + break; + } +} + +int register_volume_listener(struct q6audio_aio *audio) +{ + int rc = 0; + + audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->ac->session, + audio_aio_listner, + (void *)audio); + if (rc < 0) { + pr_err("%s[%pK]: Event listener failed\n", __func__, audio); + rc = -EACCES; + } + return rc; +} +void unregister_volume_listener(struct q6audio_aio *audio) +{ + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session); +} + +int enable_volume_ramp(struct q6audio_aio *audio) +{ + int rc = 0; + struct asm_softpause_params softpause; + struct asm_softvolume_params softvol; + + if (audio->ac == NULL) + return -EINVAL; + pr_debug("%s[%pK]\n", __func__, audio); + softpause.enable = SOFT_PAUSE_ENABLE; + softpause.period = SOFT_PAUSE_PERIOD; + softpause.step = SOFT_PAUSE_STEP; + softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR; + + softvol.period = SOFT_VOLUME_PERIOD; + softvol.step = SOFT_VOLUME_STEP; + softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR; + + if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR) + softpause.step = SOFT_PAUSE_STEP_LINEAR; + if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR) + softvol.step = SOFT_VOLUME_STEP_LINEAR; + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + return rc; + } + rc = q6asm_set_softpause(audio->ac, &softpause); + if (rc < 0) { + pr_err("%s: Send SoftPause Param failed rc=%d\n", + __func__, rc); + return rc; + } + rc = q6asm_set_softvolume(audio->ac, &softvol); + if (rc < 0) { + pr_err("%s: Send SoftVolume Param failed rc=%d\n", + __func__, rc); + return rc; + } + /* disable mute by default */ + rc = q6asm_set_mute(audio->ac, 0); + if (rc < 0) { + pr_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + return rc; + } + return rc; +} + +#else /*CONFIG_USE_DEV_CTRL_VOLUME*/ +int register_volume_listener(struct q6audio_aio *audio) +{ + return 0;/* do nothing */ +} +void unregister_volume_listener(struct q6audio_aio *audio) +{ + return;/* do nothing */ +} +int enable_volume_ramp(struct q6audio_aio *audio) +{ + return 0; /* do nothing */ +} +#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ + +int audio_aio_release(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = file->private_data; + + pr_debug("%s[%pK]\n", __func__, audio); + mutex_lock(&audio->lock); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + audio->wflush = 1; + if (audio->wakelock_voted && + (audio->audio_ws_mgr != NULL) && + (audio->miscdevice != NULL)) { + audio->wakelock_voted = false; + mutex_lock(&audio->audio_ws_mgr->ws_lock); + if ((audio->audio_ws_mgr->ref_cnt > 0) && + (--audio->audio_ws_mgr->ref_cnt == 0)) { + pm_relax(audio->miscdevice->this_device); + } + mutex_unlock(&audio->audio_ws_mgr->ws_lock); + } + if (audio->enabled) + audio_aio_flush(audio); + audio->wflush = 0; + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audio_aio_disable(audio); + audio_aio_unmap_ion_region(audio); + audio_aio_reset_ion_region(audio); + msm_audio_ion_client_destroy(audio->client); + audio->event_abort = 1; + wake_up(&audio->event_wait); + audio_aio_reset_event_queue(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + mutex_unlock(&audio->lock); + mutex_destroy(&audio->lock); + mutex_destroy(&audio->read_lock); + mutex_destroy(&audio->write_lock); + mutex_destroy(&audio->get_event_lock); + unregister_volume_listener(audio); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove(audio->dentry); +#endif + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} + +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + int rc = 0; + struct q6audio_aio *audio = file->private_data; + + if (!audio->enabled || audio->feedback) + return -EINVAL; + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + pr_debug("%s[%pK]:\n", __func__, audio); + + audio->eos_rsp = 0; + + pr_debug("%s[%pK]Wait for write done from DSP\n", __func__, audio); + rc = wait_event_interruptible(audio->write_wait, + (list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) { + pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n" + , __func__, audio); + audio->wflush = 0; + rc = -EBUSY; + } + + if (rc < 0) { + pr_err("%s[%pK]: wait event for list_empty failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio); + + if (rc < 0) + pr_err("%s[%pK]: q6asm_cmd failed, rc = %d", + __func__, audio, rc); + + pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n" + , __func__, audio); + rc = wait_event_interruptible(audio->write_wait, + (audio->eos_rsp || audio->wflush || + audio->stopped)); + + if (rc < 0) { + pr_err("%s[%pK]: wait event for eos_rsp failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + if (audio->stopped || audio->wflush) { + audio->wflush = 0; + pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n" + , __func__, audio); + rc = -EBUSY; + } + + if (audio->eos_rsp == 1) + pr_debug("%s[%pK]: EOS\n", __func__, audio); + + +done: + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +static int audio_aio_events_pending(struct q6audio_aio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort || audio->reset_event; +} + +static long audio_aio_process_event_req_common(struct q6audio_aio *audio, + struct msm_audio_event *usr_evt) +{ + long rc; + struct audio_aio_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + timeout = usr_evt->timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audio_aio_events_pending + (audio), + msecs_to_jiffies + (timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible(audio->event_wait, + audio_aio_events_pending(audio)); + } + if (rc < 0) + return rc; + + if (audio->reset_event) { + audio->reset_event = false; + pr_err("In SSR, post ENETRESET err\n"); + return -ENETRESET; + } + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt->event_type = drv_evt->event_type; + usr_evt->event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + pr_err("%s[%pK]:Unexpected path\n", __func__, audio); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return -EPERM; + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + pr_debug("%s[%pK]:posted AUDIO_EVENT_WRITE_DONE to user\n", + __func__, audio); + mutex_lock(&audio->write_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->write_lock); + } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("%s[%pK]:posted AUDIO_EVENT_READ_DONE to user\n", + __func__, audio); + mutex_lock(&audio->read_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->read_lock); + } + + /* Some read buffer might be held up in DSP,release all + * Once EOS indicated + */ + if (audio->eos_rsp && !list_empty(&audio->in_queue)) { + pr_debug("%s[%pK]:Send flush command to release read buffers", + "held up in DSP\n", __func__, audio); + mutex_lock(&audio->lock); + audio_aio_flush(audio); + mutex_unlock(&audio->lock); + } + + return rc; +} + +static long audio_aio_process_event_req(struct q6audio_aio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) { + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + rc = audio_aio_process_event_req_common(audio, &usr_evt); + + if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) { + pr_err("%s: copy_to_user failed\n", __func__); + rc = -EFAULT; + } + return rc; +} + +#ifdef CONFIG_COMPAT + +struct msm_audio_aio_buf32 { + compat_uptr_t buf_addr; + u32 buf_len; + u32 data_len; + compat_uptr_t private_data; + u16 mfield_sz; /*only useful for data has meta field */ +}; + +struct msm_audio_bitstream_info32 { + u32 codec_type; + u32 chan_info; + u32 sample_rate; + u32 bit_stream_info; + u32 bit_rate; + u32 unused[3]; +}; + +struct msm_audio_bitstream_error_info32 { + u32 dec_id; + u32 err_msg_indicator; + u32 err_type; +}; + +union msm_audio_event_payload32 { + struct msm_audio_aio_buf32 aio_buf; + struct msm_audio_bitstream_info32 stream_info; + struct msm_audio_bitstream_error_info32 error_info; + s32 reserved; +}; + +struct msm_audio_event32 { + s32 event_type; + s32 timeout_ms; + union msm_audio_event_payload32 event_payload; +}; + +static long audio_aio_process_event_req_compat(struct q6audio_aio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event32 usr_evt_32; + struct msm_audio_event usr_evt; + + if (copy_from_user(&usr_evt_32, arg, + sizeof(struct msm_audio_event32))) { + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + usr_evt.timeout_ms = usr_evt_32.timeout_ms; + + rc = audio_aio_process_event_req_common(audio, &usr_evt); + + usr_evt_32.event_type = usr_evt.event_type; + switch (usr_evt_32.event_type) { + case AUDIO_EVENT_SUSPEND: + case AUDIO_EVENT_RESUME: + case AUDIO_EVENT_WRITE_DONE: + case AUDIO_EVENT_READ_DONE: + usr_evt_32.event_payload.aio_buf.buf_addr = + ptr_to_compat(usr_evt.event_payload.aio_buf.buf_addr); + usr_evt_32.event_payload.aio_buf.buf_len = + usr_evt.event_payload.aio_buf.buf_len; + usr_evt_32.event_payload.aio_buf.data_len = + usr_evt.event_payload.aio_buf.data_len; + usr_evt_32.event_payload.aio_buf.private_data = + ptr_to_compat(usr_evt.event_payload.aio_buf.private_data); + usr_evt_32.event_payload.aio_buf.mfield_sz = + usr_evt.event_payload.aio_buf.mfield_sz; + break; + case AUDIO_EVENT_STREAM_INFO: + usr_evt_32.event_payload.stream_info.codec_type = + usr_evt.event_payload.stream_info.codec_type; + usr_evt_32.event_payload.stream_info.chan_info = + usr_evt.event_payload.stream_info.chan_info; + usr_evt_32.event_payload.stream_info.sample_rate = + usr_evt.event_payload.stream_info.sample_rate; + usr_evt_32.event_payload.stream_info.bit_stream_info = + usr_evt.event_payload.stream_info.bit_stream_info; + usr_evt_32.event_payload.stream_info.bit_rate = + usr_evt.event_payload.stream_info.bit_rate; + break; + case AUDIO_EVENT_BITSTREAM_ERROR_INFO: + usr_evt_32.event_payload.error_info.dec_id = + usr_evt.event_payload.error_info.dec_id; + usr_evt_32.event_payload.error_info.err_msg_indicator = + usr_evt.event_payload.error_info.err_msg_indicator; + usr_evt_32.event_payload.error_info.err_type = + usr_evt.event_payload.error_info.err_type; + break; + default: + pr_debug("%s: unknown audio event type = %d rc = %ld", + __func__, usr_evt_32.event_type, rc); + return rc; + } + if (copy_to_user(arg, &usr_evt_32, sizeof(usr_evt_32))) { + pr_err("%s: copy_to_user failed\n", __func__); + rc = -EFAULT; + } + return rc; +} +#endif + +static int audio_aio_ion_check(struct q6audio_aio *audio, + void *vaddr, unsigned long len) +{ + struct audio_aio_ion_region *region_elt; + struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_err("%s[%pK]:region (vaddr %pK len %ld) clashes with registered region (vaddr %pK paddr %pK len %ld)\n", + __func__, audio, vaddr, len, + region_elt->vaddr, + ®ion_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audio_aio_ion_add(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + ion_phys_addr_t paddr = 0; + size_t len = 0; + struct audio_aio_ion_region *region; + int rc = -EINVAL; + struct ion_handle *handle = NULL; + unsigned long ionflag; + void *kvaddr = NULL; + + pr_debug("%s[%pK]:\n", __func__, audio); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + rc = msm_audio_ion_import_legacy("Audio_Dec_Client", audio->client, + &handle, info->fd, &ionflag, + 0, &paddr, &len, &kvaddr); + if (rc) { + pr_err("%s: msm audio ion alloc failed\n", __func__); + goto import_error; + } + + rc = audio_aio_ion_check(audio, info->vaddr, len); + if (rc < 0) { + pr_err("%s: audio_aio_ion_check failed\n", __func__); + goto ion_error; + } + + region->handle = handle; + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->ref_cnt = 0; + pr_debug("%s[%pK]:add region paddr %pK vaddr %pK, len %lu kvaddr %pK\n", + __func__, audio, + ®ion->paddr, region->vaddr, region->len, + region->kvaddr); + list_add_tail(®ion->list, &audio->ion_region_queue); + rc = q6asm_memory_map(audio->ac, paddr, IN, len, 1); + if (rc < 0) { + pr_err("%s[%pK]: memory map failed\n", __func__, audio); + goto mmap_error; + } else { + goto end; + } +mmap_error: + list_del(®ion->list); +ion_error: + msm_audio_ion_free_legacy(audio->client, handle); +import_error: + kfree(region); +end: + return rc; +} + +static int audio_aio_ion_remove(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%pK]:info fd %d vaddr %pK\n", + __func__, audio, info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("%s[%pK]:region %pK in use ref_cnt %d\n", + __func__, audio, region, + region->ref_cnt); + break; + } + pr_debug("%s[%pK]:remove region fd %d vaddr %pK\n", + __func__, audio, info->fd, info->vaddr); + rc = q6asm_memory_unmap(audio->ac, + region->paddr, IN); + if (rc < 0) + pr_err("%s[%pK]: memory unmap failed\n", + __func__, audio); + + list_del(®ion->list); + msm_audio_ion_free_legacy(audio->client, + region->handle); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audio_aio_async_write(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + int rc; + struct audio_client *ac; + struct audio_aio_write_param param; + + if (!audio || !buf_node) { + pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n", + __func__, audio, buf_node); + return -EINVAL; + } + pr_debug("%s[%pK]: Send write buff %pK phy %pK len %d meta_enable = %d\n", + __func__, audio, buf_node, &buf_node->paddr, + buf_node->buf.data_len, + audio->buf_cfg.meta_info_enable); + pr_debug("%s[%pK]: flags = 0x%x\n", __func__, audio, + buf_node->meta_info.meta_in.nflags); + + ac = audio->ac; + /* Offset with appropriate meta */ + if (audio->feedback) { + /* Non Tunnel mode */ + param.paddr = buf_node->paddr + sizeof(struct dec_meta_in); + param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in); + } else { + /* Tunnel mode */ + param.paddr = buf_node->paddr; + param.len = buf_node->buf.data_len; + } + param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart; + param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart; + param.flags = buf_node->meta_info.meta_in.nflags; + /* If no meta_info enaled, indicate no time stamp valid */ + if (!audio->buf_cfg.meta_info_enable) + param.flags = 0xFF00; + + if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET) + param.flags |= AUDIO_DEC_EOF_SET; + + param.uid = ac->session; + /* Read command will populate session id as token */ + buf_node->token = ac->session; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_err("%s[%pK]:failed\n", __func__, audio); + return rc; +} + +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audio_aio_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC); + if (!e_node) { + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static int audio_aio_async_read(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct audio_client *ac; + struct audio_aio_read_param param; + int rc; + + pr_debug("%s[%pK]: Send read buff %pK phy %pK len %d\n", + __func__, audio, buf_node, + &buf_node->paddr, buf_node->buf.buf_len); + ac = audio->ac; + /* Provide address so driver can append nr frames information */ + param.paddr = buf_node->paddr + + sizeof(struct dec_meta_out); + param.len = buf_node->buf.buf_len - + sizeof(struct dec_meta_out); + param.uid = ac->session; + /* Write command will populate session_id as token */ + buf_node->token = ac->session; + rc = q6asm_async_read(ac, ¶m); + if (rc < 0) + pr_err("%s[%pK]:failed\n", __func__, audio); + return rc; +} + +static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir, + struct audio_aio_buffer_node *buf_node) +{ + unsigned long flags; + int ret = 0; + + pr_debug("%s[%pK]:node %pK dir %x buf_addr %pK buf_len %d data_len %d\n", + __func__, audio, buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1, + &buf_node->kvaddr); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (!audio->feedback && !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + extract_meta_out_info(audio, buf_node, 1); + /* Not a EOS buffer */ + if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) { + spin_lock_irqsave(&audio->dsp_lock, flags); + ret = audio_aio_async_write(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } else if (buf_node->meta_info.meta_in.nflags + & AUDIO_DEC_EOS_SET) { + if (!audio->wflush) { + pr_debug("%s[%pK]:Send EOS cmd at i/p\n", + __func__, audio); + /* Driver will forcefully post writedone event + * once eos ack recived from DSP + */ + audio->eos_write_payload.aio_buf = + buf_node->buf; + audio->eos_flag = 1; + audio->eos_rsp = 0; + q6asm_cmd(audio->ac, CMD_EOS); + kfree(buf_node); + } else { /* Flush in progress, send back i/p + * EOS buffer as is + */ + union msm_audio_event_payload event_payload; + + event_payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(buf_node); + } + } + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + /* No EOS reached */ + if (!audio->eos_rsp) { + spin_lock_irqsave(&audio->dsp_lock, flags); + ret = audio_aio_async_read(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->in_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + /* EOS reached at input side fake all upcoming read buffer to + * indicate the same + */ + else { + union msm_audio_event_payload event_payload; + + event_payload.aio_buf = buf_node->buf; + event_payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + pr_debug("%s[%pK]: propagate READ_DONE as EOS done\n", + __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(buf_node); + } + } + return ret; +} +#ifdef CONFIG_COMPAT +static int audio_aio_buf_add_compat(struct q6audio_aio *audio, u32 dir, + void __user *arg) +{ + struct audio_aio_buffer_node *buf_node; + struct msm_audio_aio_buf32 aio_buf_32; + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&aio_buf_32, arg, sizeof(aio_buf_32))) { + kfree(buf_node); + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + buf_node->buf.buf_addr = compat_ptr(aio_buf_32.buf_addr); + buf_node->buf.buf_len = aio_buf_32.buf_len; + buf_node->buf.data_len = aio_buf_32.data_len; + buf_node->buf.private_data = compat_ptr(aio_buf_32.private_data); + buf_node->buf.mfield_sz = aio_buf_32.mfield_sz; + + return audio_aio_buf_add_shared(audio, dir, buf_node); +} +#endif + +static int audio_aio_buf_add(struct q6audio_aio *audio, u32 dir, + void __user *arg) +{ + struct audio_aio_buffer_node *buf_node; + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + return audio_aio_buf_add_shared(audio, dir, buf_node); +} + +void audio_aio_ioport_reset(struct q6audio_aio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%pK]:fsync in progress\n", + __func__, audio); + audio->drv_ops.out_flush(audio); + } else + audio->drv_ops.out_flush(audio); + if (audio->feedback == NON_TUNNEL_MODE) + audio->drv_ops.in_flush(audio); + } +} + +int audio_aio_open(struct q6audio_aio *audio, struct file *file) +{ + int rc = 0; + int i; + struct audio_aio_event *e_node = NULL; + struct list_head *ptr, *next; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 48000; + audio->pcm_cfg.channel_count = 2; + + /* Only AIO interface */ + if (file->f_flags & O_NONBLOCK) { + pr_debug("%s[%pK]:set to aio interface\n", __func__, audio); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.out_flush = audio_aio_async_out_flush; + audio->drv_ops.in_flush = audio_aio_async_in_flush; + q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + } else { + pr_err("%s[%pK]:SIO interface not supported\n", + __func__, audio); + rc = -EACCES; + goto fail; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->cmd_wait); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->event_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->ion_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + + audio->drv_ops.out_flush(audio); + audio->opened = 1; + audio->reset_event = false; + file->private_data = audio; + audio->codec_ioctl = audio_aio_ioctl; + audio->codec_compat_ioctl = audio_aio_compat_ioctl; + for (i = 0; i < AUDIO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + rc = -ENOMEM; + goto cleanup; + } + } + audio->client = msm_audio_ion_client_create("Audio_Dec_Client"); + if (IS_ERR_OR_NULL(audio->client)) { + pr_err("Unable to create ION client\n"); + rc = -ENOMEM; + goto cleanup; + } + pr_debug("Ion client create in audio_aio_open %pK", audio->client); + + rc = register_volume_listener(audio); + if (rc < 0) + goto ion_cleanup; + + return 0; +ion_cleanup: + msm_audio_ion_client_destroy(audio->client); + audio->client = NULL; +cleanup: + list_for_each_safe(ptr, next, &audio->free_event_queue) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + kfree(e_node); + } +fail: + return rc; +} + +static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_ABORT_GET_EVENT: { + audio->event_abort = 1; + wake_up(&audio->event_wait); + break; + } + case AUDIO_OUTPORT_FLUSH: { + pr_debug("%s[%pK]:AUDIO_OUTPORT_FLUSH\n", __func__, audio); + mutex_lock(&audio->read_lock); + rc = audio_aio_outport_flush(audio); + if (rc < 0) { + pr_err("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n", + __func__, audio); + rc = -EINTR; + } + mutex_unlock(&audio->read_lock); + break; + } + case AUDIO_STOP: { + pr_debug("%s[%pK]: AUDIO_STOP session_id[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->stopped = 1; + rc = audio_aio_flush(audio); + if (rc < 0) { + pr_err("%s[%pK]:Audio Stop procedure failed rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + audio->enabled = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%pK] Waking up the audio_aio_fsync\n", + __func__, audio); + wake_up(&audio->write_wait); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PAUSE: { + pr_debug("%s[%pK]:AUDIO_PAUSE %ld\n", __func__, audio, arg); + mutex_lock(&audio->lock); + if (arg == 1) { + rc = audio_aio_pause(audio); + if (rc < 0) { + pr_err("%s[%pK]: pause FAILED rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%pK]: audio enable failed\n", + __func__, audio); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_FLUSH: { + pr_debug("%s[%pK]: AUDIO_FLUSH sessionid[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->rflush = 1; + audio->wflush = 1; + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%pK] Waking up the audio_aio_fsync\n", + __func__, audio); + wake_up(&audio->write_wait); + } + /* Flush DSP */ + rc = audio_aio_flush(audio); + /* Flush input / Output buffer in software*/ + audio_aio_ioport_reset(audio); + if (rc < 0) { + pr_err("%s[%pK]:AUDIO_FLUSH interrupted\n", + __func__, audio); + rc = -EINTR; + } else { + audio->rflush = 0; + if (audio->drv_status & ADRV_STATUS_FSYNC) + wake_up(&audio->write_wait); + else + audio->wflush = 0; + + } + audio->eos_flag = 0; + audio->eos_rsp = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_SESSION_ID: { + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->ac->session, + sizeof(u16))) { + pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PM_AWAKE: { + if ((audio->audio_ws_mgr == NULL) || + (audio->miscdevice == NULL)) { + pr_err("%s[%pK]: invalid ws_mgr or miscdevice", + __func__, audio); + rc = -EACCES; + break; + } + pr_debug("%s[%pK]:AUDIO_PM_AWAKE\n", __func__, audio); + mutex_lock(&audio->lock); + if (!audio->wakelock_voted) { + audio->wakelock_voted = true; + mutex_lock(&audio->audio_ws_mgr->ws_lock); + if (audio->audio_ws_mgr->ref_cnt++ == 0) + pm_stay_awake(audio->miscdevice->this_device); + mutex_unlock(&audio->audio_ws_mgr->ws_lock); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PM_RELAX: { + if ((audio->audio_ws_mgr == NULL) || + (audio->miscdevice == NULL)) { + pr_err("%s[%pK]: invalid ws_mgr or miscdevice", + __func__, audio); + rc = -EACCES; + break; + } + pr_debug("%s[%pK]:AUDIO_PM_RELAX\n", __func__, audio); + mutex_lock(&audio->lock); + if (audio->wakelock_voted) { + audio->wakelock_voted = false; + mutex_lock(&audio->audio_ws_mgr->ws_lock); + if ((audio->audio_ws_mgr->ref_cnt > 0) && + (--audio->audio_ws_mgr->ref_cnt == 0)) { + pm_relax(audio->miscdevice->this_device); + } + mutex_unlock(&audio->audio_ws_mgr->ws_lock); + } + mutex_unlock(&audio->lock); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; + + +} + +static long audio_aio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_ABORT_GET_EVENT: + case AUDIO_OUTPORT_FLUSH: + case AUDIO_STOP: + case AUDIO_PAUSE: + case AUDIO_FLUSH: + case AUDIO_GET_SESSION_ID: + case AUDIO_PM_AWAKE: + case AUDIO_PM_RELAX: + rc = audio_aio_shared_ioctl(file, cmd, arg); + break; + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + uint64_t timestamp; + + memset(&stats, 0, sizeof(struct msm_audio_stats)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + rc = q6asm_get_session_time(audio->ac, ×tamp); + if (rc >= 0) + memcpy(&stats.unused[0], ×tamp, sizeof(timestamp)); + else + pr_debug("Error while getting timestamp\n"); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) { + pr_err("%s: copy_frm_user for AUDIO_GET_STATS failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_GET_EVENT: { + pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audio_aio_process_event_req(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + break; + } + case AUDIO_ASYNC_WRITE: { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audio_aio_buf_add(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + break; + } + case AUDIO_ASYNC_READ: { + mutex_lock(&audio->read_lock); + if (audio->feedback) + rc = audio_aio_buf_add(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + break; + } + + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + mutex_lock(&audio->lock); + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("%s[%pK]:GET STREAM CFG %d %d\n", + __func__, audio, cfg.buffer_size, cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_STREAM_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + + pr_err("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n", + __func__, audio); + rc = -EACCES; + mutex_unlock(&audio->lock); + break; + } + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_GET_BUF CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]", + __func__, audio, + audio->ac->session, cfg.meta_info_enable); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_BUF_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_REGISTER_ION: { + struct msm_audio_ion_info info; + + pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&info, (void *)arg, sizeof(info))) { + pr_err( + "%s: copy_from_user for AUDIO_REGISTER_ION failed\n", + __func__); + rc = -EFAULT; + } else { + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_DEREGISTER_ION: { + struct msm_audio_ion_info info; + + mutex_lock(&audio->lock); + pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio); + if (copy_from_user(&info, (void *)arg, sizeof(info))) { + pr_err( + "%s: copy_from_user for AUDIO_DEREGISTER_ION failed\n", + __func__); + rc = -EFAULT; + } else { + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_stream_config32 { + u32 buffer_size; + u32 buffer_count; +}; + +struct msm_audio_stats32 { + u32 byte_count; + u32 sample_count; + u32 unused[2]; +}; + +struct msm_audio_config32 { + u32 buffer_size; + u32 buffer_count; + u32 channel_count; + u32 sample_rate; + u32 type; + u32 meta_field; + u32 bits; + u32 unused[3]; +}; + +struct msm_audio_buf_cfg32 { + u32 meta_info_enable; + u32 frames_per_buf; +}; + +struct msm_audio_ion_info32 { + int fd; + compat_uptr_t vaddr; +}; + +enum { + AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3, + struct msm_audio_config32), + AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4, + struct msm_audio_config32), + AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5, + struct msm_audio_stats32), + AUDIO_GET_EVENT_32 = _IOR(AUDIO_IOCTL_MAGIC, 13, + struct msm_audio_event32), + AUDIO_ASYNC_WRITE_32 = _IOW(AUDIO_IOCTL_MAGIC, 17, + struct msm_audio_aio_buf32), + AUDIO_ASYNC_READ_32 = _IOW(AUDIO_IOCTL_MAGIC, 18, + struct msm_audio_aio_buf32), + AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80, + struct msm_audio_stream_config32), + AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81, + struct msm_audio_stream_config32), + AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93, + struct msm_audio_buf_cfg32), + AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94, + struct msm_audio_buf_cfg32), + AUDIO_REGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 97, + struct msm_audio_ion_info32), + AUDIO_DEREGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 98, + struct msm_audio_ion_info32), +}; + +static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_ABORT_GET_EVENT: + case AUDIO_OUTPORT_FLUSH: + case AUDIO_STOP: + case AUDIO_PAUSE: + case AUDIO_FLUSH: + case AUDIO_GET_SESSION_ID: + case AUDIO_PM_AWAKE: + case AUDIO_PM_RELAX: + rc = audio_aio_shared_ioctl(file, cmd, arg); + break; + case AUDIO_GET_STATS_32: { + struct msm_audio_stats32 stats; + uint64_t timestamp; + + memset(&stats, 0, sizeof(struct msm_audio_stats32)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + rc = q6asm_get_session_time(audio->ac, ×tamp); + if (rc >= 0) + memcpy(&stats.unused[0], ×tamp, sizeof(timestamp)); + else + pr_debug("Error while getting timestamp\n"); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_STATS_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_GET_EVENT_32: { + pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audio_aio_process_event_req_compat(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + break; + } + case AUDIO_ASYNC_WRITE_32: { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audio_aio_buf_add_compat(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + break; + } + case AUDIO_ASYNC_READ_32: { + mutex_lock(&audio->read_lock); + if (audio->feedback) + rc = audio_aio_buf_add_compat(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + break; + } + + case AUDIO_GET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg; + + mutex_lock(&audio->lock); + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("%s[%pK]:GET STREAM CFG %d %d\n", + __func__, audio, cfg.buffer_size, cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) { + pr_err("%s: copy_to_user for AUDIO_GET_STREAM_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg_32; + struct msm_audio_stream_config cfg; + + pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + cfg.buffer_size = cfg_32.buffer_size; + cfg.buffer_count = cfg_32.buffer_count; + + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_CONFIG_32: { + struct msm_audio_config32 cfg_32; + + mutex_lock(&audio->lock); + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.buffer_size = audio->pcm_cfg.buffer_size; + cfg_32.buffer_count = audio->pcm_cfg.buffer_count; + cfg_32.channel_count = audio->pcm_cfg.channel_count; + cfg_32.sample_rate = audio->pcm_cfg.sample_rate; + cfg_32.type = audio->pcm_cfg.type; + cfg_32.meta_field = audio->pcm_cfg.meta_field; + cfg_32.bits = audio->pcm_cfg.bits; + + if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_CONFIG_32: { + struct msm_audio_config config; + struct msm_audio_config32 config_32; + + mutex_lock(&audio->lock); + + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n", + __func__, audio); + rc = -EACCES; + mutex_unlock(&audio->lock); + break; + } + pr_err("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio); + if (copy_from_user(&config_32, (void *)arg, + sizeof(config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + config.buffer_size = config_32.buffer_size; + config.buffer_count = config_32.buffer_count; + config.channel_count = config_32.channel_count; + config.sample_rate = config_32.sample_rate; + config.type = config_32.type; + config.meta_field = config_32.meta_field; + config.bits = config_32.bits; + + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_BUF_CFG_32: { + struct msm_audio_buf_cfg cfg; + struct msm_audio_buf_cfg32 cfg_32; + + mutex_lock(&audio->lock); + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + cfg.meta_info_enable = cfg_32.meta_info_enable; + cfg.frames_per_buf = cfg_32.frames_per_buf; + + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]", + __func__, audio, + audio->ac->session, cfg.meta_info_enable); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_BUF_CFG_32: { + struct msm_audio_buf_cfg32 cfg_32; + + pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + mutex_lock(&audio->lock); + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable; + cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(struct msm_audio_buf_cfg32))) { + pr_err("%s: copy_to_user for AUDIO_GET_BUF_CFG_32 failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_REGISTER_ION_32: { + struct msm_audio_ion_info32 info_32; + struct msm_audio_ion_info info; + + pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) { + pr_err("%s: copy_from_user for AUDIO_REGISTER_ION_32 failed\n", + __func__); + rc = -EFAULT; + } else { + info.fd = info_32.fd; + info.vaddr = compat_ptr(info_32.vaddr); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_DEREGISTER_ION_32: { + struct msm_audio_ion_info32 info_32; + struct msm_audio_ion_info info; + + mutex_lock(&audio->lock); + pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio); + if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) { + pr_err("%s: copy_from_user for AUDIO_DEREGISTER_ION_32 failed\n", + __func__); + rc = -EFAULT; + } else { + info.fd = info_32.fd; + info.vaddr = compat_ptr(info_32.vaddr); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#endif diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h new file mode 100644 index 000000000000..82374f9a17a9 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h @@ -0,0 +1,232 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6audio_common.h" + +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */ +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 +#define AUDIO_DEC_EOS_SET 0x00000001 +#define AUDIO_DEC_EOF_SET 0x00000010 +#define AUDIO_EVENT_NUM 10 + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct timestamp { + u32 lowpart; + u32 highpart; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct dec_meta_in { + unsigned char reserved[18]; + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct dec_meta_out { + unsigned int reserved[7]; + unsigned int num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +/* General meta field to store meta info locally */ +union meta_data { + struct dec_meta_out meta_out; + struct dec_meta_in meta_in; +} __packed; + +/* per device wakeup source manager */ +struct ws_mgr { + struct mutex ws_lock; + uint32_t ref_cnt; +}; + +#define PCM_BUF_COUNT (2) +/* Buffer with meta */ +#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out)) + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (2) +#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in)) + +struct audio_aio_ion_region { + struct list_head list; + struct ion_handle *handle; + int fd; + void *vaddr; + phys_addr_t paddr; + void *kvaddr; + unsigned long len; + unsigned int ref_cnt; +}; + +struct audio_aio_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio_aio_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; + uint32_t token; + void *kvaddr; + union meta_data meta_info; +}; + +struct q6audio_aio; +struct audio_aio_drv_operations { + void (*out_flush)(struct q6audio_aio *); + void (*in_flush)(struct q6audio_aio *); +}; + +struct q6audio_aio { + atomic_t in_bytes; + atomic_t in_samples; + + struct msm_audio_stream_config str_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + struct audio_client *ac; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + struct mutex get_event_lock; + wait_queue_head_t cmd_wait; + wait_queue_head_t write_wait; + wait_queue_head_t event_wait; + spinlock_t dsp_lock; + spinlock_t event_queue_lock; + + struct miscdevice *miscdevice; + uint32_t wakelock_voted; + struct ws_mgr *audio_ws_mgr; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct list_head out_queue; /* queue to retain output buffers */ + struct list_head in_queue; /* queue to retain input buffers */ + struct list_head free_event_queue; + struct list_head event_queue; + struct list_head ion_region_queue; /* protected by lock */ + struct ion_client *client; + struct audio_aio_drv_operations drv_ops; + union msm_audio_event_payload eos_write_payload; + uint32_t device_events; + uint16_t volume; + uint32_t drv_status; + int event_abort; + int eos_rsp; + int eos_flag; + int opened; + int enabled; + int stopped; + int feedback; + int rflush; /* Read flush */ + int wflush; /* Write flush */ + bool reset_event; + long (*codec_ioctl)(struct file *, unsigned int, unsigned long); + long (*codec_compat_ioctl)(struct file *, unsigned int, unsigned long); +}; + +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node); + +void extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir); + +int audio_aio_open(struct q6audio_aio *audio, struct file *file); +int audio_aio_enable(struct q6audio_aio *audio); +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload); +int audio_aio_release(struct inode *inode, struct file *file); +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync); +void audio_aio_async_out_flush(struct q6audio_aio *audio); +void audio_aio_async_in_flush(struct q6audio_aio *audio); +void audio_aio_ioport_reset(struct q6audio_aio *audio); +int enable_volume_ramp(struct q6audio_aio *audio); +#ifdef CONFIG_DEBUG_FS +int audio_aio_debug_open(struct inode *inode, struct file *file); +ssize_t audio_aio_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +#endif diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/drivers/misc/qcom/qdsp6v2/audio_wma.c new file mode 100644 index 000000000000..e35334a3313a --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_wma.c @@ -0,0 +1,345 @@ +/* wma audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_wma_misc; +static struct ws_mgr audio_wma_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wma_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wma_cfg wma_cfg; + struct msm_audio_wma_config_v2 *wma_config; + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_cfg.format_tag = wma_config->format_tag; + wma_cfg.ch_cfg = wma_config->numchannels; + wma_cfg.sample_rate = wma_config->samplingrate; + wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond; + wma_cfg.block_align = wma_config->block_align; + wma_cfg.valid_bits_per_sample = + wma_config->validbitspersample; + wma_cfg.ch_mask = wma_config->channelmask; + wma_cfg.encode_opt = wma_config->encodeopt; + /* Configure Media format block */ + rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_WMA_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wma_config_v2))) { + pr_err("%s:copy_to_user for AUDIO_SET_WMA_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wma_config_v2))) { + pr_err("%s:copy_from_user for AUDIO_SET_WMA_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_wma_config_v2_32 { + u16 format_tag; + u16 numchannels; + u32 samplingrate; + u32 avgbytespersecond; + u16 block_align; + u16 validbitspersample; + u32 channelmask; + u16 encodeopt; +}; + +enum { + AUDIO_GET_WMA_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2_32), + AUDIO_SET_WMA_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_WMA_CONFIG_V2_32: { + struct msm_audio_wma_config_v2 *wma_config; + struct msm_audio_wma_config_v2_32 wma_config_32; + + memset(&wma_config_32, 0, sizeof(wma_config_32)); + + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_config_32.format_tag = wma_config->format_tag; + wma_config_32.numchannels = wma_config->numchannels; + wma_config_32.samplingrate = wma_config->samplingrate; + wma_config_32.avgbytespersecond = wma_config->avgbytespersecond; + wma_config_32.block_align = wma_config->block_align; + wma_config_32.validbitspersample = + wma_config->validbitspersample; + wma_config_32.channelmask = wma_config->channelmask; + wma_config_32.encodeopt = wma_config->encodeopt; + if (copy_to_user((void *)arg, &wma_config_32, + sizeof(wma_config_32))) { + pr_err("%s: copy_to_user for GET_WMA_CONFIG_V2_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2_32: { + struct msm_audio_wma_config_v2 *wma_config; + struct msm_audio_wma_config_v2_32 wma_config_32; + + if (copy_from_user(&wma_config_32, (void *)arg, + sizeof(wma_config_32))) { + pr_err("%s: copy_from_user for SET_WMA_CONFIG_V2_32 failed\n" + , __func__); + rc = -EFAULT; + break; + } + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_config->format_tag = wma_config_32.format_tag; + wma_config->numchannels = wma_config_32.numchannels; + wma_config->samplingrate = wma_config_32.samplingrate; + wma_config->avgbytespersecond = wma_config_32.avgbytespersecond; + wma_config->block_align = wma_config_32.block_align; + wma_config->validbitspersample = + wma_config_32.validbitspersample; + wma_config->channelmask = wma_config_32.channelmask; + wma_config->encodeopt = wma_config_32.encodeopt; + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_wma_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_wma_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V9); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_wma_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_wma_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_wma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +static int __init audio_wma_init(void) +{ + int ret = misc_register(&audio_wma_misc); + + if (ret == 0) + device_init_wakeup(audio_wma_misc.this_device, true); + audio_wma_ws_mgr.ref_cnt = 0; + mutex_init(&audio_wma_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_wma_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c new file mode 100644 index 000000000000..3cb9db15f872 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c @@ -0,0 +1,418 @@ +/* wmapro audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_wmapro_misc; +static struct ws_mgr audio_wmapro_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wmapro_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wmapro_cfg wmapro_cfg; + struct msm_audio_wmapro_config *wmapro_config; + + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /* bits per sample */ + true, /* use default channel map */ + true, /* use back channel map flavor */ + NULL); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wmapro_config = (struct msm_audio_wmapro_config *) + audio->codec_cfg; + if ((wmapro_config->formattag == 0x162) || + (wmapro_config->formattag == 0x163) || + (wmapro_config->formattag == 0x166) || + (wmapro_config->formattag == 0x167)) { + wmapro_cfg.format_tag = wmapro_config->formattag; + } else { + pr_err("%s:AUDIO_START failed: formattag = %d\n", + __func__, wmapro_config->formattag); + rc = -EINVAL; + break; + } + if (wmapro_config->numchannels > 0) { + wmapro_cfg.ch_cfg = wmapro_config->numchannels; + } else { + pr_err("%s:AUDIO_START failed: channels = %d\n", + __func__, wmapro_config->numchannels); + rc = -EINVAL; + break; + } + if (wmapro_config->samplingrate > 0) { + wmapro_cfg.sample_rate = wmapro_config->samplingrate; + } else { + pr_err("%s:AUDIO_START failed: sample_rate = %d\n", + __func__, wmapro_config->samplingrate); + rc = -EINVAL; + break; + } + wmapro_cfg.avg_bytes_per_sec = + wmapro_config->avgbytespersecond; + if ((wmapro_config->asfpacketlength <= 13376) || + (wmapro_config->asfpacketlength > 0)) { + wmapro_cfg.block_align = + wmapro_config->asfpacketlength; + } else { + pr_err("%s:AUDIO_START failed: block_align = %d\n", + __func__, wmapro_config->asfpacketlength); + rc = -EINVAL; + break; + } + if ((wmapro_config->validbitspersample == 16) || + (wmapro_config->validbitspersample == 24)) { + wmapro_cfg.valid_bits_per_sample = + wmapro_config->validbitspersample; + } else { + pr_err("%s:AUDIO_START failed: bitspersample = %d\n", + __func__, wmapro_config->validbitspersample); + rc = -EINVAL; + break; + } + wmapro_cfg.ch_mask = wmapro_config->channelmask; + wmapro_cfg.encode_opt = wmapro_config->encodeopt; + wmapro_cfg.adv_encode_opt = + wmapro_config->advancedencodeopt; + wmapro_cfg.adv_encode_opt2 = + wmapro_config->advancedencodeopt2; + /* Configure Media format block */ + rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd %d\n", __func__, cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_GET_WMAPRO_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wmapro_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wmapro_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_WMAPRO_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT + +struct msm_audio_wmapro_config32 { + u16 armdatareqthr; + u8 validbitspersample; + u8 numchannels; + u16 formattag; + u32 samplingrate; + u32 avgbytespersecond; + u16 asfpacketlength; + u32 channelmask; + u16 encodeopt; + u16 advancedencodeopt; + u32 advancedencodeopt2; +}; + +enum { + AUDIO_GET_WMAPRO_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config32), + AUDIO_SET_WMAPRO_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_GET_WMAPRO_CONFIG_32: { + struct msm_audio_wmapro_config *wmapro_config; + struct msm_audio_wmapro_config32 wmapro_config_32; + + memset(&wmapro_config_32, 0, sizeof(wmapro_config_32)); + + wmapro_config = + (struct msm_audio_wmapro_config *)audio->codec_cfg; + wmapro_config_32.armdatareqthr = wmapro_config->armdatareqthr; + wmapro_config_32.validbitspersample = + wmapro_config->validbitspersample; + wmapro_config_32.numchannels = wmapro_config->numchannels; + wmapro_config_32.formattag = wmapro_config->formattag; + wmapro_config_32.samplingrate = wmapro_config->samplingrate; + wmapro_config_32.avgbytespersecond = + wmapro_config->avgbytespersecond; + wmapro_config_32.asfpacketlength = + wmapro_config->asfpacketlength; + wmapro_config_32.channelmask = wmapro_config->channelmask; + wmapro_config_32.encodeopt = wmapro_config->encodeopt; + wmapro_config_32.advancedencodeopt = + wmapro_config->advancedencodeopt; + wmapro_config_32.advancedencodeopt2 = + wmapro_config->advancedencodeopt2; + + if (copy_to_user((void *)arg, &wmapro_config_32, + sizeof(struct msm_audio_wmapro_config32))) { + pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG_V2_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG_32: { + struct msm_audio_wmapro_config *wmapro_config; + struct msm_audio_wmapro_config32 wmapro_config_32; + + if (copy_from_user(&wmapro_config_32, (void *)arg, + sizeof(struct msm_audio_wmapro_config32))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_WMAPRO_CONFG_V2_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + wmapro_config = + (struct msm_audio_wmapro_config *)audio->codec_cfg; + wmapro_config->armdatareqthr = wmapro_config_32.armdatareqthr; + wmapro_config->validbitspersample = + wmapro_config_32.validbitspersample; + wmapro_config->numchannels = wmapro_config_32.numchannels; + wmapro_config->formattag = wmapro_config_32.formattag; + wmapro_config->samplingrate = wmapro_config_32.samplingrate; + wmapro_config->avgbytespersecond = + wmapro_config_32.avgbytespersecond; + wmapro_config->asfpacketlength = + wmapro_config_32.asfpacketlength; + wmapro_config->channelmask = wmapro_config_32.channelmask; + wmapro_config->encodeopt = wmapro_config_32.encodeopt; + wmapro_config->advancedencodeopt = + wmapro_config_32.advancedencodeopt; + wmapro_config->advancedencodeopt2 = + wmapro_config_32.advancedencodeopt2; + break; + } + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_wmapro_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_wmapro_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_wmapro_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_wmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_wmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +static int __init audio_wmapro_init(void) +{ + int ret = misc_register(&audio_wmapro_misc); + + if (ret == 0) + device_init_wakeup(audio_wmapro_misc.this_device, true); + audio_wmapro_ws_mgr.ref_cnt = 0; + mutex_init(&audio_wmapro_ws_mgr.ws_lock); + + return ret; +} + +device_initcall(audio_wmapro_init); diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/drivers/misc/qcom/qdsp6v2/evrc_in.c new file mode 100644 index 000000000000..e30271dd8102 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/evrc_in.c @@ -0,0 +1,410 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10)) + +static long evrc_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_evrc_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* rate_modulation_cmd set to zero + * currently not configurable from user space + */ + rc = q6asm_enc_cfg_blk_evrc(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd evrc media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config *cfg; + struct msm_audio_evrc_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + cfg = (struct msm_audio_evrc_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, "AUDIO_SET_EVRC_ENC_CONFIG"); + rc = -EINVAL; + break; + } + if (cfg->min_bit_rate > 4 || + cfg->min_bit_rate < 1 || + (cfg->min_bit_rate == 2)) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg->max_bit_rate > 4 || + cfg->max_bit_rate < 1 || + (cfg->max_bit_rate == 2)) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg->min_bit_rate; + enc_cfg->max_bit_rate = cfg->max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n", + __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long evrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = evrc_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_evrc_enc_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_evrc_enc_config32 { + u32 cdma_rate; + u32 min_bit_rate; + u32 max_bit_rate; +}; + +enum { + AUDIO_SET_EVRC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + 2, struct msm_audio_evrc_enc_config32), + AUDIO_GET_EVRC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + 3, struct msm_audio_evrc_enc_config32) +}; + +static long evrc_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = evrc_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG_32: { + struct msm_audio_evrc_enc_config32 cfg_32; + struct msm_audio_evrc_enc_config *enc_cfg; + + memset(&cfg_32, 0, sizeof(cfg_32)); + + enc_cfg = audio->enc_cfg; + cfg_32.cdma_rate = enc_cfg->cdma_rate; + cfg_32.min_bit_rate = enc_cfg->min_bit_rate; + cfg_32.max_bit_rate = enc_cfg->max_bit_rate; + + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG_32: { + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_evrc_enc_config32 cfg_32; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.cdma_rate = cfg_32.cdma_rate; + cfg.min_bit_rate = cfg_32.min_bit_rate; + cfg.max_bit_rate = cfg_32.max_bit_rate; + cmd = AUDIO_SET_EVRC_ENC_CONFIG; + rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define evrc_in_compat_ioctl NULL +#endif + +static int evrc_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_evrc_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 23; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open evrc encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = evrc_in_compat_ioctl; + audio->enc_ioctl = evrc_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = evrc_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_in_fops, +}; + +static int __init evrc_in_init(void) +{ + return misc_register(&audio_evrc_in_misc); +} + +device_initcall(evrc_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/g711alaw_in.c b/drivers/misc/qcom/qdsp6v2/g711alaw_in.c new file mode 100644 index 000000000000..bc8c0a36a825 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/g711alaw_in.c @@ -0,0 +1,382 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((320+sizeof(struct meta_out_dsp)) * 10)) +static long g711_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate); + rc = q6asm_enc_cfg_blk_g711(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate); + + if (rc < 0) { + pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config *cfg; + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + + cfg = (struct msm_audio_g711_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + if (cfg->sample_rate != 8000 && + cfg->sample_rate != 16000) { + pr_err("%s:session id %d: invalid sample rate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg->sample_rate; + pr_debug("%s:session id %d: sample_rate= 0x%x", + __func__, + audio->ac->session, enc_cfg->sample_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +static long g711_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_g711_enc_config))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + break; + } + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_enc_config32 { + uint32_t sample_rate; +}; + +enum { + AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32), + AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32) +}; + +static long g711_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg; + cfg_32.sample_rate = enc_cfg->sample_rate; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 cfg; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.sample_rate = cfg_32.sample_rate; + cmd = AUDIO_SET_G711_ENC_CONFIG; + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} +#else +#define g711_in_compat_ioctl NULL +#endif + +static int g711_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_g711_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* + * Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 320; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open g711 encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_G711_ALAW_FS, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_G711_ALAW_FS); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = g711_in_compat_ioctl; + audio->enc_ioctl = g711_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = g711_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_in_compat_ioctl, +#endif +}; + +struct miscdevice audio_g711alaw_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711alaw_in", + .fops = &audio_in_fops, +}; + +static int __init g711alaw_in_init(void) +{ + return misc_register(&audio_g711alaw_in_misc); +} + +device_initcall(g711alaw_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c b/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c new file mode 100644 index 000000000000..b92c44957377 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c @@ -0,0 +1,385 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +#ifdef CONFIG_COMPAT +#undef PROC_ADD +#endif +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((320+sizeof(struct meta_out_dsp)) * 10)) +static long g711_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate); + rc = q6asm_enc_cfg_blk_g711(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate); + + if (rc < 0) { + pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config *cfg; + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + + cfg = (struct msm_audio_g711_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + if (cfg->sample_rate != 8000 && + cfg->sample_rate != 16000) { + pr_err("%s:session id %d: invalid sample rate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg->sample_rate; + pr_debug("%s:session id %d: sample_rate= 0x%x", + __func__, + audio->ac->session, enc_cfg->sample_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +static long g711_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_g711_enc_config))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + break; + } + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_enc_config32 { + uint32_t sample_rate; +}; + +enum { + AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32), + AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32) +}; + +static long g711_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg; + cfg_32.sample_rate = enc_cfg->sample_rate; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 cfg; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.sample_rate = cfg_32.sample_rate; + cmd = AUDIO_SET_G711_ENC_CONFIG; + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} +#else +#define g711_in_compat_ioctl NULL +#endif + +static int g711_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_g711_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* + * Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 320; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open g711 encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_G711_MLAW_FS, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_G711_MLAW_FS); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = g711_in_compat_ioctl; + audio->enc_ioctl = g711_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = g711_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_in_compat_ioctl, +#endif +}; + +struct miscdevice audio_g711mlaw_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711mlaw_in", + .fops = &audio_in_fops, +}; + +static int __init g711mlaw_in_init(void) +{ + return misc_register(&audio_g711mlaw_in_misc); +} + +device_initcall(g711mlaw_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_common.h b/drivers/misc/qcom/qdsp6v2/q6audio_common.h new file mode 100644 index 000000000000..49b88b793cc3 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_common.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +/* For Decoders */ +#ifndef __Q6_AUDIO_COMMON_H__ +#define __Q6_AUDIO_COMMON_H__ + +#include +#include + + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *audio); + + +/* For Encoders */ +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_in_get_dsp_frames(void *audio, + uint32_t token, uint32_t *payload); + +#endif /*__Q6_AUDIO_COMMON_H__*/ diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c new file mode 100644 index 000000000000..6c05165a18bc --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2012-2013, 2015-2017, The Linux Foundation. All rights + * reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in *audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE_V2: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE_V2: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_EVENT_RENDERED_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2: + break; + case ASM_SESSION_EVENTX_OVERFLOW: + pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + case RESET_EVENTS: + pr_debug("%s:received RESET EVENTS\n", __func__); + audio->enabled = 0; + audio->stopped = 1; + audio->event_abort = 1; + audio->reset_event = true; + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + break; + default: + pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +void audio_in_get_dsp_frames(void *priv, + uint32_t token, uint32_t *payload) +{ + struct q6audio_in *audio = (struct q6audio_in *)priv; + uint32_t index; + + index = q6asm_get_buf_index_from_token(token); + pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n", + __func__, audio->ac->session, token, payload[9], + payload[5]); + pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__, + audio->ac->session, payload[7], payload[6]); + pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__, + audio->ac->session, payload[8], payload[10]); + pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__, + audio->ac->session, payload[4]); + + /* Ensure the index is within max array size: FRAME_NUM */ + if (index >= FRAME_NUM) { + pr_err("%s: Invalid index %d\n", + __func__, index); + return; + } + + audio->out_frame_info[index][0] = payload[9]; + audio->out_frame_info[index][1] = payload[5]; + + /* statistics of read */ + atomic_add(payload[4], &audio->in_bytes); + atomic_add(payload[9], &audio->in_samples); + + if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) { + atomic_inc(&audio->out_count); + wake_up(&audio->read_wait); + } +} diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c new file mode 100644 index 000000000000..9f764587888b --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c @@ -0,0 +1,223 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils_aio.h" + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + + pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + case ASM_DATA_EVENT_READ_DONE_V2: + case ASM_DATA_EVENT_RENDERED_EOS: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + case RESET_EVENTS: + audio_aio_cb(opcode, token, payload, audio); + break; + default: + pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode); + break; + } +} + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv/*struct q6audio_aio *audio*/) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + union msm_audio_event_payload e_payload; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + pr_debug("%s[%pK]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_write_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_READ_DONE_V2: + pr_debug("%s[%pK]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_read_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_RENDERED_EOS: + /* EOS Handle */ + pr_debug("%s[%pK]:ASM_DATA_CMDRSP_EOS\n", __func__, audio); + if (audio->feedback) { /* Non-Tunnel mode */ + audio->eos_rsp = 1; + /* propagate input EOS i/p buffer, + * after receiving DSP acknowledgment + */ + if (audio->eos_flag && + (audio->eos_write_payload.aio_buf.buf_addr)) { + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload, 0, + sizeof(union msm_audio_event_payload)); + audio->eos_flag = 0; + } + } else { /* Tunnel mode */ + audio->eos_rsp = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + break; + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + pr_debug("%s[%pK]:payload0[%x] payloa1d[%x]opcode= 0x%x\n", + __func__, audio, payload[0], payload[1], opcode); + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n", + __func__, audio, payload[0], + payload[1], payload[2], payload[3]); + + pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,", + __func__, audio, audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + audio->pcm_cfg.sample_rate = payload[0]; + audio->pcm_cfg.channel_count = payload[1] & 0xFFFF; + e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count; + e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate; + audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); + break; + case RESET_EVENTS: + pr_err("%s: Received opcode:0x%x\n", __func__, opcode); + audio->stopped = 1; + audio->reset_event = true; + wake_up(&audio->event_wait); + break; + default: + break; + } +} + +void extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + uint32_t temp; + + if (dir) { /* input buffer - Write */ + if (audio->buf_cfg.meta_info_enable) + memcpy(&buf_node->meta_info.meta_in, + (char *)buf_node->kvaddr, sizeof(struct dec_meta_in)); + else + memset(&buf_node->meta_info.meta_in, + 0, sizeof(struct dec_meta_in)); + pr_debug("%s[%pK]:i/p: msw_ts %d lsw_ts %d nflags 0x%8x\n", + __func__, audio, + buf_node->meta_info.meta_in.ntimestamp.highpart, + buf_node->meta_info.meta_in.ntimestamp.lowpart, + buf_node->meta_info.meta_in.nflags); + } else { /* output buffer - Read */ + memcpy((char *)buf_node->kvaddr, + &buf_node->meta_info.meta_out, + sizeof(struct dec_meta_out)); + meta_data->meta_out_dsp[0].nflags = 0x00000000; + temp = meta_data->meta_out_dsp[0].msw_ts; + meta_data->meta_out_dsp[0].msw_ts = + meta_data->meta_out_dsp[0].lsw_ts; + meta_data->meta_out_dsp[0].lsw_ts = temp; + + pr_debug("%s[%pK]:o/p: msw_ts %d lsw_ts %d nflags 0x%8x, num_frames = %d\n", + __func__, audio, + ((struct dec_meta_out *)buf_node->kvaddr)-> + meta_out_dsp[0].msw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)-> + meta_out_dsp[0].lsw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)-> + meta_out_dsp[0].nflags, + ((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames); + } +} + +/* Read buffer from DSP / Handle Ack from DSP */ +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *filled_buf; + + pr_debug("%s\n", __func__); + + /* No active flush in progress */ + if (audio->rflush) + return; + + /* Statistics of read */ + atomic_add(payload[4], &audio->in_bytes); + atomic_add(payload[9], &audio->in_samples); + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (list_empty(&audio->in_queue)) { + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_warn("%s unexpected ack from dsp\n", __func__); + return; + } + filled_buf = list_first_entry(&audio->in_queue, + struct audio_aio_buffer_node, list); + + pr_debug("%s token: 0x[%x], filled_buf->token: 0x[%x]", + __func__, token, filled_buf->token); + if (token == (filled_buf->token)) { + list_del(&filled_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + event_payload.aio_buf = filled_buf->buf; + /* Read done Buffer due to flush/normal condition + * after EOS event, so append EOS buffer + */ + if (audio->eos_rsp == 0x1) { + event_payload.aio_buf.data_len = + insert_eos_buf(audio, filled_buf); + /* Reset flag back to indicate eos intimated */ + audio->eos_rsp = 0; + } else { + filled_buf->meta_info.meta_out.num_of_frames + = payload[9]; + event_payload.aio_buf.data_len = payload[4] + + payload[5] + sizeof(struct dec_meta_out); + pr_debug("%s[%pK]:nr of frames 0x%8x len=%d\n", + __func__, audio, + filled_buf->meta_info.meta_out.num_of_frames, + event_payload.aio_buf.data_len); + extract_meta_out_info(audio, filled_buf, 0); + audio->eos_rsp = 0; + } + pr_debug("%s, posting read done to the app here\n", __func__); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + pr_err("%s[%pK]:expected=%x ret=%x\n", + __func__, audio, filled_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/drivers/misc/qcom/qdsp6v2/qcelp_in.c new file mode 100644 index 000000000000..da5520f75a62 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/qcelp_in.c @@ -0,0 +1,410 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10)) + +static long qcelp_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_qcelp_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* reduced_rate_level, rate_modulation_cmd set to zero + * currently not configurable from user space + */ + rc = q6asm_enc_cfg_blk_qcelp(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd qcelp media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config *cfg; + struct msm_audio_qcelp_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + cfg = (struct msm_audio_qcelp_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + if (cfg->min_bit_rate > 4 || + cfg->min_bit_rate < 1) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg->max_bit_rate > 4 || + cfg->max_bit_rate < 1) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->cdma_rate = cfg->cdma_rate; + enc_cfg->min_bit_rate = cfg->min_bit_rate; + enc_cfg->max_bit_rate = cfg->max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n", + __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long qcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = qcelp_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_qcelp_enc_config))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + break; + } + rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_qcelp_enc_config32 { + u32 cdma_rate; + u32 min_bit_rate; + u32 max_bit_rate; +}; + +enum { + AUDIO_SET_QCELP_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + 0, struct msm_audio_qcelp_enc_config32), + AUDIO_GET_QCELP_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + 1, struct msm_audio_qcelp_enc_config32) +}; + +static long qcelp_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = qcelp_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG_32: { + struct msm_audio_qcelp_enc_config32 cfg_32; + struct msm_audio_qcelp_enc_config *enc_cfg; + + memset(&cfg_32, 0, sizeof(cfg_32)); + + enc_cfg = (struct msm_audio_qcelp_enc_config *)audio->enc_cfg; + cfg_32.cdma_rate = enc_cfg->cdma_rate; + cfg_32.min_bit_rate = enc_cfg->min_bit_rate; + cfg_32.max_bit_rate = enc_cfg->max_bit_rate; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; +} + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG_32: { + struct msm_audio_qcelp_enc_config32 cfg_32; + struct msm_audio_qcelp_enc_config cfg; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.cdma_rate = cfg_32.cdma_rate; + cfg.min_bit_rate = cfg_32.min_bit_rate; + cfg.max_bit_rate = cfg_32.max_bit_rate; + cmd = AUDIO_SET_QCELP_ENC_CONFIG; + rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define qcelp_in_compat_ioctl NULL +#endif + +static int qcelp_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_qcelp_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 35; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open qcelp encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_V13K, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = qcelp_in_compat_ioctl; + audio->enc_ioctl = qcelp_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = qcelp_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_in_fops, +}; + +static int __init qcelp_in_init(void) +{ + return misc_register(&audio_qcelp_in_misc); +} + +device_initcall(qcelp_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile new file mode 100644 index 000000000000..41f614aa4eb3 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile @@ -0,0 +1,2 @@ +ccflags-y := -I$(src)/.. +obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c new file mode 100644 index 000000000000..f20f335be3cb --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c @@ -0,0 +1,1467 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6usm.h" + +#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 + +#define MEM_4K_OFFSET 4095 +#define MEM_4K_MASK 0xfffff000 + +#define USM_SESSION_MAX 0x02 /* aDSP:USM limit */ + +#define READDONE_IDX_STATUS 0 + +#define WRITEDONE_IDX_STATUS 0 + +/* Standard timeout in the asynchronous ops */ +#define Q6USM_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */ + +static DEFINE_MUTEX(session_lock); + +static struct us_client *session[USM_SESSION_MAX]; +static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv); +static int32_t q6usm_callback(struct apr_client_data *data, void *priv); +static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg); + +struct usm_mmap { + atomic_t ref_cnt; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; + void *apr; + int mem_handle; +}; + +static struct usm_mmap this_mmap; + +static void q6usm_add_mmaphdr(struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg, u32 token) +{ + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0; + hdr->dest_port = 0; + if (cmd_flg) { + hdr->token = token; + atomic_set(&this_mmap.cmd_state, 1); + } + hdr->pkt_size = pkt_size; +} + +static int q6usm_memory_map(phys_addr_t buf_add, int dir, uint32_t bufsz, + uint32_t bufcnt, uint32_t session, uint32_t *mem_handle) +{ + struct usm_cmd_memory_map_region mem_region_map; + int rc = 0; + + if (this_mmap.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_mmaphdr(&mem_region_map.hdr, + sizeof(struct usm_cmd_memory_map_region), true, + ((session << 8) | dir)); + + mem_region_map.hdr.opcode = USM_CMD_SHARED_MEM_MAP_REGION; + mem_region_map.mempool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + + mem_region_map.num_regions = 1; + mem_region_map.flags = 0; + + mem_region_map.shm_addr_lsw = lower_32_bits(buf_add); + mem_region_map.shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add); + mem_region_map.mem_size_bytes = bufsz * bufcnt; + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_region_map); + if (rc < 0) { + pr_err("%s: mem_map op[0x%x]rc[%d]\n", + __func__, mem_region_map.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for memory_map\n", __func__); + } else { + *mem_handle = this_mmap.mem_handle; + rc = 0; + } +fail_cmd: + return rc; +} + +int q6usm_memory_unmap(phys_addr_t buf_add, int dir, uint32_t session, + uint32_t mem_handle) +{ + struct usm_cmd_memory_unmap_region mem_unmap; + int rc = 0; + + if (this_mmap.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_mmaphdr(&mem_unmap.hdr, + sizeof(struct usm_cmd_memory_unmap_region), true, + ((session << 8) | dir)); + mem_unmap.hdr.opcode = USM_CMD_SHARED_MEM_UNMAP_REGION; + mem_unmap.mem_map_handle = mem_handle; + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("%s: mem_unmap op[0x%x] rc[%d]\n", + __func__, mem_unmap.hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for memory_unmap\n", __func__); + } else + rc = 0; +fail_cmd: + return rc; +} + +static int q6usm_session_alloc(struct us_client *usc) +{ + int ind = 0; + + mutex_lock(&session_lock); + for (ind = 0; ind < USM_SESSION_MAX; ++ind) { + if (!session[ind]) { + session[ind] = usc; + mutex_unlock(&session_lock); + ++ind; /* session id: 0 reserved */ + pr_debug("%s: session[%d] was allocated\n", + __func__, ind); + return ind; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void q6usm_session_free(struct us_client *usc) +{ + /* Session index was incremented during allocation */ + uint16_t ind = (uint16_t)usc->session - 1; + + pr_debug("%s: to free session[%d]\n", __func__, ind); + if (ind < USM_SESSION_MAX) { + mutex_lock(&session_lock); + session[ind] = NULL; + mutex_unlock(&session_lock); + } +} + +static int q6usm_us_client_buf_free(unsigned int dir, + struct us_client *usc) +{ + struct us_port_data *port; + int rc = 0; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT))) + return -EINVAL; + + mutex_lock(&usc->cmd_lock); + port = &usc->port[dir]; + if (port == NULL) { + mutex_unlock(&usc->cmd_lock); + return -EINVAL; + } + + if (port->data == NULL) { + mutex_unlock(&usc->cmd_lock); + return 0; + } + + rc = q6usm_memory_unmap(port->phys, dir, usc->session, + *((uint32_t *)port->ext)); + pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__, + (void *)port->data, (u64)port->phys, (void *)&port->phys); + + msm_audio_ion_free(port->client, port->handle); + + port->data = NULL; + port->phys = 0; + port->buf_size = 0; + port->buf_cnt = 0; + port->client = NULL; + port->handle = NULL; + + mutex_unlock(&usc->cmd_lock); + return rc; +} + +int q6usm_us_param_buf_free(unsigned int dir, + struct us_client *usc) +{ + struct us_port_data *port; + int rc = 0; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT))) + return -EINVAL; + + mutex_lock(&usc->cmd_lock); + port = &usc->port[dir]; + if (port == NULL) { + mutex_unlock(&usc->cmd_lock); + return -EINVAL; + } + + if (port->param_buf == NULL) { + mutex_unlock(&usc->cmd_lock); + return 0; + } + + rc = q6usm_memory_unmap(port->param_phys, dir, usc->session, + *((uint32_t *)port->param_buf_mem_handle)); + pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__, + (void *)port->param_buf, (u64)port->param_phys, + (void *)&port->param_phys); + + msm_audio_ion_free(port->param_client, port->param_handle); + + port->param_buf = NULL; + port->param_phys = 0; + port->param_buf_size = 0; + port->param_client = NULL; + port->param_handle = NULL; + + mutex_unlock(&usc->cmd_lock); + return rc; +} + +void q6usm_us_client_free(struct us_client *usc) +{ + int loopcnt = 0; + struct us_port_data *port; + uint32_t *p_mem_handle = NULL; + + if ((usc == NULL) || + !(usc->session)) + return; + + for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) { + port = &usc->port[loopcnt]; + if (port->data == NULL) + continue; + pr_debug("%s: loopcnt = %d\n", __func__, loopcnt); + q6usm_us_client_buf_free(loopcnt, usc); + q6usm_us_param_buf_free(loopcnt, usc); + } + q6usm_session_free(usc); + apr_deregister(usc->apr); + + pr_debug("%s: APR De-Register\n", __func__); + + if (atomic_read(&this_mmap.ref_cnt) <= 0) { + pr_err("%s: APR Common Port Already Closed\n", __func__); + goto done; + } + + atomic_dec(&this_mmap.ref_cnt); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + apr_deregister(this_mmap.apr); + pr_debug("%s: APR De-Register common port\n", __func__); + } + +done: + p_mem_handle = (uint32_t *)usc->port[IN].ext; + kfree(p_mem_handle); + kfree(usc); + pr_debug("%s:\n", __func__); +} + +struct us_client *q6usm_us_client_alloc( + void (*cb)(uint32_t, uint32_t, uint32_t *, void *), + void *priv) +{ + struct us_client *usc; + uint32_t *p_mem_handle = NULL; + int n; + int lcnt = 0; + + usc = kzalloc(sizeof(struct us_client), GFP_KERNEL); + if (usc == NULL) + return NULL; + + p_mem_handle = kzalloc(sizeof(uint32_t) * 4, GFP_KERNEL); + if (p_mem_handle == NULL) { + kfree(usc); + return NULL; + } + + n = q6usm_session_alloc(usc); + if (n <= 0) + goto fail_session; + usc->session = n; + usc->cb = cb; + usc->priv = priv; + usc->apr = apr_register("ADSP", "USM", + (apr_fn)q6usm_callback, + ((usc->session) << 8 | 0x0001), + usc); + + if (usc->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + goto fail; + } + pr_debug("%s: Registering the common port with APR\n", __func__); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + this_mmap.apr = apr_register("ADSP", "USM", + (apr_fn)q6usm_mmapcallback, + 0x0FFFFFFFF, &this_mmap); + if (this_mmap.apr == NULL) { + pr_err("%s: USM port registration failed\n", + __func__); + goto fail; + } + } + + atomic_inc(&this_mmap.ref_cnt); + init_waitqueue_head(&usc->cmd_wait); + mutex_init(&usc->cmd_lock); + for (lcnt = 0; lcnt <= OUT; ++lcnt) { + mutex_init(&usc->port[lcnt].lock); + spin_lock_init(&usc->port[lcnt].dsp_lock); + usc->port[lcnt].ext = (void *)p_mem_handle++; + usc->port[lcnt].param_buf_mem_handle = (void *)p_mem_handle++; + pr_err("%s: usc->port[%d].ext=%pK;\n", + __func__, lcnt, usc->port[lcnt].ext); + } + atomic_set(&usc->cmd_state, 0); + + return usc; +fail: + kfree(p_mem_handle); + q6usm_us_client_free(usc); + return NULL; +fail_session: + kfree(p_mem_handle); + kfree(usc); + return NULL; +} + +int q6usm_us_client_buf_alloc(unsigned int dir, + struct us_client *usc, + unsigned int bufsz, + unsigned int bufcnt) +{ + int rc = 0; + struct us_port_data *port = NULL; + unsigned int size = bufsz*bufcnt; + size_t len; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT)) || (size == 0) || + (usc->session <= 0 || usc->session > USM_SESSION_MAX)) { + pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n", + __func__, size, bufcnt); + return -EINVAL; + } + + mutex_lock(&usc->cmd_lock); + + port = &usc->port[dir]; + + /* The size to allocate should be multiple of 4K bytes */ + size = PAGE_ALIGN(size); + + rc = msm_audio_ion_alloc("ultrasound_client", + &port->client, &port->handle, + size, &port->phys, + &len, &port->data); + + if (rc) { + pr_err("%s: US ION allocation failed, rc = %d\n", + __func__, rc); + mutex_unlock(&usc->cmd_lock); + return -ENOMEM; + } + + port->buf_cnt = bufcnt; + port->buf_size = bufsz; + pr_debug("%s: data[%pK]; phys[%llx]; [%pK]\n", __func__, + (void *)port->data, + (u64)port->phys, + (void *)&port->phys); + + rc = q6usm_memory_map(port->phys, dir, size, 1, usc->session, + (uint32_t *)port->ext); + if (rc < 0) { + pr_err("%s: CMD Memory_map failed\n", __func__); + mutex_unlock(&usc->cmd_lock); + q6usm_us_client_buf_free(dir, usc); + q6usm_us_param_buf_free(dir, usc); + } else { + mutex_unlock(&usc->cmd_lock); + rc = 0; + } + + return rc; +} + +int q6usm_us_param_buf_alloc(unsigned int dir, + struct us_client *usc, + unsigned int bufsz) +{ + int rc = 0; + struct us_port_data *port = NULL; + unsigned int size = bufsz; + size_t len; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT)) || + (usc->session <= 0 || usc->session > USM_SESSION_MAX)) { + pr_err("%s: wrong parameters: direction=%d, bufsz=%d\n", + __func__, dir, bufsz); + return -EINVAL; + } + + mutex_lock(&usc->cmd_lock); + + port = &usc->port[dir]; + + if (bufsz == 0) { + pr_debug("%s: bufsz=0, get/set param commands are forbidden\n", + __func__); + port->param_buf = NULL; + mutex_unlock(&usc->cmd_lock); + return rc; + } + + /* The size to allocate should be multiple of 4K bytes */ + size = PAGE_ALIGN(size); + + rc = msm_audio_ion_alloc("ultrasound_client", + &port->param_client, &port->param_handle, + size, &port->param_phys, + &len, &port->param_buf); + + if (rc) { + pr_err("%s: US ION allocation failed, rc = %d\n", + __func__, rc); + mutex_unlock(&usc->cmd_lock); + return -ENOMEM; + } + + port->param_buf_size = bufsz; + pr_debug("%s: param_buf[%pK]; param_phys[%llx]; [%pK]\n", __func__, + (void *)port->param_buf, + (u64)port->param_phys, + (void *)&port->param_phys); + + rc = q6usm_memory_map(port->param_phys, (IN | OUT), size, 1, + usc->session, (uint32_t *)port->param_buf_mem_handle); + if (rc < 0) { + pr_err("%s: CMD Memory_map failed\n", __func__); + mutex_unlock(&usc->cmd_lock); + q6usm_us_client_buf_free(dir, usc); + q6usm_us_param_buf_free(dir, usc); + } else { + mutex_unlock(&usc->cmd_lock); + rc = 0; + } + + return rc; +} + +static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv) +{ + uint32_t token; + uint32_t *payload = data->payload; + + pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n", + __func__, payload[0], payload[1], data->opcode); + pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n", + __func__, data->token, data->payload_size, + data->src_port, data->dest_port); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + /* status field check */ + if (payload[1]) { + pr_err("%s: wrong response[%d] on cmd [%d]\n", + __func__, payload[1], payload[0]); + } else { + token = data->token; + switch (payload[0]) { + case USM_CMD_SHARED_MEM_UNMAP_REGION: + if (atomic_read(&this_mmap.cmd_state)) { + atomic_set(&this_mmap.cmd_state, 0); + wake_up(&this_mmap.cmd_wait); + } + /* fallthrough */ + case USM_CMD_SHARED_MEM_MAP_REGION: + /* For MEM_MAP, additional answer is waited, */ + /* therfore, no wake-up here */ + pr_debug("%s: cmd[0x%x]; result[0x%x]\n", + __func__, payload[0], payload[1]); + break; + default: + pr_debug("%s: wrong command[0x%x]\n", + __func__, payload[0]); + break; + } + } + } else { + if (data->opcode == USM_CMDRSP_SHARED_MEM_MAP_REGION) { + this_mmap.mem_handle = payload[0]; + pr_debug("%s: memory map handle = 0x%x", + __func__, payload[0]); + if (atomic_read(&this_mmap.cmd_state)) { + atomic_set(&this_mmap.cmd_state, 0); + wake_up(&this_mmap.cmd_wait); + } + } + } + return 0; +} + + +static int32_t q6usm_callback(struct apr_client_data *data, void *priv) +{ + struct us_client *usc = (struct us_client *)priv; + unsigned long dsp_flags; + uint32_t *payload = data->payload; + uint32_t token = data->token; + uint32_t opcode = Q6USM_EVENT_UNDEF; + + if (usc == NULL) { + pr_err("%s: client info is NULL\n", __func__); + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + /* status field check */ + if (payload[1]) { + pr_err("%s: wrong response[%d] on cmd [%d]\n", + __func__, payload[1], payload[0]); + if (usc->cb) + usc->cb(data->opcode, token, + (uint32_t *)data->payload, usc->priv); + } else { + switch (payload[0]) { + case USM_SESSION_CMD_RUN: + case USM_STREAM_CMD_CLOSE: + if (token != usc->session) { + pr_err("%s: wrong token[%d]", + __func__, token); + break; + } + case USM_STREAM_CMD_OPEN_READ: + case USM_STREAM_CMD_OPEN_WRITE: + case USM_STREAM_CMD_SET_ENC_PARAM: + case USM_DATA_CMD_MEDIA_FORMAT_UPDATE: + case USM_SESSION_CMD_SIGNAL_DETECT_MODE: + case USM_STREAM_CMD_SET_PARAM: + case USM_STREAM_CMD_GET_PARAM: + if (atomic_read(&usc->cmd_state)) { + atomic_set(&usc->cmd_state, 0); + wake_up(&usc->cmd_wait); + } + if (usc->cb) + usc->cb(data->opcode, token, + (uint32_t *)data->payload, + usc->priv); + break; + default: + break; + } + } + return 0; + } + + switch (data->opcode) { + case RESET_EVENTS: { + pr_err("%s: Reset event is received: %d %d\n", + __func__, + data->reset_event, + data->reset_proc); + + opcode = RESET_EVENTS; + + apr_reset(this_mmap.apr); + this_mmap.apr = NULL; + + apr_reset(usc->apr); + usc->apr = NULL; + + break; + } + + + case USM_DATA_EVENT_READ_DONE: { + struct us_port_data *port = &usc->port[OUT]; + + opcode = Q6USM_EVENT_READ_DONE; + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (payload[READDONE_IDX_STATUS]) { + pr_err("%s: wrong READDONE[%d]; token[%d]\n", + __func__, + payload[READDONE_IDX_STATUS], + token); + token = USM_WRONG_TOKEN; + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } + + if (port->expected_token != token) { + u32 cpu_buf = port->cpu_buf; + + pr_err("%s: expected[%d] != token[%d]\n", + __func__, port->expected_token, token); + pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n", + __func__, port->dsp_buf, cpu_buf); + + token = USM_WRONG_TOKEN; + /* To prevent data handle continiue */ + port->expected_token = USM_WRONG_TOKEN; + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } /* port->expected_token != data->token */ + + port->expected_token = token + 1; + if (port->expected_token == port->buf_cnt) + port->expected_token = 0; + + /* gap support */ + if (port->expected_token != port->cpu_buf) { + port->dsp_buf = port->expected_token; + token = port->dsp_buf; /* for callback */ + } else + port->dsp_buf = token; + + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + break; + } /* case USM_DATA_EVENT_READ_DONE */ + + case USM_DATA_EVENT_WRITE_DONE: { + struct us_port_data *port = &usc->port[IN]; + + opcode = Q6USM_EVENT_WRITE_DONE; + if (payload[WRITEDONE_IDX_STATUS]) { + pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n", + __func__, + payload[WRITEDONE_IDX_STATUS]); + break; + } + + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + port->dsp_buf = token + 1; + if (port->dsp_buf == port->buf_cnt) + port->dsp_buf = 0; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + break; + } /* case USM_DATA_EVENT_WRITE_DONE */ + + case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: { + pr_debug("%s: US detect result: result=%d", + __func__, + payload[0]); + opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT; + + break; + } /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */ + + default: + return 0; + + } /* switch */ + + if (usc->cb) + usc->cb(opcode, token, + data->payload, usc->priv); + + return 0; +} + +uint32_t q6usm_get_virtual_address(int dir, + struct us_client *usc, + struct vm_area_struct *vms) +{ + uint32_t ret = 0xffffffff; + + if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) { + struct us_port_data *port = &usc->port[dir]; + int size = PAGE_ALIGN(port->buf_size * port->buf_cnt); + struct audio_buffer ab; + + ab.phys = port->phys; + ab.data = port->data; + ab.used = 1; + ab.size = size; + ab.actual_size = size; + ab.handle = port->handle; + ab.client = port->client; + + ret = msm_audio_ion_mmap(&ab, vms); + + } + return ret; +} + +static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg) +{ + mutex_lock(&usc->cmd_lock); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)usc->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_USM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = (usc->session << 8) | 0x0001; + hdr->dest_port = (usc->session << 8) | 0x0001; + if (cmd_flg) { + hdr->token = usc->session; + atomic_set(&usc->cmd_state, 1); + } + hdr->pkt_size = pkt_size; + mutex_unlock(&usc->cmd_lock); +} + +static uint32_t q6usm_ext2int_format(uint32_t ext_format) +{ + uint32_t int_format = INVALID_FORMAT; + + switch (ext_format) { + case FORMAT_USPS_EPOS: + int_format = US_POINT_EPOS_FORMAT_V2; + break; + case FORMAT_USRAW: + int_format = US_RAW_FORMAT_V2; + break; + case FORMAT_USPROX: + int_format = US_PROX_FORMAT_V4; + break; + case FORMAT_USGES_SYNC: + int_format = US_GES_SYNC_FORMAT; + break; + case FORMAT_USRAW_SYNC: + int_format = US_RAW_SYNC_FORMAT; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, ext_format); + break; + } + + return int_format; +} + +int q6usm_open_read(struct us_client *usc, + uint32_t format) +{ + uint32_t int_format = INVALID_FORMAT; + int rc = 0x00; + struct usm_stream_cmd_open_read open; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: client or its apr is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: session[%d]", __func__, usc->session); + + q6usm_add_hdr(usc, &open.hdr, sizeof(open), true); + open.hdr.opcode = USM_STREAM_CMD_OPEN_READ; + open.src_endpoint = 0; /* AFE */ + open.pre_proc_top = 0; /* No preprocessing required */ + + int_format = q6usm_ext2int_format(format); + if (int_format == INVALID_FORMAT) + return -EINVAL; + + open.uMode = STREAM_PRIORITY_NORMAL; + open.format = int_format; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n", + __func__, rc); + goto fail_cmd; + } else + rc = 0; +fail_cmd: + return rc; +} + + +int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg) +{ + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj; + struct usm_stream_cmd_encdec_cfg_blk *enc_cfg = &enc_cfg_obj; + int rc = 0; + uint32_t total_cfg_size = + sizeof(struct usm_stream_cmd_encdec_cfg_blk); + uint32_t round_params_size = 0; + uint8_t is_allocated = 0; + + + if ((usc == NULL) || (us_cfg == NULL)) { + pr_err("%s: wrong input", __func__); + return -EINVAL; + } + + int_format = q6usm_ext2int_format(us_cfg->format_id); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong input format[%d]", + __func__, us_cfg->format_id); + return -EINVAL; + } + + /* Transparent configuration data is after enc_cfg */ + /* Integer number of u32s is required */ + round_params_size = ((us_cfg->params_size + 3)/4) * 4; + if (round_params_size > USM_MAX_CFG_DATA_SIZE) { + /* Dynamic allocated encdec_cfg_blk is required */ + /* static part use */ + round_params_size -= USM_MAX_CFG_DATA_SIZE; + total_cfg_size += round_params_size; + enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL); + if (enc_cfg == NULL) { + pr_err("%s: enc_cfg[%d] allocation failed\n", + __func__, total_cfg_size); + return -ENOMEM; + } + is_allocated = 1; + } else + round_params_size = 0; + + q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size, true); + + enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM; + enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK; + enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+ + round_params_size; + enc_cfg->enc_blk.frames_per_buf = 1; + enc_cfg->enc_blk.format_id = int_format; + enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+ + USM_MAX_CFG_DATA_SIZE + + round_params_size; + memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common), + sizeof(struct usm_cfg_common)); + + /* Transparent data copy */ + memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params, + us_cfg->params_size); + pr_debug("%s: cfg_size[%d], params_size[%d]\n", + __func__, + enc_cfg->enc_blk.cfg_size, + us_cfg->params_size); + pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n", + __func__, + enc_cfg->enc_blk.transp_data[0], + enc_cfg->enc_blk.transp_data[1], + enc_cfg->enc_blk.transp_data[2], + enc_cfg->enc_blk.transp_data[3], + enc_cfg->enc_blk.transp_data[4], + enc_cfg->enc_blk.transp_data[5], + enc_cfg->enc_blk.transp_data[6], + enc_cfg->enc_blk.transp_data[7] + ); + pr_debug("%s: srate:%d, ch=%d, bps= %d;\n", + __func__, enc_cfg->enc_blk.cfg_common.sample_rate, + enc_cfg->enc_blk.cfg_common.ch_cfg, + enc_cfg->enc_blk.cfg_common.bits_per_sample); + pr_debug("dmap:[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]; dev_id=0x%x\n", + enc_cfg->enc_blk.cfg_common.data_map[0], + enc_cfg->enc_blk.cfg_common.data_map[1], + enc_cfg->enc_blk.cfg_common.data_map[2], + enc_cfg->enc_blk.cfg_common.data_map[3], + enc_cfg->enc_blk.cfg_common.data_map[4], + enc_cfg->enc_blk.cfg_common.data_map[5], + enc_cfg->enc_blk.cfg_common.data_map[6], + enc_cfg->enc_blk.cfg_common.data_map[7], + enc_cfg->enc_blk.cfg_common.dev_id); + + rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg); + if (rc < 0) { + pr_err("%s:Comamnd open failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg->hdr.opcode); + } else + rc = 0; + +fail_cmd: + if (is_allocated == 1) + kfree(enc_cfg); + + return rc; +} + +int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg) +{ + + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_media_format_update dec_cfg_obj; + struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj; + + int rc = 0; + uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update); + uint32_t round_params_size = 0; + uint8_t is_allocated = 0; + + + if ((usc == NULL) || (us_cfg == NULL)) { + pr_err("%s: wrong input", __func__); + return -EINVAL; + } + + int_format = q6usm_ext2int_format(us_cfg->format_id); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong input format[%d]", + __func__, us_cfg->format_id); + return -EINVAL; + } + + /* Transparent configuration data is after enc_cfg */ + /* Integer number of u32s is required */ + round_params_size = ((us_cfg->params_size + 3)/4) * 4; + if (round_params_size > USM_MAX_CFG_DATA_SIZE) { + /* Dynamic allocated encdec_cfg_blk is required */ + /* static part use */ + round_params_size -= USM_MAX_CFG_DATA_SIZE; + total_cfg_size += round_params_size; + dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL); + if (dec_cfg == NULL) { + pr_err("%s:dec_cfg[%d] allocation failed\n", + __func__, total_cfg_size); + return -ENOMEM; + } + is_allocated = 1; + } else { /* static transp_data is enough */ + round_params_size = 0; + } + + q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size, true); + + dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE; + dec_cfg->format_id = int_format; + dec_cfg->cfg_size = sizeof(struct usm_cfg_common) + + USM_MAX_CFG_DATA_SIZE + + round_params_size; + memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common), + sizeof(struct usm_cfg_common)); + /* Transparent data copy */ + memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size); + pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n", + __func__, + dec_cfg->cfg_size, + us_cfg->params_size, + dec_cfg->transp_data[0], + dec_cfg->transp_data[1], + dec_cfg->transp_data[2], + dec_cfg->transp_data[3] + ); + + rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg); + if (rc < 0) { + pr_err("%s:Comamnd open failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout opcode[0x%x]\n", + __func__, dec_cfg->hdr.opcode); + } else + rc = 0; + +fail_cmd: + if (is_allocated == 1) + kfree(dec_cfg); + + return rc; +} + +int q6usm_open_write(struct us_client *usc, + uint32_t format) +{ + int rc = 0; + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_cmd_open_write open; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: session[%d]", __func__, usc->session); + + q6usm_add_hdr(usc, &open.hdr, sizeof(open), true); + open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE; + + int_format = q6usm_ext2int_format(format); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong format[%d]", __func__, format); + return -EINVAL; + } + + open.format = int_format; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s:open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n", + __func__, rc); + goto fail_cmd; + } else + rc = 0; + +fail_cmd: + return rc; +} + +int q6usm_run(struct us_client *usc, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct usm_stream_cmd_run run; + int rc = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + q6usm_add_hdr(usc, &run.hdr, sizeof(run), true); + + run.hdr.opcode = USM_SESSION_CMD_RUN; + run.flags = flags; + run.msw_ts = msw_ts; + run.lsw_ts = lsw_ts; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]\n", __func__, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for run success rc[%d]\n", + __func__, rc); + } else + rc = 0; + +fail_cmd: + return rc; +} + + + +int q6usm_read(struct us_client *usc, uint32_t read_ind) +{ + struct usm_stream_cmd_read read; + struct us_port_data *port = NULL; + int rc = 0; + u32 read_counter = 0; + u32 loop_ind = 0; + u64 buf_addr = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[OUT]; + + if (read_ind > port->buf_cnt) { + pr_err("%s: wrong read_ind[%d]\n", + __func__, read_ind); + return -EINVAL; + } + if (read_ind == port->cpu_buf) { + pr_err("%s: no free region\n", __func__); + return 0; + } + + if (read_ind > port->cpu_buf) { /* 1 range */ + read_counter = read_ind - port->cpu_buf; + } else { /* 2 ranges */ + read_counter = (port->buf_cnt - port->cpu_buf) + read_ind; + } + + q6usm_add_hdr(usc, &read.hdr, sizeof(read), false); + + read.hdr.opcode = USM_DATA_CMD_READ; + read.buf_size = port->buf_size; + buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf); + read.buf_addr_lsw = lower_32_bits(buf_addr); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr); + read.mem_map_handle = *((uint32_t *)(port->ext)); + + for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) { + u32 temp_cpu_buf = port->cpu_buf; + + buf_addr = (u64)(port->phys) + + port->buf_size * (port->cpu_buf); + read.buf_addr_lsw = lower_32_bits(buf_addr); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr); + read.seq_id = port->cpu_buf; + read.hdr.token = port->cpu_buf; + read.counter = 1; + + ++(port->cpu_buf); + if (port->cpu_buf == port->buf_cnt) + port->cpu_buf = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &read); + + if (rc < 0) { + port->cpu_buf = temp_cpu_buf; + + pr_err("%s:read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + break; + } + + rc = 0; + } /* bufs loop */ + + return rc; +} + +int q6usm_write(struct us_client *usc, uint32_t write_ind) +{ + int rc = 0; + struct usm_stream_cmd_write cmd_write; + struct us_port_data *port = NULL; + u32 current_dsp_buf = 0; + u64 buf_addr = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[IN]; + + current_dsp_buf = port->dsp_buf; + /* free region, caused by new dsp_buf report from DSP, */ + /* can be only extended */ + if (port->cpu_buf >= current_dsp_buf) { + /* 2 -part free region, including empty buffer */ + if ((write_ind <= port->cpu_buf) && + (write_ind > current_dsp_buf)) { + pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n", + __func__, write_ind, + current_dsp_buf, port->cpu_buf); + return -EINVAL; + } + } else { + /* 1 -part free region */ + if ((write_ind <= port->cpu_buf) || + (write_ind > current_dsp_buf)) { + pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n", + __func__, write_ind, + current_dsp_buf, port->cpu_buf); + return -EINVAL; + } + } + + q6usm_add_hdr(usc, &cmd_write.hdr, sizeof(cmd_write), false); + + cmd_write.hdr.opcode = USM_DATA_CMD_WRITE; + cmd_write.buf_size = port->buf_size; + buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf); + cmd_write.buf_addr_lsw = lower_32_bits(buf_addr); + cmd_write.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr); + cmd_write.mem_map_handle = *((uint32_t *)(port->ext)); + cmd_write.res0 = 0; + cmd_write.res1 = 0; + cmd_write.res2 = 0; + + while (port->cpu_buf != write_ind) { + u32 temp_cpu_buf = port->cpu_buf; + + buf_addr = (u64)(port->phys) + + port->buf_size * (port->cpu_buf); + cmd_write.buf_addr_lsw = lower_32_bits(buf_addr); + cmd_write.buf_addr_msw = + msm_audio_populate_upper_32_bits(buf_addr); + cmd_write.seq_id = port->cpu_buf; + cmd_write.hdr.token = port->cpu_buf; + + ++(port->cpu_buf); + if (port->cpu_buf == port->buf_cnt) + port->cpu_buf = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write); + + if (rc < 0) { + port->cpu_buf = temp_cpu_buf; + pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n", + __func__, cmd_write.hdr.opcode, + rc, port->cpu_buf); + break; + } + + rc = 0; + } + + return rc; +} + +bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region) +{ + struct us_port_data *port = NULL; + u32 cpu_buf = 0; + + if ((usc == NULL) || !free_region) { + pr_err("%s: input data wrong\n", __func__); + return false; + } + port = &usc->port[IN]; + cpu_buf = port->cpu_buf + 1; + if (cpu_buf == port->buf_cnt) + cpu_buf = 0; + + *free_region = port->dsp_buf; + + return cpu_buf == *free_region; +} + +int q6usm_cmd(struct us_client *usc, int cmd) +{ + struct apr_hdr hdr; + int rc = 0; + atomic_t *state; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + q6usm_add_hdr(usc, &hdr, sizeof(hdr), true); + switch (cmd) { + case CMD_CLOSE: + hdr.opcode = USM_STREAM_CMD_CLOSE; + state = &usc->cmd_state; + break; + + default: + pr_err("%s:Invalid format[%d]\n", __func__, cmd); + goto fail_cmd; + } + + rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + } else + rc = 0; +fail_cmd: + return rc; +} + +int q6usm_set_us_detection(struct us_client *usc, + struct usm_session_cmd_detect_info *detect_info, + uint16_t detect_info_size) +{ + int rc = 0; + + if ((usc == NULL) || + (detect_info_size == 0) || + (detect_info == NULL)) { + pr_err("%s: wrong input: usc=0x%pK, inf_size=%d; info=0x%pK", + __func__, + usc, + detect_info_size, + detect_info); + return -EINVAL; + } + + q6usm_add_hdr(usc, &detect_info->hdr, detect_info_size, true); + + detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE; + + rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info); + if (rc < 0) { + pr_err("%s:Comamnd signal detect failed\n", __func__); + return -EINVAL; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +int q6usm_set_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size) +{ + int rc = 0; + struct usm_stream_cmd_set_param cmd_set_param; + struct us_port_data *port = NULL; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[dir]; + + q6usm_add_hdr(usc, &cmd_set_param.hdr, sizeof(cmd_set_param), true); + + cmd_set_param.hdr.opcode = USM_STREAM_CMD_SET_PARAM; + cmd_set_param.buf_size = buf_size; + cmd_set_param.buf_addr_msw = + msm_audio_populate_upper_32_bits(port->param_phys); + cmd_set_param.buf_addr_lsw = lower_32_bits(port->param_phys); + cmd_set_param.mem_map_handle = + *((uint32_t *)(port->param_buf_mem_handle)); + cmd_set_param.module_id = module_id; + cmd_set_param.param_id = param_id; + cmd_set_param.hdr.token = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_set_param); + + if (rc < 0) { + pr_err("%s:write op[0x%x];rc[%d]\n", + __func__, cmd_set_param.hdr.opcode, rc); + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_SET_PARAM: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +int q6usm_get_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size) +{ + int rc = 0; + struct usm_stream_cmd_get_param cmd_get_param; + struct us_port_data *port = NULL; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[dir]; + + q6usm_add_hdr(usc, &cmd_get_param.hdr, sizeof(cmd_get_param), true); + + cmd_get_param.hdr.opcode = USM_STREAM_CMD_GET_PARAM; + cmd_get_param.buf_size = buf_size; + cmd_get_param.buf_addr_msw = + msm_audio_populate_upper_32_bits(port->param_phys); + cmd_get_param.buf_addr_lsw = lower_32_bits(port->param_phys); + cmd_get_param.mem_map_handle = + *((uint32_t *)(port->param_buf_mem_handle)); + cmd_get_param.module_id = module_id; + cmd_get_param.param_id = param_id; + cmd_get_param.hdr.token = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_get_param); + + if (rc < 0) { + pr_err("%s:write op[0x%x];rc[%d]\n", + __func__, cmd_get_param.hdr.opcode, rc); + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_GET_PARAM: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +static int __init q6usm_init(void) +{ + pr_debug("%s\n", __func__); + init_waitqueue_head(&this_mmap.cmd_wait); + memset(session, 0, sizeof(session)); + return 0; +} + +device_initcall(q6usm_init); diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h new file mode 100644 index 000000000000..d45d1657c924 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __Q6_USM_H__ +#define __Q6_USM_H__ + +#include + +#define Q6USM_EVENT_UNDEF 0 +#define Q6USM_EVENT_READ_DONE 1 +#define Q6USM_EVENT_WRITE_DONE 2 +#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3 + +/* cyclic buffer with 1 gap support */ +#define USM_MIN_BUF_CNT 3 + +#define FORMAT_USPS_EPOS 0x00000000 +#define FORMAT_USRAW 0x00000001 +#define FORMAT_USPROX 0x00000002 +#define FORMAT_USGES_SYNC 0x00000003 +#define FORMAT_USRAW_SYNC 0x00000004 +#define INVALID_FORMAT 0xffffffff + +#define IN 0x000 +#define OUT 0x001 + +#define USM_WRONG_TOKEN 0xffffffff +#define USM_UNDEF_TOKEN 0xfffffffe + +#define CMD_CLOSE 0x0004 + +/* bit 0:1 represents priority of stream */ +#define STREAM_PRIORITY_NORMAL 0x0000 +#define STREAM_PRIORITY_LOW 0x0001 +#define STREAM_PRIORITY_HIGH 0x0002 + +/* bit 4 represents META enable of encoded data buffer */ +#define BUFFER_META_ENABLE 0x0010 + +struct us_port_data { + dma_addr_t phys; + /* cyclic region of buffers with 1 gap */ + void *data; + /* number of buffers in the region */ + uint32_t buf_cnt; + /* size of buffer */ + size_t buf_size; + /* write index */ + uint32_t dsp_buf; + /* read index */ + uint32_t cpu_buf; + /* expected token from dsp */ + uint32_t expected_token; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; + /* ION memory handle */ + struct ion_handle *handle; + /* ION memory client */ + struct ion_client *client; + /* extended parameters, related to q6 variants */ + void *ext; + /* physical address of parameter buffer */ + dma_addr_t param_phys; + /* buffer which stores the parameter data */ + void *param_buf; + /* size of parameter buffer */ + uint32_t param_buf_size; + /* parameter buffer memory handle */ + void *param_buf_mem_handle; + /* ION memory handle for parameter buffer */ + struct ion_handle *param_handle; + /* ION memory client for parameter buffer */ + struct ion_client *param_client; +}; + +struct us_client { + int session; + /* idx:1 out port, 0: in port*/ + struct us_port_data port[2]; + + struct apr_svc *apr; + struct mutex cmd_lock; + + atomic_t cmd_state; + atomic_t eos_state; + wait_queue_head_t cmd_wait; + + void (*cb)(uint32_t, uint32_t, uint32_t *, void *); + void *priv; +}; + +int q6usm_run(struct us_client *usc, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); +int q6usm_cmd(struct us_client *usc, int cmd); +int q6usm_us_client_buf_alloc(unsigned int dir, struct us_client *usc, + unsigned int bufsz, unsigned int bufcnt); +int q6usm_us_param_buf_alloc(unsigned int dir, struct us_client *usc, + unsigned int bufsz); +int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg); +int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg); +int q6usm_read(struct us_client *usc, uint32_t read_ind); +struct us_client *q6usm_us_client_alloc( + void (*cb)(uint32_t, uint32_t, uint32_t *, void *), + void *priv); +int q6usm_open_read(struct us_client *usc, uint32_t format); +void q6usm_us_client_free(struct us_client *usc); +uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc, + struct vm_area_struct *vms); +int q6usm_open_write(struct us_client *usc, uint32_t format); +int q6usm_write(struct us_client *usc, uint32_t write_ind); +bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region); +int q6usm_set_us_detection(struct us_client *usc, + struct usm_session_cmd_detect_info *detect_info, + uint16_t detect_info_size); +int q6usm_set_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size); +int q6usm_get_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size); + +#endif /* __Q6_USM_H__ */ diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c new file mode 100644 index 000000000000..c964dcbca9d9 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c @@ -0,0 +1,2468 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6usm.h" +#include "usfcdev.h" + +/* The driver version*/ +#define DRV_VERSION "1.7.1" +#define USF_VERSION_ID 0x0171 + +/* Standard timeout in the asynchronous ops */ +#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */ + +/* Undefined USF device */ +#define USF_UNDEF_DEV_ID 0xffff + +/* TX memory mapping flag */ +#define USF_VM_READ 1 +/* RX memory mapping flag */ +#define USF_VM_WRITE 2 + +/* Number of events, copied from the user space to kernel one */ +#define USF_EVENTS_PORTION_SIZE 20 + +/* Indexes in range definitions */ +#define MIN_IND 0 +#define MAX_IND 1 + +/* The coordinates indexes */ +#define X_IND 0 +#define Y_IND 1 +#define Z_IND 2 + +/* Shared memory limits */ +/* max_buf_size = (port_size(65535*2) * port_num(8) * group_size(3) */ +#define USF_MAX_BUF_SIZE 3145680 +#define USF_MAX_BUF_NUM 32 + +/* max size for buffer set from user space */ +#define USF_MAX_USER_BUF_SIZE 100000 + +/* Place for opreation result, received from QDSP6 */ +#define APR_RESULT_IND 1 + +/* Place for US detection result, received from QDSP6 */ +#define APR_US_DETECT_RESULT_IND 0 + +#define BITS_IN_BYTE 8 + +/* Time to stay awake after tx read event (e.g., proximity) */ +#define STAY_AWAKE_AFTER_READ_MSECS 3000 + +/* The driver states */ +enum usf_state_type { + USF_IDLE_STATE, + USF_OPENED_STATE, + USF_CONFIGURED_STATE, + USF_WORK_STATE, + USF_ADSP_RESTART_STATE, + USF_ERROR_STATE +}; + +/* The US detection status upon FW/HW based US detection results */ +enum usf_us_detect_type { + USF_US_DETECT_UNDEF, + USF_US_DETECT_YES, + USF_US_DETECT_NO +}; + +struct usf_xx_type { + /* Name of the client - event calculator */ + char client_name[USF_MAX_CLIENT_NAME_SIZE]; + /* The driver state in TX or RX direction */ + enum usf_state_type usf_state; + /* wait for q6 events mechanism */ + wait_queue_head_t wait; + /* IF with q6usm info */ + struct us_client *usc; + /* Q6:USM' Encoder/decoder configuration */ + struct us_encdec_cfg encdec_cfg; + /* Shared buffer (with Q6:USM) size */ + uint32_t buffer_size; + /* Number of the shared buffers (with Q6:USM) */ + uint32_t buffer_count; + /* Shared memory (Cyclic buffer with 1 gap) control */ + uint32_t new_region; + uint32_t prev_region; + /* Q6:USM's events handler */ + void (*cb)(uint32_t, uint32_t, uint32_t *, void *); + /* US detection result */ + enum usf_us_detect_type us_detect_type; + /* User's update info isn't acceptable */ + u8 user_upd_info_na; +}; + +struct usf_type { + /* TX device component configuration & control */ + struct usf_xx_type usf_tx; + /* RX device component configuration & control */ + struct usf_xx_type usf_rx; + /* Index into the opened device container */ + /* To prevent mutual usage of the same device */ + uint16_t dev_ind; + /* Event types, supported by device */ + uint16_t event_types; + /* The input devices are "input" module registered clients */ + struct input_dev *input_ifs[USF_MAX_EVENT_IND]; + /* Bitmap of types of events, conflicting to USF's ones */ + uint16_t conflicting_event_types; + /* Bitmap of types of events from devs, conflicting with USF */ + uint16_t conflicting_event_filters; + /* The requested buttons bitmap */ + uint16_t req_buttons_bitmap; + /* Mutex for exclusive operations (all public APIs) */ + struct mutex mutex; +}; + +struct usf_input_dev_type { + /* Input event type, supported by the input device */ + uint16_t event_type; + /* Input device name */ + const char *input_dev_name; + /* Input device registration function */ + int (*prepare_dev)(uint16_t, struct usf_type *, + struct us_input_info_type *, + const char *); + /* Input event notification function */ + void (*notify_event)(struct usf_type *, + uint16_t, + struct usf_event_type * + ); +}; + + +/* The MAX number of the supported devices */ +#define MAX_DEVS_NUMBER 1 + +/* + * code for a special button that is used to show/hide a + * hovering cursor in the input framework. Must be in + * sync with the button code definition in the framework + * (EventHub.h) + */ +#define BTN_USF_HOVERING_CURSOR 0x230 + +/* Supported buttons container */ +static const int s_button_map[] = { + BTN_STYLUS, + BTN_STYLUS2, + BTN_TOOL_PEN, + BTN_TOOL_RUBBER, + BTN_TOOL_FINGER, + BTN_USF_HOVERING_CURSOR +}; + +/* The opened devices container */ +static int s_opened_devs[MAX_DEVS_NUMBER]; + +static struct wakeup_source usf_wakeup_source; + +#define USF_NAME_PREFIX "usf_" +#define USF_NAME_PREFIX_SIZE 4 + + +static struct input_dev *allocate_dev(uint16_t ind, const char *name) +{ + struct input_dev *in_dev = input_allocate_device(); + + if (in_dev == NULL) { + pr_err("%s: input_allocate_device() failed\n", __func__); + } else { + /* Common part configuration */ + in_dev->name = name; + in_dev->phys = NULL; + in_dev->id.bustype = BUS_HOST; + in_dev->id.vendor = 0x0001; + in_dev->id.product = 0x0001; + in_dev->id.version = USF_VERSION_ID; + } + return in_dev; +} + +static int prepare_tsc_input_device(uint16_t ind, + struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + int i = 0; + + int num_buttons = min(ARRAY_SIZE(s_button_map), + sizeof(input_info->req_buttons_bitmap) * + BITS_IN_BYTE); + uint16_t max_buttons_bitmap = ((1 << ARRAY_SIZE(s_button_map)) - 1); + + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + if (input_info->req_buttons_bitmap > max_buttons_bitmap) { + pr_err("%s: Requested buttons[%d] exceeds max buttons available[%d]\n", + __func__, + input_info->req_buttons_bitmap, + max_buttons_bitmap); + input_free_device(in_dev); + return -EINVAL; + } + + usf_info->input_ifs[ind] = in_dev; + usf_info->req_buttons_bitmap = + input_info->req_buttons_bitmap; + in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + for (i = 0; i < num_buttons; i++) + if (input_info->req_buttons_bitmap & (1 << i)) + in_dev->keybit[BIT_WORD(s_button_map[i])] |= + BIT_MASK(s_button_map[i]); + + input_set_abs_params(in_dev, ABS_X, + input_info->tsc_x_dim[MIN_IND], + input_info->tsc_x_dim[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_Y, + input_info->tsc_y_dim[MIN_IND], + input_info->tsc_y_dim[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_DISTANCE, + input_info->tsc_z_dim[MIN_IND], + input_info->tsc_z_dim[MAX_IND], + 0, 0); + + input_set_abs_params(in_dev, ABS_PRESSURE, + input_info->tsc_pressure[MIN_IND], + input_info->tsc_pressure[MAX_IND], + 0, 0); + + input_set_abs_params(in_dev, ABS_TILT_X, + input_info->tsc_x_tilt[MIN_IND], + input_info->tsc_x_tilt[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_TILT_Y, + input_info->tsc_y_tilt[MIN_IND], + input_info->tsc_y_tilt[MAX_IND], + 0, 0); + + return 0; +} + +static int prepare_mouse_input_device(uint16_t ind, struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + + in_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT) | + BIT_MASK(BTN_MIDDLE); + in_dev->relbit[0] = BIT_MASK(REL_X) | + BIT_MASK(REL_Y) | + BIT_MASK(REL_Z); + + return 0; +} + +static int prepare_keyboard_input_device( + uint16_t ind, + struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] |= BIT_MASK(EV_KEY); + /* All keys are permitted */ + memset(in_dev->keybit, 0xff, sizeof(in_dev->keybit)); + + return 0; +} + +static void notify_tsc_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) + +{ + int i = 0; + int num_buttons = min(ARRAY_SIZE(s_button_map), + sizeof(usf_info->req_buttons_bitmap) * + BITS_IN_BYTE); + + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct point_event_type *pe = &(event->event_data.point_event); + + input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]); + input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]); + input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]); + + input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]); + input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]); + + input_report_abs(input_if, ABS_PRESSURE, pe->pressure); + input_report_key(input_if, BTN_TOUCH, !!(pe->pressure)); + + for (i = 0; i < num_buttons; i++) { + uint16_t mask = (1 << i), + btn_state = !!(pe->buttons_state_bitmap & mask); + if (usf_info->req_buttons_bitmap & mask) + input_report_key(input_if, s_button_map[i], btn_state); + } + + input_sync(input_if); + + pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d], buttons[%d]\n", + __func__, + pe->coordinates[X_IND], + pe->coordinates[Y_IND], + pe->coordinates[Z_IND], + pe->inclinations[X_IND], + pe->inclinations[Y_IND], + pe->pressure, + pe->buttons_state_bitmap); +} + +static void notify_mouse_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct mouse_event_type *me = &(event->event_data.mouse_event); + + input_report_rel(input_if, REL_X, me->rels[X_IND]); + input_report_rel(input_if, REL_Y, me->rels[Y_IND]); + input_report_rel(input_if, REL_Z, me->rels[Z_IND]); + + input_report_key(input_if, BTN_LEFT, + me->buttons_states & USF_BUTTON_LEFT_MASK); + input_report_key(input_if, BTN_MIDDLE, + me->buttons_states & USF_BUTTON_MIDDLE_MASK); + input_report_key(input_if, BTN_RIGHT, + me->buttons_states & USF_BUTTON_RIGHT_MASK); + + input_sync(input_if); + + pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n", + __func__, me->rels[X_IND], + me->rels[Y_IND], me->buttons_states); +} + +static void notify_key_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct key_event_type *ke = &(event->event_data.key_event); + + input_report_key(input_if, ke->key, ke->key_state); + input_sync(input_if); + pr_debug("%s: key event: key[%d], state[%d]\n", + __func__, + ke->key, + ke->key_state); + +} + +static struct usf_input_dev_type s_usf_input_devs[] = { + {USF_TSC_EVENT, "usf_tsc", + prepare_tsc_input_device, notify_tsc_event}, + {USF_TSC_PTR_EVENT, "usf_tsc_ptr", + prepare_tsc_input_device, notify_tsc_event}, + {USF_MOUSE_EVENT, "usf_mouse", + prepare_mouse_input_device, notify_mouse_event}, + {USF_KEYBOARD_EVENT, "usf_kb", + prepare_keyboard_input_device, notify_key_event}, + {USF_TSC_EXT_EVENT, "usf_tsc_ext", + prepare_tsc_input_device, notify_tsc_event}, +}; + +static void usf_rx_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv; + + if (usf_xx == NULL) { + pr_err("%s: the private data is NULL\n", __func__); + return; + } + + switch (opcode) { + case Q6USM_EVENT_WRITE_DONE: + wake_up(&usf_xx->wait); + break; + + case RESET_EVENTS: + pr_err("%s: received RESET_EVENTS\n", __func__); + usf_xx->usf_state = USF_ADSP_RESTART_STATE; + wake_up(&usf_xx->wait); + break; + + default: + break; + } +} + +static void usf_tx_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv; + + if (usf_xx == NULL) { + pr_err("%s: the private data is NULL\n", __func__); + return; + } + + switch (opcode) { + case Q6USM_EVENT_READ_DONE: + pr_debug("%s: acquiring %d msec wake lock\n", __func__, + STAY_AWAKE_AFTER_READ_MSECS); + __pm_wakeup_event(&usf_wakeup_source, + STAY_AWAKE_AFTER_READ_MSECS); + if (token == USM_WRONG_TOKEN) + usf_xx->usf_state = USF_ERROR_STATE; + usf_xx->new_region = token; + wake_up(&usf_xx->wait); + break; + + case Q6USM_EVENT_SIGNAL_DETECT_RESULT: + usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ? + USF_US_DETECT_YES : + USF_US_DETECT_NO; + + wake_up(&usf_xx->wait); + break; + + case APR_BASIC_RSP_RESULT: + if (payload[APR_RESULT_IND]) { + usf_xx->usf_state = USF_ERROR_STATE; + usf_xx->new_region = USM_WRONG_TOKEN; + wake_up(&usf_xx->wait); + } + break; + + case RESET_EVENTS: + pr_err("%s: received RESET_EVENTS\n", __func__); + usf_xx->usf_state = USF_ADSP_RESTART_STATE; + wake_up(&usf_xx->wait); + break; + + default: + break; + } +} + +static void release_xx(struct usf_xx_type *usf_xx) +{ + if (usf_xx != NULL) { + if (usf_xx->usc) { + q6usm_us_client_free(usf_xx->usc); + usf_xx->usc = NULL; + } + + if (usf_xx->encdec_cfg.params != NULL) { + kfree(usf_xx->encdec_cfg.params); + usf_xx->encdec_cfg.params = NULL; + } + } +} + +static void usf_disable(struct usf_xx_type *usf_xx) +{ + if (usf_xx != NULL) { + if ((usf_xx->usf_state != USF_IDLE_STATE) && + (usf_xx->usf_state != USF_OPENED_STATE)) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + usf_xx->usf_state = USF_OPENED_STATE; + wake_up(&usf_xx->wait); + } + release_xx(usf_xx); + } +} + +static int config_xx(struct usf_xx_type *usf_xx, struct us_xx_info_type *config) +{ + int rc = 0; + uint16_t data_map_size = 0; + uint16_t min_map_size = 0; + + if ((usf_xx == NULL) || + (config == NULL)) + return -EINVAL; + + if ((config->buf_size == 0) || + (config->buf_size > USF_MAX_BUF_SIZE) || + (config->buf_num == 0) || + (config->buf_num > USF_MAX_BUF_NUM)) { + pr_err("%s: wrong params: buf_size=%d; buf_num=%d\n", + __func__, config->buf_size, config->buf_num); + return -EINVAL; + } + + data_map_size = sizeof(usf_xx->encdec_cfg.cfg_common.data_map); + min_map_size = min(data_map_size, config->port_cnt); + + if (config->client_name != NULL) { + if (strncpy_from_user(usf_xx->client_name, + (char __user *)config->client_name, + sizeof(usf_xx->client_name) - 1) < 0) { + pr_err("%s: get client name failed\n", __func__); + return -EINVAL; + } + } + + pr_debug("%s: name=%s; buf_size:%d; dev_id:0x%x; sample_rate:%d\n", + __func__, usf_xx->client_name, config->buf_size, + config->dev_id, config->sample_rate); + + pr_debug("%s: buf_num:%d; format:%d; port_cnt:%d; data_size=%d\n", + __func__, config->buf_num, config->stream_format, + config->port_cnt, config->params_data_size); + + pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d,\n", + __func__, + config->port_id[0], + config->port_id[1], + config->port_id[2], + config->port_id[3], + config->port_id[4]); + + pr_debug("id[5]=%d, id[6]=%d, id[7]=%d\n", + config->port_id[5], + config->port_id[6], + config->port_id[7]); + + /* q6usm allocation & configuration */ + usf_xx->buffer_size = config->buf_size; + usf_xx->buffer_count = config->buf_num; + usf_xx->encdec_cfg.cfg_common.bits_per_sample = + config->bits_per_sample; + usf_xx->encdec_cfg.cfg_common.sample_rate = config->sample_rate; + /* AFE port e.g. AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX */ + usf_xx->encdec_cfg.cfg_common.dev_id = config->dev_id; + + usf_xx->encdec_cfg.cfg_common.ch_cfg = config->port_cnt; + memcpy((void *)&usf_xx->encdec_cfg.cfg_common.data_map, + (void *)config->port_id, + min_map_size); + + usf_xx->encdec_cfg.format_id = config->stream_format; + usf_xx->encdec_cfg.params_size = config->params_data_size; + usf_xx->user_upd_info_na = 1; /* it's used in US_GET_TX_UPDATE */ + + if (config->params_data_size > 0) { /* transparent data copy */ + usf_xx->encdec_cfg.params = kzalloc(config->params_data_size, + GFP_KERNEL); + /* False memory leak here - pointer in packed struct + * is undetected by kmemleak tool + */ + kmemleak_ignore(usf_xx->encdec_cfg.params); + if (usf_xx->encdec_cfg.params == NULL) { + pr_err("%s: params memory alloc[%d] failure\n", + __func__, + config->params_data_size); + return -ENOMEM; + } + rc = copy_from_user(usf_xx->encdec_cfg.params, + (uint8_t __user *)config->params_data, + config->params_data_size); + if (rc) { + pr_err("%s: transparent data copy failure\n", + __func__); + kfree(usf_xx->encdec_cfg.params); + usf_xx->encdec_cfg.params = NULL; + return -EFAULT; + } + pr_debug("%s: params_size[%d]; params[%d,%d,%d,%d, %d]\n", + __func__, + config->params_data_size, + usf_xx->encdec_cfg.params[0], + usf_xx->encdec_cfg.params[1], + usf_xx->encdec_cfg.params[2], + usf_xx->encdec_cfg.params[3], + usf_xx->encdec_cfg.params[4] + ); + } + + usf_xx->usc = q6usm_us_client_alloc(usf_xx->cb, (void *)usf_xx); + if (!usf_xx->usc) { + pr_err("%s: Could not allocate q6usm client\n", __func__); + rc = -EFAULT; + } + + return rc; +} + +static bool usf_match(uint16_t event_type_ind, struct input_dev *dev) +{ + bool rc = false; + + rc = (event_type_ind < MAX_EVENT_TYPE_NUM) && + ((dev->name == NULL) || + strcmp(dev->name, USF_NAME_PREFIX)); + pr_debug("%s: name=[%s]; rc=%d\n", + __func__, dev->name, rc); + + return rc; +} + +static bool usf_register_conflicting_events(uint16_t event_types) +{ + bool rc = true; + uint16_t ind = 0; + uint16_t mask = 1; + + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (event_types & mask) { + rc = usfcdev_register(ind, usf_match); + if (!rc) + break; + } + mask = mask << 1; + } + + return rc; +} + +static void usf_unregister_conflicting_events(uint16_t event_types) +{ + uint16_t ind = 0; + uint16_t mask = 1; + + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (event_types & mask) + usfcdev_unregister(ind); + mask = mask << 1; + } +} + +static void usf_set_event_filters(struct usf_type *usf, uint16_t event_filters) +{ + uint16_t ind = 0; + uint16_t mask = 1; + + if (usf->conflicting_event_filters != event_filters) { + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (usf->conflicting_event_types & mask) + usfcdev_set_filter(ind, event_filters&mask); + mask = mask << 1; + } + usf->conflicting_event_filters = event_filters; + } +} + +static int register_input_device(struct usf_type *usf_info, + struct us_input_info_type *input_info) +{ + int rc = 0; + bool ret = true; + uint16_t ind = 0; + + if ((usf_info == NULL) || + (input_info == NULL) || + !(input_info->event_types & USF_ALL_EVENTS)) { + pr_err("%s: wrong input parameter(s)\n", __func__); + return -EINVAL; + } + + for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) { + if (usf_info->input_ifs[ind] != NULL) { + pr_err("%s: input_if[%d] is already allocated\n", + __func__, ind); + return -EFAULT; + } + if ((input_info->event_types & + s_usf_input_devs[ind].event_type) && + s_usf_input_devs[ind].prepare_dev) { + rc = (*s_usf_input_devs[ind].prepare_dev)( + ind, + usf_info, + input_info, + s_usf_input_devs[ind].input_dev_name); + if (rc) + return rc; + + rc = input_register_device(usf_info->input_ifs[ind]); + if (rc) { + pr_err("%s: input_reg_dev() failed; rc=%d\n", + __func__, rc); + input_free_device(usf_info->input_ifs[ind]); + usf_info->input_ifs[ind] = NULL; + } else { + usf_info->event_types |= + s_usf_input_devs[ind].event_type; + pr_debug("%s: input device[%s] was registered\n", + __func__, + s_usf_input_devs[ind].input_dev_name); + } + } /* supported event */ + } /* event types loop */ + + ret = usf_register_conflicting_events( + input_info->conflicting_event_types); + if (ret) + usf_info->conflicting_event_types = + input_info->conflicting_event_types; + + return 0; +} + + +static void handle_input_event(struct usf_type *usf_info, + uint16_t event_counter, + struct usf_event_type __user *event) +{ + uint16_t ind = 0; + uint16_t events_num = 0; + struct usf_event_type usf_events[USF_EVENTS_PORTION_SIZE]; + int rc = 0; + + if ((usf_info == NULL) || + (event == NULL) || (!event_counter)) { + return; + } + + while (event_counter > 0) { + if (event_counter > USF_EVENTS_PORTION_SIZE) { + events_num = USF_EVENTS_PORTION_SIZE; + event_counter -= USF_EVENTS_PORTION_SIZE; + } else { + events_num = event_counter; + event_counter = 0; + } + rc = copy_from_user(usf_events, + (struct usf_event_type __user *)event, + events_num * sizeof(struct usf_event_type)); + if (rc) { + pr_err("%s: copy upd_rx_info from user; rc=%d\n", + __func__, rc); + return; + } + for (ind = 0; ind < events_num; ++ind) { + struct usf_event_type *p_event = &usf_events[ind]; + uint16_t if_ind = p_event->event_type_ind; + + if ((if_ind >= USF_MAX_EVENT_IND) || + (usf_info->input_ifs[if_ind] == NULL)) + continue; /* event isn't supported */ + + if (s_usf_input_devs[if_ind].notify_event) + (*s_usf_input_devs[if_ind].notify_event)( + usf_info, + if_ind, + p_event); + } /* loop in the portion */ + } /* all events loop */ +} + +static int usf_start_tx(struct usf_xx_type *usf_xx) +{ + int rc = q6usm_run(usf_xx->usc, 0, 0, 0); + + pr_debug("%s: tx: q6usm_run; rc=%d\n", __func__, rc); + if (!rc) { + if (usf_xx->buffer_count >= USM_MIN_BUF_CNT) { + /* supply all buffers */ + rc = q6usm_read(usf_xx->usc, + usf_xx->buffer_count); + pr_debug("%s: q6usm_read[%d]\n", + __func__, rc); + + if (rc) + pr_err("%s: buf read failed", + __func__); + else + usf_xx->usf_state = + USF_WORK_STATE; + } else + usf_xx->usf_state = + USF_WORK_STATE; + } + + return rc; +} /* usf_start_tx */ + +static int usf_start_rx(struct usf_xx_type *usf_xx) +{ + int rc = q6usm_run(usf_xx->usc, 0, 0, 0); + + pr_debug("%s: rx: q6usm_run; rc=%d\n", + __func__, rc); + if (!rc) + usf_xx->usf_state = USF_WORK_STATE; + + return rc; +} /* usf_start_rx */ + +static int __usf_set_us_detection(struct usf_type *usf, + struct us_detect_info_type *detect_info) +{ + uint32_t timeout = 0; + struct usm_session_cmd_detect_info *p_allocated_memory = NULL; + struct usm_session_cmd_detect_info usm_detect_info; + struct usm_session_cmd_detect_info *p_usm_detect_info = + &usm_detect_info; + uint32_t detect_info_size = sizeof(struct usm_session_cmd_detect_info); + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + if (detect_info->us_detector != US_DETECT_FW) { + pr_err("%s: unsupported detector: %d\n", + __func__, detect_info->us_detector); + return -EINVAL; + } + + if ((detect_info->params_data_size != 0) && + (detect_info->params_data != NULL)) { + uint8_t *p_data = NULL; + + detect_info_size += detect_info->params_data_size; + p_allocated_memory = kzalloc(detect_info_size, GFP_KERNEL); + if (p_allocated_memory == NULL) { + pr_err("%s: detect_info[%d] allocation failed\n", + __func__, detect_info_size); + return -ENOMEM; + } + p_usm_detect_info = p_allocated_memory; + p_data = (uint8_t *)p_usm_detect_info + + sizeof(struct usm_session_cmd_detect_info); + + rc = copy_from_user(p_data, + (uint8_t __user *)(detect_info->params_data), + detect_info->params_data_size); + if (rc) { + pr_err("%s: copy params from user; rc=%d\n", + __func__, rc); + kfree(p_allocated_memory); + return -EFAULT; + } + p_usm_detect_info->algorithm_cfg_size = + detect_info->params_data_size; + } else + usm_detect_info.algorithm_cfg_size = 0; + + p_usm_detect_info->detect_mode = detect_info->us_detect_mode; + p_usm_detect_info->skip_interval = detect_info->skip_time; + + usf_xx->us_detect_type = USF_US_DETECT_UNDEF; + + rc = q6usm_set_us_detection(usf_xx->usc, + p_usm_detect_info, + detect_info_size); + if (rc || (detect_info->detect_timeout == USF_NO_WAIT_TIMEOUT)) { + kfree(p_allocated_memory); + return rc; + } + + /* Get US detection result */ + if (detect_info->detect_timeout == USF_INFINITIVE_TIMEOUT) { + rc = wait_event_interruptible(usf_xx->wait, + (usf_xx->us_detect_type != + USF_US_DETECT_UNDEF) || + (usf_xx->usf_state == + USF_ADSP_RESTART_STATE)); + } else { + if (detect_info->detect_timeout == USF_DEFAULT_TIMEOUT) + timeout = USF_TIMEOUT_JIFFIES; + else + timeout = detect_info->detect_timeout * HZ; + } + rc = wait_event_interruptible_timeout(usf_xx->wait, + (usf_xx->us_detect_type != + USF_US_DETECT_UNDEF) || + (usf_xx->usf_state == + USF_ADSP_RESTART_STATE), timeout); + + /* In the case of aDSP restart, "no US" is assumed */ + if (usf_xx->usf_state == USF_ADSP_RESTART_STATE) + rc = -EFAULT; + + /* In the case of timeout, "no US" is assumed */ + if (rc < 0) + pr_err("%s: Getting US detection failed rc[%d]\n", + __func__, rc); + else { + usf->usf_rx.us_detect_type = usf->usf_tx.us_detect_type; + detect_info->is_us = + (usf_xx->us_detect_type == USF_US_DETECT_YES); + } + + kfree(p_allocated_memory); + + return rc; +} /* __usf_set_us_detection */ + +static int usf_set_us_detection(struct usf_type *usf, unsigned long arg) +{ + struct us_detect_info_type detect_info; + + int rc = copy_from_user(&detect_info, + (struct us_detect_info_type __user *) arg, + sizeof(detect_info)); + + if (rc) { + pr_err("%s: copy detect_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (detect_info.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + rc = __usf_set_us_detection(usf, &detect_info); + if (rc < 0) { + pr_err("%s: set us detection failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &detect_info, + sizeof(detect_info)); + if (rc) { + pr_err("%s: copy detect_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_us_detection */ + +static int __usf_set_tx_info(struct usf_type *usf, + struct us_tx_info_type *config_tx) +{ + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + usf_xx->new_region = USM_UNDEF_TOKEN; + usf_xx->prev_region = USM_UNDEF_TOKEN; + usf_xx->cb = usf_tx_cb; + + init_waitqueue_head(&usf_xx->wait); + + if (config_tx->us_xx_info.client_name != NULL) { + int res = strncpy_from_user( + usf_xx->client_name, + (char __user *)(config_tx->us_xx_info.client_name), + sizeof(usf_xx->client_name)-1); + if (res < 0) { + pr_err("%s: get client name failed\n", + __func__); + return -EINVAL; + } + } + + rc = config_xx(usf_xx, &(config_tx->us_xx_info)); + if (rc) + return rc; + + rc = q6usm_open_read(usf_xx->usc, + usf_xx->encdec_cfg.format_id); + if (rc) + return rc; + + rc = q6usm_us_client_buf_alloc(OUT, usf_xx->usc, + usf_xx->buffer_size, + usf_xx->buffer_count); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_us_param_buf_alloc(OUT, usf_xx->usc, + config_tx->us_xx_info.max_get_set_param_buf_size); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_enc_cfg_blk(usf_xx->usc, + &usf_xx->encdec_cfg); + if (!rc && + (config_tx->input_info.event_types != USF_NO_EVENT)) { + rc = register_input_device(usf, + &(config_tx->input_info)); + } + + if (rc) + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + else + usf_xx->usf_state = USF_CONFIGURED_STATE; + + return rc; +} /* __usf_set_tx_info */ + +static int usf_set_tx_info(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_info_type config_tx; + + int rc = copy_from_user(&config_tx, + (struct us_tx_info_type __user *) arg, + sizeof(config_tx)); + + if (rc) { + pr_err("%s: copy config_tx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (config_tx.us_xx_info.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + return __usf_set_tx_info(usf, &config_tx); +} /* usf_set_tx_info */ + +static int __usf_set_rx_info(struct usf_type *usf, + struct us_rx_info_type *config_rx) +{ + struct usf_xx_type *usf_xx = &usf->usf_rx; + int rc = 0; + + usf_xx->new_region = USM_UNDEF_TOKEN; + usf_xx->prev_region = USM_UNDEF_TOKEN; + + usf_xx->cb = usf_rx_cb; + + rc = config_xx(usf_xx, &(config_rx->us_xx_info)); + if (rc) + return rc; + + rc = q6usm_open_write(usf_xx->usc, + usf_xx->encdec_cfg.format_id); + if (rc) + return rc; + + rc = q6usm_us_client_buf_alloc( + IN, + usf_xx->usc, + usf_xx->buffer_size, + usf_xx->buffer_count); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_us_param_buf_alloc(IN, usf_xx->usc, + config_rx->us_xx_info.max_get_set_param_buf_size); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_dec_cfg_blk(usf_xx->usc, + &usf_xx->encdec_cfg); + if (rc) + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + else { + init_waitqueue_head(&usf_xx->wait); + usf_xx->usf_state = USF_CONFIGURED_STATE; + } + + return rc; +} /* __usf_set_rx_info */ + +static int usf_set_rx_info(struct usf_type *usf, unsigned long arg) +{ + struct us_rx_info_type config_rx; + + int rc = copy_from_user(&config_rx, + (struct us_rx_info_type __user *) arg, + sizeof(config_rx)); + + if (rc) { + pr_err("%s: copy config_rx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (config_rx.us_xx_info.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + return __usf_set_rx_info(usf, &config_rx); +} /* usf_set_rx_info */ + +static int __usf_get_tx_update(struct usf_type *usf, + struct us_tx_update_info_type *upd_tx_info) +{ + unsigned long prev_jiffies = 0; + uint32_t timeout = 0; + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + if (!usf_xx->user_upd_info_na) { + usf_set_event_filters(usf, upd_tx_info->event_filters); + handle_input_event(usf, + upd_tx_info->event_counter, + upd_tx_info->event); + + /* Release available regions */ + rc = q6usm_read(usf_xx->usc, + upd_tx_info->free_region); + if (rc) + return rc; + } else + usf_xx->user_upd_info_na = 0; + + /* Get data ready regions */ + if (upd_tx_info->timeout == USF_INFINITIVE_TIMEOUT) { + rc = wait_event_interruptible(usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE)); + } else { + if (upd_tx_info->timeout == USF_NO_WAIT_TIMEOUT) + rc = (usf_xx->prev_region != usf_xx->new_region); + else { + prev_jiffies = jiffies; + if (upd_tx_info->timeout == USF_DEFAULT_TIMEOUT) { + timeout = USF_TIMEOUT_JIFFIES; + rc = wait_event_timeout( + usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE), + timeout); + } else { + timeout = upd_tx_info->timeout * HZ; + rc = wait_event_interruptible_timeout( + usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE), + timeout); + } + } + if (!rc) { + pr_debug("%s: timeout. prev_j=%lu; j=%lu\n", + __func__, prev_jiffies, jiffies); + pr_debug("%s: timeout. prev=%d; new=%d\n", + __func__, usf_xx->prev_region, + usf_xx->new_region); + pr_debug("%s: timeout. free_region=%d;\n", + __func__, upd_tx_info->free_region); + if (usf_xx->prev_region == + usf_xx->new_region) { + pr_err("%s:read data: timeout\n", + __func__); + return -ETIME; + } + } + } + + if ((usf_xx->usf_state != USF_WORK_STATE) || + (rc == -ERESTARTSYS)) { + pr_err("%s: Get ready region failure; state[%d]; rc[%d]\n", + __func__, usf_xx->usf_state, rc); + return -EINTR; + } + + upd_tx_info->ready_region = usf_xx->new_region; + usf_xx->prev_region = upd_tx_info->ready_region; + + if (upd_tx_info->ready_region == USM_WRONG_TOKEN) { + pr_err("%s: TX path corrupted; prev=%d\n", + __func__, usf_xx->prev_region); + return -EIO; + } + + return rc; +} /* __usf_get_tx_update */ + +static int usf_get_tx_update(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_update_info_type upd_tx_info; + + int rc = copy_from_user(&upd_tx_info, + (struct us_tx_update_info_type __user *) arg, + sizeof(upd_tx_info)); + + if (rc < 0) { + pr_err("%s: copy upd_tx_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = __usf_get_tx_update(usf, &upd_tx_info); + if (rc < 0) { + pr_err("%s: get tx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &upd_tx_info, + sizeof(upd_tx_info)); + if (rc) { + pr_err("%s: copy upd_tx_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_tx_update */ + +static int __usf_set_rx_update(struct usf_xx_type *usf_xx, + struct us_rx_update_info_type *upd_rx_info) +{ + int rc = 0; + + /* Send available data regions */ + if (upd_rx_info->ready_region != + usf_xx->buffer_count) { + rc = q6usm_write( + usf_xx->usc, + upd_rx_info->ready_region); + if (rc) + return rc; + } + + /* Get free regions */ + rc = wait_event_timeout( + usf_xx->wait, + !q6usm_is_write_buf_full( + usf_xx->usc, + &(upd_rx_info->free_region)) || + (usf_xx->usf_state == USF_IDLE_STATE), + USF_TIMEOUT_JIFFIES); + + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. wait for write buf not full\n", + __func__); + } else { + if (usf_xx->usf_state != + USF_WORK_STATE) { + pr_err("%s: RX: state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EINTR; + } + } + + return rc; +} /* __usf_set_rx_update */ + +static int usf_set_rx_update(struct usf_xx_type *usf_xx, unsigned long arg) +{ + struct us_rx_update_info_type upd_rx_info; + + int rc = copy_from_user(&upd_rx_info, + (struct us_rx_update_info_type __user *) arg, + sizeof(upd_rx_info)); + + if (rc) { + pr_err("%s: copy upd_rx_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = __usf_set_rx_update(usf_xx, &upd_rx_info); + if (rc < 0) { + pr_err("%s: set rx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &upd_rx_info, + sizeof(upd_rx_info)); + if (rc) { + pr_err("%s: copy rx_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_rx_update */ + +static void usf_release_input(struct usf_type *usf) +{ + uint16_t ind = 0; + + usf_unregister_conflicting_events( + usf->conflicting_event_types); + usf->conflicting_event_types = 0; + for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) { + if (usf->input_ifs[ind] == NULL) + continue; + input_unregister_device(usf->input_ifs[ind]); + usf->input_ifs[ind] = NULL; + pr_debug("%s input_unregister_device[%s]\n", + __func__, + s_usf_input_devs[ind].input_dev_name); + } +} /* usf_release_input */ + +static int usf_stop_tx(struct usf_type *usf) +{ + struct usf_xx_type *usf_xx = &usf->usf_tx; + + usf_release_input(usf); + usf_disable(usf_xx); + + return 0; +} /* usf_stop_tx */ + +static int __usf_get_version(struct us_version_info_type *version_info) +{ + int rc = 0; + + if (version_info->buf_size < sizeof(DRV_VERSION)) { + pr_err("%s: buf_size (%d) < version string size (%zu)\n", + __func__, version_info->buf_size, sizeof(DRV_VERSION)); + return -EINVAL; + } + + rc = copy_to_user((void __user *)(version_info->pbuf), + DRV_VERSION, + sizeof(DRV_VERSION)); + if (rc) { + pr_err("%s: copy to version_info.pbuf; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* __usf_get_version */ + +static int usf_get_version(unsigned long arg) +{ + struct us_version_info_type version_info; + + int rc = copy_from_user(&version_info, + (struct us_version_info_type __user *) arg, + sizeof(version_info)); + + if (rc) { + pr_err("%s: copy version_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = __usf_get_version(&version_info); + if (rc < 0) { + pr_err("%s: get version failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &version_info, + sizeof(version_info)); + if (rc) { + pr_err("%s: copy version_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_version */ + +static int __usf_set_stream_param(struct usf_xx_type *usf_xx, + struct us_stream_param_type *set_stream_param, + int dir) +{ + struct us_client *usc = usf_xx->usc; + struct us_port_data *port; + int rc = 0; + + if (usc == NULL) { + pr_err("%s: usc is null\n", + __func__); + return -EFAULT; + } + + port = &usc->port[dir]; + if (port == NULL) { + pr_err("%s: port is null\n", + __func__); + return -EFAULT; + } + + if (port->param_buf == NULL) { + pr_err("%s: parameter buffer is null\n", + __func__); + return -EFAULT; + } + + if (set_stream_param->buf_size > port->param_buf_size) { + pr_err("%s: buf_size (%d) > maximum buf size (%d)\n", + __func__, set_stream_param->buf_size, + port->param_buf_size); + return -EINVAL; + } + + if (set_stream_param->buf_size == 0) { + pr_err("%s: buf_size is 0\n", __func__); + return -EINVAL; + } + + rc = copy_from_user(port->param_buf, + (uint8_t __user *) set_stream_param->pbuf, + set_stream_param->buf_size); + if (rc) { + pr_err("%s: copy param buf from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = q6usm_set_us_stream_param(dir, usc, set_stream_param->module_id, + set_stream_param->param_id, + set_stream_param->buf_size); + if (rc) { + pr_err("%s: q6usm_set_us_stream_param failed; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return rc; +} + +static int usf_set_stream_param(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type set_stream_param; + int rc = 0; + + rc = copy_from_user(&set_stream_param, + (struct us_stream_param_type __user *) arg, + sizeof(set_stream_param)); + + if (rc) { + pr_err("%s: copy set_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return __usf_set_stream_param(usf_xx, &set_stream_param, dir); +} /* usf_set_stream_param */ + +static int __usf_get_stream_param(struct usf_xx_type *usf_xx, + struct us_stream_param_type *get_stream_param, + int dir) +{ + struct us_client *usc = usf_xx->usc; + struct us_port_data *port; + int rc = 0; + + if (usc == NULL) { + pr_err("%s: us_client is null\n", + __func__); + return -EFAULT; + } + + port = &usc->port[dir]; + + if (port->param_buf == NULL) { + pr_err("%s: parameter buffer is null\n", + __func__); + return -EFAULT; + } + + if (get_stream_param->buf_size > port->param_buf_size) { + pr_err("%s: buf_size (%d) > maximum buf size (%d)\n", + __func__, get_stream_param->buf_size, + port->param_buf_size); + return -EINVAL; + } + + if (get_stream_param->buf_size == 0) { + pr_err("%s: buf_size is 0\n", __func__); + return -EINVAL; + } + + rc = q6usm_get_us_stream_param(dir, usc, get_stream_param->module_id, + get_stream_param->param_id, + get_stream_param->buf_size); + if (rc) { + pr_err("%s: q6usm_get_us_stream_param failed; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = copy_to_user((uint8_t __user *) get_stream_param->pbuf, + port->param_buf, + get_stream_param->buf_size); + if (rc) { + pr_err("%s: copy param buf to user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return rc; +} + +static int usf_get_stream_param(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type get_stream_param; + int rc = 0; + + rc = copy_from_user(&get_stream_param, + (struct us_stream_param_type __user *) arg, + sizeof(get_stream_param)); + + if (rc) { + pr_err("%s: copy get_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return __usf_get_stream_param(usf_xx, &get_stream_param, dir); +} /* usf_get_stream_param */ + +static long __usf_ioctl(struct usf_type *usf, + unsigned int cmd, + unsigned long arg) +{ + + int rc = 0; + struct usf_xx_type *usf_xx = NULL; + + switch (cmd) { + case US_START_TX: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_CONFIGURED_STATE) + rc = usf_start_tx(usf_xx); + else { + pr_err("%s: start_tx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } + + case US_START_RX: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_CONFIGURED_STATE) + rc = usf_start_rx(usf_xx); + else { + pr_err("%s: start_rx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } + + case US_SET_TX_INFO: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_tx_info(usf, arg); + else { + pr_err("%s: set_tx_info: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_TX_INFO */ + + case US_SET_RX_INFO: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_rx_info(usf, arg); + else { + pr_err("%s: set_rx_info: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_RX_INFO */ + + case US_GET_TX_UPDATE: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_get_tx_update(usf, arg); + else { + pr_err("%s: get_tx_update: wrong state[%d]\n", __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_GET_TX_UPDATE */ + + case US_SET_RX_UPDATE: { + struct usf_xx_type *usf_xx = &usf->usf_rx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_rx_update(usf_xx, arg); + else { + pr_err("%s: set_rx_update: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_RX_UPDATE */ + + case US_STOP_TX: { + usf_xx = &usf->usf_tx; + if ((usf_xx->usf_state == USF_WORK_STATE) + || (usf_xx->usf_state == USF_ADSP_RESTART_STATE)) + rc = usf_stop_tx(usf); + else { + pr_err("%s: stop_tx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } /* US_STOP_TX */ + + case US_STOP_RX: { + usf_xx = &usf->usf_rx; + if ((usf_xx->usf_state == USF_WORK_STATE) + || (usf_xx->usf_state == USF_ADSP_RESTART_STATE)) + usf_disable(usf_xx); + else { + pr_err("%s: stop_rx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } /* US_STOP_RX */ + + case US_SET_DETECTION: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_us_detection(usf, arg); + else { + pr_err("%s: set us detection: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_DETECTION */ + + case US_GET_VERSION: { + rc = usf_get_version(arg); + break; + } /* US_GET_VERSION */ + + case US_SET_TX_STREAM_PARAM: { + rc = usf_set_stream_param(&usf->usf_tx, arg, OUT); + break; + } /* US_SET_TX_STREAM_PARAM */ + + case US_GET_TX_STREAM_PARAM: { + rc = usf_get_stream_param(&usf->usf_tx, arg, OUT); + break; + } /* US_GET_TX_STREAM_PARAM */ + + case US_SET_RX_STREAM_PARAM: { + rc = usf_set_stream_param(&usf->usf_rx, arg, IN); + break; + } /* US_SET_RX_STREAM_PARAM */ + + case US_GET_RX_STREAM_PARAM: { + rc = usf_get_stream_param(&usf->usf_rx, arg, IN); + break; + } /* US_GET_RX_STREAM_PARAM */ + + default: + pr_err("%s: unsupported IOCTL command [%d]\n", + __func__, + cmd); + rc = -ENOTTY; + break; + } + + if (rc && + ((cmd == US_SET_TX_INFO) || + (cmd == US_SET_RX_INFO))) + release_xx(usf_xx); + + return rc; +} /* __usf_ioctl */ + +static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usf_type *usf = file->private_data; + int rc = 0; + + mutex_lock(&usf->mutex); + rc = __usf_ioctl(usf, cmd, arg); + mutex_unlock(&usf->mutex); + + return rc; +} /* usf_ioctl */ + +#ifdef CONFIG_COMPAT + +#define US_SET_TX_INFO32 _IOW(USF_IOCTL_MAGIC, 0, \ + struct us_tx_info_type32) +#define US_GET_TX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 2, \ + struct us_tx_update_info_type32) +#define US_SET_RX_INFO32 _IOW(USF_IOCTL_MAGIC, 3, \ + struct us_rx_info_type32) +#define US_SET_RX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 4, \ + struct us_rx_update_info_type32) +#define US_SET_DETECTION32 _IOWR(USF_IOCTL_MAGIC, 8, \ + struct us_detect_info_type32) +#define US_GET_VERSION32 _IOWR(USF_IOCTL_MAGIC, 9, \ + struct us_version_info_type32) +#define US_SET_TX_STREAM_PARAM32 _IOW(USF_IOCTL_MAGIC, 10, \ + struct us_stream_param_type32) +#define US_GET_TX_STREAM_PARAM32 _IOWR(USF_IOCTL_MAGIC, 11, \ + struct us_stream_param_type32) +#define US_SET_RX_STREAM_PARAM32 _IOW(USF_IOCTL_MAGIC, 12, \ + struct us_stream_param_type32) +#define US_GET_RX_STREAM_PARAM32 _IOWR(USF_IOCTL_MAGIC, 13, \ + struct us_stream_param_type32) + +/* Info structure common for TX and RX */ +struct us_xx_info_type32 { +/* Input: general info */ +/* Name of the client - event calculator, ptr to char */ + const compat_uptr_t client_name; +/* Selected device identification, accepted in the kernel's CAD */ + uint32_t dev_id; +/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */ + uint32_t stream_format; +/* Required sample rate in Hz */ + uint32_t sample_rate; +/* Size of a buffer (bytes) for US data transfer between the module and USF */ + uint32_t buf_size; +/* Number of the buffers for the US data transfer */ + uint16_t buf_num; +/* Number of the microphones (TX) or speakers(RX) */ + uint16_t port_cnt; +/* Microphones(TX) or speakers(RX) indexes in their enumeration */ + uint8_t port_id[USF_MAX_PORT_NUM]; +/* Bits per sample 16 or 32 */ + uint16_t bits_per_sample; +/* Input: Transparent info for encoder in the LPASS */ +/* Parameters data size in bytes */ + uint16_t params_data_size; +/* Pointer to the parameters, ptr to uint8_t */ + compat_uptr_t params_data; +/* Max size of buffer for get and set parameter */ + uint32_t max_get_set_param_buf_size; +}; + +struct us_tx_info_type32 { +/* Common info. This struct includes ptr and therefore the 32 version */ + struct us_xx_info_type32 us_xx_info; +/* Info specific for TX. This struct doesn't include long or ptr + * and therefore no 32 version + */ + struct us_input_info_type input_info; +}; + +struct us_tx_update_info_type32 { +/* Input general: */ +/* Number of calculated events */ + uint16_t event_counter; +/* Calculated events or NULL, ptr to struct usf_event_type */ + compat_uptr_t event; +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +/* Time (sec) to wait for data or special values: */ +/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */ + uint32_t timeout; +/* Events (from conflicting devs) to be disabled/enabled */ + uint16_t event_filters; + +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* Pointer to the parameters, ptr to uint8_t */ + compat_uptr_t params_data; +/* Output parameters: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +}; + +struct us_rx_info_type32 { + /* Common info */ + struct us_xx_info_type32 us_xx_info; + /* Info specific for RX*/ +}; + +struct us_rx_update_info_type32 { +/* Input general: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* pPointer to the parameters, ptr to uint8_t */ + compat_uptr_t params_data; +/* Output parameters: */ +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +}; + +struct us_detect_info_type32 { +/* US detection place (HW|FW) */ +/* NA in the Active and OFF states */ + enum us_detect_place_enum us_detector; +/* US detection mode */ + enum us_detect_mode_enum us_detect_mode; +/* US data dropped during this time (msec) */ + uint32_t skip_time; +/* Transparent data size */ + uint16_t params_data_size; +/* Pointer to the transparent data, ptr to uint8_t */ + compat_uptr_t params_data; +/* Time (sec) to wait for US presence event */ + uint32_t detect_timeout; +/* Out parameter: US presence */ + bool is_us; +}; + +struct us_version_info_type32 { +/* Size of memory for the version string */ + uint16_t buf_size; +/* Pointer to the memory for the version string, ptr to char */ + compat_uptr_t pbuf; +}; + +struct us_stream_param_type32 { +/* Id of module */ + uint32_t module_id; +/* Id of parameter */ + uint32_t param_id; +/* Size of memory of the parameter buffer */ + uint32_t buf_size; +/* Pointer to the memory of the parameter buffer */ + compat_uptr_t pbuf; +}; + +static void usf_compat_xx_info_type(struct us_xx_info_type32 *us_xx_info32, + struct us_xx_info_type *us_xx_info) +{ + int i = 0; + + us_xx_info->client_name = compat_ptr(us_xx_info32->client_name); + us_xx_info->dev_id = us_xx_info32->dev_id; + us_xx_info->stream_format = us_xx_info32->stream_format; + us_xx_info->sample_rate = us_xx_info32->sample_rate; + us_xx_info->buf_size = us_xx_info32->buf_size; + us_xx_info->buf_num = us_xx_info32->buf_num; + us_xx_info->port_cnt = us_xx_info32->port_cnt; + for (i = 0; i < USF_MAX_PORT_NUM; i++) + us_xx_info->port_id[i] = us_xx_info32->port_id[i]; + us_xx_info->bits_per_sample = us_xx_info32->bits_per_sample; + us_xx_info->params_data_size = us_xx_info32->params_data_size; + us_xx_info->params_data = compat_ptr(us_xx_info32->params_data); + us_xx_info->max_get_set_param_buf_size = + us_xx_info32->max_get_set_param_buf_size; +} + +static int usf_set_tx_info32(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_info_type32 config_tx32; + struct us_tx_info_type config_tx; + + int rc = copy_from_user(&config_tx32, + (struct us_tx_info_type32 __user *) arg, + sizeof(config_tx32)); + + if (rc) { + pr_err("%s: copy config_tx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + memset(&config_tx, 0, sizeof(config_tx)); + usf_compat_xx_info_type(&(config_tx32.us_xx_info), + &(config_tx.us_xx_info)); + config_tx.input_info = config_tx32.input_info; + + return __usf_set_tx_info(usf, &config_tx); +} /* usf_set_tx_info 32*/ + +static int usf_set_rx_info32(struct usf_type *usf, unsigned long arg) +{ + struct us_rx_info_type32 config_rx32; + struct us_rx_info_type config_rx; + + int rc = copy_from_user(&config_rx32, + (struct us_rx_info_type32 __user *) arg, + sizeof(config_rx32)); + + if (rc) { + pr_err("%s: copy config_rx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + memset(&config_rx, 0, sizeof(config_rx)); + usf_compat_xx_info_type(&(config_rx32.us_xx_info), + &(config_rx.us_xx_info)); + + return __usf_set_rx_info(usf, &config_rx); +} /* usf_set_rx_info32 */ + +static int usf_get_tx_update32(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_update_info_type32 upd_tx_info32; + struct us_tx_update_info_type upd_tx_info; + + int rc = copy_from_user(&upd_tx_info32, + (struct us_tx_update_info_type32 __user *) arg, + sizeof(upd_tx_info32)); + + if (rc) { + pr_err("%s: copy upd_tx_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&upd_tx_info, 0, sizeof(upd_tx_info)); + upd_tx_info.event_counter = upd_tx_info32.event_counter; + upd_tx_info.event = compat_ptr(upd_tx_info32.event); + upd_tx_info.free_region = upd_tx_info32.free_region; + upd_tx_info.timeout = upd_tx_info32.timeout; + upd_tx_info.event_filters = upd_tx_info32.event_filters; + upd_tx_info.params_data_size = upd_tx_info32.params_data_size; + upd_tx_info.params_data = compat_ptr(upd_tx_info32.params_data); + upd_tx_info.ready_region = upd_tx_info32.ready_region; + + rc = __usf_get_tx_update(usf, &upd_tx_info); + if (rc < 0) { + pr_err("%s: get tx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* Update only the fields that were changed */ + upd_tx_info32.ready_region = upd_tx_info.ready_region; + + rc = copy_to_user((void __user *)arg, &upd_tx_info32, + sizeof(upd_tx_info32)); + if (rc) { + pr_err("%s: copy upd_tx_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_tx_update */ + +static int usf_set_rx_update32(struct usf_xx_type *usf_xx, unsigned long arg) +{ + struct us_rx_update_info_type32 upd_rx_info32; + struct us_rx_update_info_type upd_rx_info; + + int rc = copy_from_user(&upd_rx_info32, + (struct us_rx_update_info_type32 __user *) arg, + sizeof(upd_rx_info32)); + + if (rc) { + pr_err("%s: copy upd_rx_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&upd_rx_info, 0, sizeof(upd_rx_info)); + upd_rx_info.ready_region = upd_rx_info32.ready_region; + upd_rx_info.params_data_size = upd_rx_info32.params_data_size; + upd_rx_info.params_data = compat_ptr(upd_rx_info32.params_data); + upd_rx_info.free_region = upd_rx_info32.free_region; + + rc = __usf_set_rx_update(usf_xx, &upd_rx_info); + if (rc < 0) { + pr_err("%s: set rx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* Update only the fields that were changed */ + upd_rx_info32.free_region = upd_rx_info.free_region; + + rc = copy_to_user((void __user *)arg, + &upd_rx_info32, + sizeof(upd_rx_info32)); + if (rc) { + pr_err("%s: copy rx_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_rx_update32 */ + +static int usf_set_us_detection32(struct usf_type *usf, unsigned long arg) +{ + struct us_detect_info_type32 detect_info32; + struct us_detect_info_type detect_info; + + int rc = copy_from_user(&detect_info32, + (struct us_detect_info_type32 __user *) arg, + sizeof(detect_info32)); + + if (rc) { + pr_err("%s: copy detect_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (detect_info32.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + memset(&detect_info, 0, sizeof(detect_info)); + detect_info.us_detector = detect_info32.us_detector; + detect_info.us_detect_mode = detect_info32.us_detect_mode; + detect_info.skip_time = detect_info32.skip_time; + detect_info.params_data_size = detect_info32.params_data_size; + detect_info.params_data = compat_ptr(detect_info32.params_data); + detect_info.detect_timeout = detect_info32.detect_timeout; + detect_info.is_us = detect_info32.is_us; + + rc = __usf_set_us_detection(usf, &detect_info); + if (rc < 0) { + pr_err("%s: set us detection failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* Update only the fields that were changed */ + detect_info32.is_us = detect_info.is_us; + + rc = copy_to_user((void __user *)arg, + &detect_info32, + sizeof(detect_info32)); + if (rc) { + pr_err("%s: copy detect_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_us_detection32 */ + +static int usf_get_version32(unsigned long arg) +{ + struct us_version_info_type32 version_info32; + struct us_version_info_type version_info; + + int rc = copy_from_user(&version_info32, + (struct us_version_info_type32 __user *) arg, + sizeof(version_info32)); + + if (rc) { + pr_err("%s: copy version_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&version_info, 0, sizeof(version_info)); + version_info.buf_size = version_info32.buf_size; + version_info.pbuf = compat_ptr(version_info32.pbuf); + + rc = __usf_get_version(&version_info); + if (rc < 0) { + pr_err("%s: get version failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* None of the fields were changed */ + + rc = copy_to_user((void __user *)arg, + &version_info32, + sizeof(version_info32)); + if (rc) { + pr_err("%s: copy version_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_version32 */ + +static int usf_set_stream_param32(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type32 set_stream_param32; + struct us_stream_param_type set_stream_param; + int rc = 0; + + rc = copy_from_user(&set_stream_param32, + (struct us_stream_param_type32 __user *) arg, + sizeof(set_stream_param32)); + + if (rc) { + pr_err("%s: copy set_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&set_stream_param, 0, sizeof(set_stream_param)); + set_stream_param.module_id = set_stream_param32.module_id; + set_stream_param.param_id = set_stream_param32.param_id; + set_stream_param.buf_size = set_stream_param32.buf_size; + set_stream_param.pbuf = compat_ptr(set_stream_param32.pbuf); + + return __usf_set_stream_param(usf_xx, &set_stream_param, dir); +} /* usf_set_stream_param32 */ + +static int usf_get_stream_param32(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type32 get_stream_param32; + struct us_stream_param_type get_stream_param; + int rc = 0; + + rc = copy_from_user(&get_stream_param32, + (struct us_stream_param_type32 __user *) arg, + sizeof(get_stream_param32)); + + if (rc) { + pr_err("%s: copy get_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&get_stream_param, 0, sizeof(get_stream_param)); + get_stream_param.module_id = get_stream_param32.module_id; + get_stream_param.param_id = get_stream_param32.param_id; + get_stream_param.buf_size = get_stream_param32.buf_size; + get_stream_param.pbuf = compat_ptr(get_stream_param32.pbuf); + + return __usf_get_stream_param(usf_xx, &get_stream_param, dir); +} /* usf_get_stream_param32 */ + +static long __usf_compat_ioctl(struct usf_type *usf, + unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + struct usf_xx_type *usf_xx = NULL; + + switch (cmd) { + case US_START_TX: + case US_START_RX: + case US_STOP_TX: + case US_STOP_RX: { + return __usf_ioctl(usf, cmd, arg); + } + + case US_SET_TX_INFO32: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_tx_info32(usf, arg); + else { + pr_err("%s: set_tx_info32: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_TX_INFO32 */ + + case US_SET_RX_INFO32: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_rx_info32(usf, arg); + else { + pr_err("%s: set_rx_info32: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_RX_INFO32 */ + + case US_GET_TX_UPDATE32: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_get_tx_update32(usf, arg); + else { + pr_err("%s: get_tx_update32: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_GET_TX_UPDATE32 */ + + case US_SET_RX_UPDATE32: { + struct usf_xx_type *usf_xx = &usf->usf_rx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_rx_update32(usf_xx, arg); + else { + pr_err("%s: set_rx_update: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_RX_UPDATE32 */ + + case US_SET_DETECTION32: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_us_detection32(usf, arg); + else { + pr_err("%s: set us detection: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_DETECTION32 */ + + case US_GET_VERSION32: { + rc = usf_get_version32(arg); + break; + } /* US_GET_VERSION32 */ + + case US_SET_TX_STREAM_PARAM32: { + rc = usf_set_stream_param32(&usf->usf_tx, arg, OUT); + break; + } /* US_SET_TX_STREAM_PARAM32 */ + + case US_GET_TX_STREAM_PARAM32: { + rc = usf_get_stream_param32(&usf->usf_tx, arg, OUT); + break; + } /* US_GET_TX_STREAM_PARAM32 */ + + case US_SET_RX_STREAM_PARAM32: { + rc = usf_set_stream_param32(&usf->usf_rx, arg, IN); + break; + } /* US_SET_RX_STREAM_PARAM32 */ + + case US_GET_RX_STREAM_PARAM32: { + rc = usf_get_stream_param32(&usf->usf_rx, arg, IN); + break; + } /* US_GET_RX_STREAM_PARAM32 */ + + default: + pr_err("%s: unsupported IOCTL command [%d]\n", + __func__, + cmd); + rc = -ENOTTY; + break; + } + + if (rc && + ((cmd == US_SET_TX_INFO) || + (cmd == US_SET_RX_INFO))) + release_xx(usf_xx); + + return rc; +} /* __usf_compat_ioctl */ + +static long usf_compat_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct usf_type *usf = file->private_data; + int rc = 0; + + mutex_lock(&usf->mutex); + rc = __usf_compat_ioctl(usf, cmd, arg); + mutex_unlock(&usf->mutex); + + return rc; +} /* usf_compat_ioctl */ +#endif /* CONFIG_COMPAT */ + +static int usf_mmap(struct file *file, struct vm_area_struct *vms) +{ + struct usf_type *usf = file->private_data; + int dir = OUT; + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + mutex_lock(&usf->mutex); + if (vms->vm_flags & USF_VM_WRITE) { /* RX buf mapping */ + dir = IN; + usf_xx = &usf->usf_rx; + } + rc = q6usm_get_virtual_address(dir, usf_xx->usc, vms); + mutex_unlock(&usf->mutex); + + return rc; +} + +static uint16_t add_opened_dev(int minor) +{ + uint16_t ind = 0; + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { + if (minor == s_opened_devs[ind]) { + pr_err("%s: device %d is already opened\n", + __func__, minor); + return USF_UNDEF_DEV_ID; + } + + if (s_opened_devs[ind] == 0) { + s_opened_devs[ind] = minor; + pr_debug("%s: device %d is added; ind=%d\n", + __func__, minor, ind); + return ind; + } + } + + pr_err("%s: there is no place for device %d\n", + __func__, minor); + return USF_UNDEF_DEV_ID; +} + +static int usf_open(struct inode *inode, struct file *file) +{ + struct usf_type *usf = NULL; + uint16_t dev_ind = 0; + int minor = MINOR(inode->i_rdev); + + dev_ind = add_opened_dev(minor); + if (dev_ind == USF_UNDEF_DEV_ID) + return -EBUSY; + + usf = kzalloc(sizeof(struct usf_type), GFP_KERNEL); + if (usf == NULL) + return -ENOMEM; + + wakeup_source_init(&usf_wakeup_source, "usf"); + + file->private_data = usf; + usf->dev_ind = dev_ind; + + usf->usf_tx.usf_state = USF_OPENED_STATE; + usf->usf_rx.usf_state = USF_OPENED_STATE; + + usf->usf_tx.us_detect_type = USF_US_DETECT_UNDEF; + usf->usf_rx.us_detect_type = USF_US_DETECT_UNDEF; + + mutex_init(&usf->mutex); + + pr_debug("%s:usf in open\n", __func__); + return 0; +} + +static int usf_release(struct inode *inode, struct file *file) +{ + struct usf_type *usf = file->private_data; + + pr_debug("%s: release entry\n", __func__); + + mutex_lock(&usf->mutex); + usf_release_input(usf); + + usf_disable(&usf->usf_tx); + usf_disable(&usf->usf_rx); + + s_opened_devs[usf->dev_ind] = 0; + + wakeup_source_trash(&usf_wakeup_source); + mutex_unlock(&usf->mutex); + mutex_destroy(&usf->mutex); + kfree(usf); + pr_debug("%s: release exit\n", __func__); + return 0; +} + +extern long usf_compat_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg); + +static const struct file_operations usf_fops = { + .owner = THIS_MODULE, + .open = usf_open, + .release = usf_release, + .unlocked_ioctl = usf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = usf_compat_ioctl, +#endif /* CONFIG_COMPAT */ + .mmap = usf_mmap, +}; + +static struct miscdevice usf_misc[MAX_DEVS_NUMBER] = { + { + .minor = MISC_DYNAMIC_MINOR, + .name = "usf1", + .fops = &usf_fops, + }, +}; + +static int __init usf_init(void) +{ + int rc = 0; + uint16_t ind = 0; + + pr_debug("%s: USF SW version %s.\n", __func__, DRV_VERSION); + pr_debug("%s: Max %d devs registration\n", __func__, MAX_DEVS_NUMBER); + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { + rc = misc_register(&usf_misc[ind]); + if (rc) { + pr_err("%s: misc_register() failed ind=%d; rc = %d\n", + __func__, ind, rc); + break; + } + } + + return rc; +} + +device_initcall(usf_init); + +MODULE_DESCRIPTION("Ultrasound framework driver"); diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c new file mode 100644 index 000000000000..6b8aae01ec53 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c @@ -0,0 +1,422 @@ +/* Copyright (c) 2012-2013, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "usfcdev.h" + +#define UNDEF_ID 0xffffffff +#define SLOT_CMD_ID 0 +#define MAX_RETRIES 10 + +enum usdev_event_status { + USFCDEV_EVENT_ENABLED, + USFCDEV_EVENT_DISABLING, + USFCDEV_EVENT_DISABLED, +}; + +struct usfcdev_event { + bool (*match_cb)(uint16_t, struct input_dev *dev); + bool registered_event; + bool interleaved; + enum usdev_event_status event_status; +}; +static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM]; + +struct usfcdev_input_command { + unsigned int type; + unsigned int code; + unsigned int value; +}; + +static long s_usf_pid; + +static bool usfcdev_filter(struct input_handle *handle, + unsigned int type, unsigned int code, int value); +static bool usfcdev_match(struct input_handler *handler, + struct input_dev *dev); +static int usfcdev_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id); +static void usfcdev_disconnect(struct input_handle *handle); + +static const struct input_device_id usfc_tsc_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + /* assumption: ABS_X & ABS_Y are in the same long */ + .absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) | + BIT_MASK(ABS_Y) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + /* assumption: MT_.._X & MT_.._Y are in the same long */ + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, usfc_tsc_ids); + +static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = { + { /* TSC handler */ + .filter = usfcdev_filter, + .match = usfcdev_match, + .connect = usfcdev_connect, + .disconnect = usfcdev_disconnect, + /* .minor can be used as index in the container, */ + /* because .fops isn't supported */ + .minor = TSC_EVENT_TYPE_IND, + .name = "usfc_tsc_handler", + .id_table = usfc_tsc_ids, + }, +}; + +/* + * For each event type, there are a number conflicting devices (handles) + * The first registered device (primary) is real TSC device; it's mandatory + * Optionally, later registered devices are simulated ones. + * They are dynamically managed + * The primary device's handles are stored in the below static array + */ +static struct input_handle s_usfc_primary_handles[MAX_EVENT_TYPE_NUM] = { + { /* TSC handle */ + .handler = &s_usfc_handlers[TSC_EVENT_TYPE_IND], + .name = "usfc_tsc_handle", + }, +}; + +static struct usfcdev_input_command initial_clear_cmds[] = { + {EV_ABS, ABS_PRESSURE, 0}, + {EV_KEY, BTN_TOUCH, 0}, +}; + +static struct usfcdev_input_command slot_clear_cmds[] = { + {EV_ABS, ABS_MT_SLOT, 0}, + {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID}, +}; + +static struct usfcdev_input_command no_filter_cmds[] = { + {EV_ABS, ABS_MT_SLOT, 0}, + {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID}, + {EV_SYN, SYN_REPORT, 0}, +}; + +static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev) +{ + bool rc = false; + int ind = handler->minor; + + pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind); + + if (s_usfcdev_events[ind].registered_event && + s_usfcdev_events[ind].match_cb) { + rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev); + pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc); + } + return rc; +} + +static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + int ret = 0; + uint16_t ind = handler->minor; + struct input_handle *usfc_handle = NULL; + + if (s_usfc_primary_handles[ind].dev == NULL) { + pr_debug("%s: primary device; ind=%d\n", + __func__, + ind); + usfc_handle = &s_usfc_primary_handles[ind]; + } else { + pr_debug("%s: secondary device; ind=%d\n", + __func__, + ind); + usfc_handle = kzalloc(sizeof(struct input_handle), + GFP_KERNEL); + if (!usfc_handle) + return -ENOMEM; + + usfc_handle->handler = &s_usfc_handlers[ind]; + usfc_handle->name = s_usfc_primary_handles[ind].name; + } + usfc_handle->dev = dev; + ret = input_register_handle(usfc_handle); + pr_debug("%s: name=[%s]; ind=%d; dev=0x%pK\n", + __func__, + dev->name, + ind, + usfc_handle->dev); + if (ret) + pr_err("%s: input_register_handle[%d] failed: ret=%d\n", + __func__, + ind, + ret); + else { + ret = input_open_device(usfc_handle); + if (ret) { + pr_err("%s: input_open_device[%d] failed: ret=%d\n", + __func__, + ind, + ret); + input_unregister_handle(usfc_handle); + } else + pr_debug("%s: device[%d] is opened\n", + __func__, + ind); + } + + return ret; +} + +static void usfcdev_disconnect(struct input_handle *handle) +{ + int ind = handle->handler->minor; + + input_close_device(handle); + input_unregister_handle(handle); + pr_debug("%s: handle[%d], name=[%s] is disconnected\n", + __func__, + ind, + handle->dev->name); + if (s_usfc_primary_handles[ind].dev == handle->dev) + s_usfc_primary_handles[ind].dev = NULL; + else + kfree(handle); +} + +static bool usfcdev_filter(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + uint16_t i = 0; + uint16_t ind = (uint16_t)handle->handler->minor; + bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED); + + if (s_usf_pid == sys_getpid()) { + /* Pass events from usfcdev driver */ + rc = false; + pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d", + __func__, + ind, + type, + code, + value); + } else if (s_usfcdev_events[ind].event_status == + USFCDEV_EVENT_DISABLING) { + uint32_t u_value = value; + + s_usfcdev_events[ind].interleaved = true; + /* Pass events for freeing slots from TSC driver */ + for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) { + if ((no_filter_cmds[i].type == type) && + (no_filter_cmds[i].code == code) && + (no_filter_cmds[i].value <= u_value)) { + rc = false; + pr_debug("%s: no_filter_cmds[%d]; %d", + __func__, + i, + no_filter_cmds[i].value); + break; + } + } + } + + return rc; +} + +bool usfcdev_register( + uint16_t event_type_ind, + bool (*match_cb)(uint16_t, struct input_dev *dev)) +{ + int ret = 0; + bool rc = false; + + if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) { + pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%pK\n", + __func__, + event_type_ind, + match_cb); + return false; + } + + if (s_usfcdev_events[event_type_ind].registered_event) { + pr_info("%s: handler[%d] was already registered\n", + __func__, + event_type_ind); + return true; + } + + s_usfcdev_events[event_type_ind].registered_event = true; + s_usfcdev_events[event_type_ind].match_cb = match_cb; + s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED; + ret = input_register_handler(&s_usfc_handlers[event_type_ind]); + if (!ret) { + rc = true; + pr_debug("%s: handler[%d] was registered\n", + __func__, + event_type_ind); + } else { + s_usfcdev_events[event_type_ind].registered_event = false; + s_usfcdev_events[event_type_ind].match_cb = NULL; + pr_err("%s: handler[%d] registration failed: ret=%d\n", + __func__, + event_type_ind, + ret); + } + + return rc; +} + +void usfcdev_unregister(uint16_t event_type_ind) +{ + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return; + } + if (s_usfcdev_events[event_type_ind].registered_event) { + input_unregister_handler(&s_usfc_handlers[event_type_ind]); + pr_debug("%s: handler[%d] was unregistered\n", + __func__, + event_type_ind); + s_usfcdev_events[event_type_ind].registered_event = false; + s_usfcdev_events[event_type_ind].match_cb = NULL; + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_ENABLED; + + } +} + +static inline void usfcdev_send_cmd( + struct input_dev *dev, + struct usfcdev_input_command cmd) +{ + input_event(dev, cmd.type, cmd.code, cmd.value); +} + +static void usfcdev_clean_dev(uint16_t event_type_ind) +{ + struct input_dev *dev = NULL; + int i; + int j; + int retries = 0; + + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return; + } + /* Only primary device must exist */ + dev = s_usfc_primary_handles[event_type_ind].dev; + if (dev == NULL) { + pr_err("%s: NULL primary device\n", + __func__); + return; + } + + for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++) + usfcdev_send_cmd(dev, initial_clear_cmds[i]); + input_sync(dev); + + /* Send commands to free all slots */ + for (i = 0; i < dev->mt->num_slots; i++) { + s_usfcdev_events[event_type_ind].interleaved = false; + if (input_mt_get_value(&dev->mt->slots[i], + ABS_MT_TRACKING_ID) < 0) { + pr_debug("%s: skipping slot %d", + __func__, i); + continue; + } + slot_clear_cmds[SLOT_CMD_ID].value = i; + for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++) + usfcdev_send_cmd(dev, slot_clear_cmds[j]); + + if (s_usfcdev_events[event_type_ind].interleaved) { + pr_debug("%s: interleaved(%d): slot(%d)", + __func__, i, dev->mt->slot); + if (retries++ < MAX_RETRIES) { + --i; + continue; + } + pr_warn("%s: index(%d) reached max retires", + __func__, i); + } + + retries = 0; + input_sync(dev); + } +} + +bool usfcdev_set_filter(uint16_t event_type_ind, bool filter) +{ + bool rc = true; + + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return false; + } + + if (s_usfcdev_events[event_type_ind].registered_event) { + + pr_debug("%s: event_type[%d]; filter=%d\n", + __func__, + event_type_ind, + filter + ); + if (filter) { + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_DISABLING; + s_usf_pid = sys_getpid(); + usfcdev_clean_dev(event_type_ind); + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_DISABLED; + } else + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_ENABLED; + } else { + pr_err("%s: event_type[%d] isn't registered\n", + __func__, + event_type_ind); + rc = false; + } + + return rc; +} + +static int __init usfcdev_init(void) +{ + return 0; +} + +device_initcall(usfcdev_init); + +MODULE_DESCRIPTION("Handle of events from devices, conflicting with USF"); diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h new file mode 100644 index 000000000000..03b62c5ec83c --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __USFCDEV_H__ +#define __USFCDEV_H__ + +#include + +/* TSC event type index in the containers of the handlers & handles */ +#define TSC_EVENT_TYPE_IND 0 +/* Number of supported event types to be filtered */ +#define MAX_EVENT_TYPE_NUM 1 + +bool usfcdev_register( + uint16_t event_type_ind, + bool (*match_cb)(uint16_t, struct input_dev *dev)); +void usfcdev_unregister(uint16_t event_type_ind); +bool usfcdev_set_filter(uint16_t event_type_ind, bool filter); +#endif /* __USFCDEV_H__ */ diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 85342d1736a6..7ea7114bad45 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o obj-$(CONFIG_MSM_IPC_ROUTER_GLINK_XPRT) += ipc_router_glink_xprt.o obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o obj-$(CONFIG_MSM_GLINK_PKT) += msm_glink_pkt.o +obj-y += qdsp6v2/ obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR) += system_health_monitor_v01.o obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR) += system_health_monitor.o obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile new file mode 100644 index 000000000000..f3505bab1a34 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o voice_svc.o +obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o voice_svc.o +obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o voice_svc.o +obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o voice_svc.o +obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += msm_audio_ion.o +obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o +obj-$(CONFIG_MSM_QDSP6_SSR) += audio_ssr.o +obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o +obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c new file mode 100644 index 000000000000..1bde1bfab12f --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define Q6_PIL_GET_DELAY_MS 100 +#define BOOT_CMD 1 +#define IMAGE_UNLOAD_CMD 0 + +static ssize_t adsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +struct adsp_loader_private { + void *pil_h; + struct kobject *boot_adsp_obj; + struct attribute_group *attr_group; +}; + +static struct kobj_attribute adsp_boot_attribute = + __ATTR(boot, 0220, NULL, adsp_boot_store); + +static struct attribute *attrs[] = { + &adsp_boot_attribute.attr, + NULL, +}; + +static struct platform_device *adsp_private; +static void adsp_loader_unload(struct platform_device *pdev); + +static void adsp_loader_do(struct platform_device *pdev) +{ + + struct adsp_loader_private *priv = NULL; + + const char *adsp_dt = "qcom,adsp-state"; + int rc = 0; + u32 adsp_state; + const char *img_name; + + if (!pdev) { + dev_err(&pdev->dev, "%s: Platform device null\n", __func__); + goto fail; + } + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, + "%s: Device tree information missing\n", __func__); + goto fail; + } + + rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state); + if (rc) { + dev_err(&pdev->dev, + "%s: ADSP state = %x\n", __func__, adsp_state); + goto fail; + } + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,proc-img-to-load", + &img_name); + + if (rc) { + dev_dbg(&pdev->dev, + "%s: loading default image ADSP\n", __func__); + goto load_adsp; + } + if (!strcmp(img_name, "modem")) { + /* adsp_state always returns "0". So load modem image based on + * apr_modem_state to prevent loading of image twice + */ + adsp_state = apr_get_modem_state(); + if (adsp_state == APR_SUBSYS_DOWN) { + priv = platform_get_drvdata(pdev); + if (!priv) { + dev_err(&pdev->dev, + " %s: Private data get failed\n", __func__); + goto fail; + } + + priv->pil_h = subsystem_get("modem"); + if (IS_ERR(priv->pil_h)) { + dev_err(&pdev->dev, "%s: pil get failed,\n", + __func__); + goto fail; + } + + /* Set the state of the ADSP in APR driver */ + apr_set_modem_state(APR_SUBSYS_LOADED); + } else if (adsp_state == APR_SUBSYS_LOADED) { + dev_dbg(&pdev->dev, + "%s: MDSP state = %x\n", __func__, adsp_state); + } + + dev_dbg(&pdev->dev, "%s: Q6/MDSP image is loaded\n", __func__); + return; + } +load_adsp: + { + adsp_state = apr_get_q6_state(); + if (adsp_state == APR_SUBSYS_DOWN) { + priv = platform_get_drvdata(pdev); + if (!priv) { + dev_err(&pdev->dev, + " %s: Private data get failed\n", __func__); + goto fail; + } + + priv->pil_h = subsystem_get("adsp"); + if (IS_ERR(priv->pil_h)) { + dev_err(&pdev->dev, "%s: pil get failed,\n", + __func__); + goto fail; + } + + /* Set the state of the ADSP in APR driver */ + apr_set_q6_state(APR_SUBSYS_LOADED); + } else if (adsp_state == APR_SUBSYS_LOADED) { + dev_dbg(&pdev->dev, + "%s: ADSP state = %x\n", __func__, adsp_state); + } + + dev_dbg(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__); + return; + } +fail: + dev_err(&pdev->dev, "%s: Q6 image loading failed\n", __func__); +} + + +static ssize_t adsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + int boot = 0; + + if (sscanf(buf, "%du", &boot) != 1) { + pr_err("%s: failed to read boot info from string\n", __func__); + return -EINVAL; + } + + if (boot == BOOT_CMD) { + pr_debug("%s: going to call adsp_loader_do\n", __func__); + adsp_loader_do(adsp_private); + } else if (boot == IMAGE_UNLOAD_CMD) { + pr_debug("%s: going to call adsp_unloader\n", __func__); + adsp_loader_unload(adsp_private); + } + return count; +} + +static void adsp_loader_unload(struct platform_device *pdev) +{ + struct adsp_loader_private *priv = NULL; + + priv = platform_get_drvdata(pdev); + + if (!priv) + return; + + if (priv->pil_h) { + dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__); + subsystem_put(priv->pil_h); + priv->pil_h = NULL; + } +} + +static int adsp_loader_init_sysfs(struct platform_device *pdev) +{ + int ret = -EINVAL; + struct adsp_loader_private *priv = NULL; + + adsp_private = NULL; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + return ret; + } + + platform_set_drvdata(pdev, priv); + + priv->pil_h = NULL; + priv->boot_adsp_obj = NULL; + priv->attr_group = devm_kzalloc(&pdev->dev, + sizeof(*(priv->attr_group)), + GFP_KERNEL); + if (!priv->attr_group) { + ret = -ENOMEM; + goto error_return; + } + + priv->attr_group->attrs = attrs; + + priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj); + if (!priv->boot_adsp_obj) { + dev_err(&pdev->dev, "%s: sysfs create and add failed\n", + __func__); + ret = -ENOMEM; + goto error_return; + } + + ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group); + if (ret) { + dev_err(&pdev->dev, "%s: sysfs create group failed %d\n", + __func__, ret); + goto error_return; + } + + adsp_private = pdev; + + return 0; + +error_return: + + if (priv->boot_adsp_obj) { + kobject_del(priv->boot_adsp_obj); + priv->boot_adsp_obj = NULL; + } + + return ret; +} + +static int adsp_loader_remove(struct platform_device *pdev) +{ + struct adsp_loader_private *priv = NULL; + + priv = platform_get_drvdata(pdev); + + if (!priv) + return 0; + + if (priv->pil_h) { + subsystem_put(priv->pil_h); + priv->pil_h = NULL; + } + + if (priv->boot_adsp_obj) { + sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group); + kobject_del(priv->boot_adsp_obj); + priv->boot_adsp_obj = NULL; + } + + return 0; +} + +static int adsp_loader_probe(struct platform_device *pdev) +{ + int ret = adsp_loader_init_sysfs(pdev); + + if (ret != 0) { + dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__); + return ret; + } + + return 0; +} + +static const struct of_device_id adsp_loader_dt_match[] = { + { .compatible = "qcom,adsp-loader" }, + { } +}; +MODULE_DEVICE_TABLE(of, adsp_loader_dt_match); + +static struct platform_driver adsp_loader_driver = { + .driver = { + .name = "adsp-loader", + .owner = THIS_MODULE, + .of_match_table = adsp_loader_dt_match, + }, + .probe = adsp_loader_probe, + .remove = adsp_loader_remove, +}; + +static int __init adsp_loader_init(void) +{ + return platform_driver_register(&adsp_loader_driver); +} +module_init(adsp_loader_init); + +static void __exit adsp_loader_exit(void) +{ + platform_driver_unregister(&adsp_loader_driver); +} +module_exit(adsp_loader_exit); + +MODULE_DESCRIPTION("ADSP Loader module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c new file mode 100644 index 000000000000..5f3c9d47d8b8 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -0,0 +1,970 @@ +/* Copyright (c) 2010-2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APR_PKT_IPC_LOG_PAGE_CNT 2 + +static struct apr_q6 q6; +static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX]; +static void *apr_pkt_ctx; +static wait_queue_head_t dsp_wait; +static wait_queue_head_t modem_wait; +static bool is_modem_up; +static bool is_initial_boot; +/* Subsystem restart: QDSP6 data, functions */ +static struct workqueue_struct *apr_reset_workqueue; +static void apr_reset_deregister(struct work_struct *work); +static void dispatch_event(unsigned long code, uint16_t proc); +struct apr_reset_work { + void *handle; + struct work_struct work; +}; + +static bool apr_cf_debug; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_apr_debug; +static ssize_t apr_debug_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char cmd; + + if (copy_from_user(&cmd, ubuf, 1)) + return -EFAULT; + + apr_cf_debug = (cmd == '1') ? true : false; + + return cnt; +} + +static const struct file_operations apr_debug_ops = { + .write = apr_debug_write, +}; +#endif + +#define APR_PKT_INFO(x...) \ +do { \ + if (apr_pkt_ctx) \ + ipc_log_string(apr_pkt_ctx, ": "x); \ +} while (0) + + +struct apr_svc_table { + char name[64]; + int idx; + int id; + int client_id; +}; + +static const struct apr_svc_table svc_tbl_qdsp6[] = { + { + .name = "AFE", + .idx = 0, + .id = APR_SVC_AFE, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "ASM", + .idx = 1, + .id = APR_SVC_ASM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "ADM", + .idx = 2, + .id = APR_SVC_ADM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CORE", + .idx = 3, + .id = APR_SVC_ADSP_CORE, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "TEST", + .idx = 4, + .id = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "MVM", + .idx = 5, + .id = APR_SVC_ADSP_MVM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CVS", + .idx = 6, + .id = APR_SVC_ADSP_CVS, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CVP", + .idx = 7, + .id = APR_SVC_ADSP_CVP, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "USM", + .idx = 8, + .id = APR_SVC_USM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "VIDC", + .idx = 9, + .id = APR_SVC_VIDC, + }, + { + .name = "LSM", + .idx = 10, + .id = APR_SVC_LSM, + .client_id = APR_CLIENT_AUDIO, + }, +}; + +static struct apr_svc_table svc_tbl_voice[] = { + { + .name = "VSM", + .idx = 0, + .id = APR_SVC_VSM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "VPM", + .idx = 1, + .id = APR_SVC_VPM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "MVS", + .idx = 2, + .id = APR_SVC_MVS, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "MVM", + .idx = 3, + .id = APR_SVC_MVM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "CVS", + .idx = 4, + .id = APR_SVC_CVS, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "CVP", + .idx = 5, + .id = APR_SVC_CVP, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "SRD", + .idx = 6, + .id = APR_SVC_SRD, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "TEST", + .idx = 7, + .id = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_VOICE, + }, +}; + +enum apr_subsys_state apr_get_modem_state(void) +{ + return atomic_read(&q6.modem_state); +} + +void apr_set_modem_state(enum apr_subsys_state state) +{ + atomic_set(&q6.modem_state, state); +} + +enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.modem_state, prev, new); +} + +static void apr_modem_down(unsigned long opcode) +{ + apr_set_modem_state(APR_SUBSYS_DOWN); + dispatch_event(opcode, APR_DEST_MODEM); +} + +static void apr_modem_up(void) +{ + if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) == + APR_SUBSYS_DOWN) + wake_up(&modem_wait); + is_modem_up = 1; +} + +enum apr_subsys_state apr_get_q6_state(void) +{ + return atomic_read(&q6.q6_state); +} +EXPORT_SYMBOL(apr_get_q6_state); + +int apr_set_q6_state(enum apr_subsys_state state) +{ + pr_debug("%s: setting adsp state %d\n", __func__, state); + if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED) + return -EINVAL; + atomic_set(&q6.q6_state, state); + return 0; +} +EXPORT_SYMBOL(apr_set_q6_state); + +enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.q6_state, prev, new); +} + +static void apr_adsp_down(unsigned long opcode) +{ + apr_set_q6_state(APR_SUBSYS_DOWN); + dispatch_event(opcode, APR_DEST_QDSP6); +} + +static void apr_adsp_up(void) +{ + if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) == + APR_SUBSYS_DOWN) + wake_up(&dsp_wait); +} + +int apr_wait_for_device_up(int dest_id) +{ + int rc = -1; + + if (dest_id == APR_DEST_MODEM) + rc = wait_event_interruptible_timeout(modem_wait, + (apr_get_modem_state() == APR_SUBSYS_UP), + (1 * HZ)); + else if (dest_id == APR_DEST_QDSP6) + rc = wait_event_interruptible_timeout(dsp_wait, + (apr_get_q6_state() == APR_SUBSYS_UP), + (1 * HZ)); + else + pr_err("%s: unknown dest_id %d\n", __func__, dest_id); + /* returns left time */ + return rc; +} + +int apr_load_adsp_image(void) +{ + int rc = 0; + + mutex_lock(&q6.lock); + if (apr_get_q6_state() == APR_SUBSYS_UP) { + q6.pil = subsystem_get("adsp"); + if (IS_ERR(q6.pil)) { + rc = PTR_ERR(q6.pil); + pr_err("APR: Unable to load q6 image, error:%d\n", rc); + } else { + apr_set_q6_state(APR_SUBSYS_LOADED); + pr_debug("APR: Image is loaded, stated\n"); + } + } else if (apr_get_q6_state() == APR_SUBSYS_LOADED) { + pr_debug("APR: q6 image already loaded\n"); + } else { + pr_debug("APR: cannot load state %d\n", apr_get_q6_state()); + } + mutex_unlock(&q6.lock); + return rc; +} + +struct apr_client *apr_get_client(int dest_id, int client_id) +{ + return &client[dest_id][client_id]; +} + +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + struct apr_hdr *hdr; + uint16_t dest_id; + uint16_t client_id; + uint16_t w_len; + int rc; + unsigned long flags; + + if (!handle || !buf) { + pr_err("APR: Wrong parameters\n"); + return -EINVAL; + } + if (svc->need_reset) { + pr_err("apr: send_pkt service need reset\n"); + return -ENETRESET; + } + + if ((svc->dest_id == APR_DEST_QDSP6) && + (apr_get_q6_state() != APR_SUBSYS_LOADED)) { + pr_err("%s: Still dsp is not Up\n", __func__); + return -ENETRESET; + } else if ((svc->dest_id == APR_DEST_MODEM) && + (apr_get_modem_state() == APR_SUBSYS_DOWN)) { + pr_err("apr: Still Modem is not Up\n"); + return -ENETRESET; + } + + spin_lock_irqsave(&svc->w_lock, flags); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (!client[dest_id][client_id].handle) { + pr_err("APR: Still service is not yet opened\n"); + spin_unlock_irqrestore(&svc->w_lock, flags); + return -EINVAL; + } + hdr = (struct apr_hdr *)buf; + + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + hdr->dest_domain = svc->dest_domain; + hdr->dest_svc = svc->id; + + if (unlikely(apr_cf_debug)) { + APR_PKT_INFO( + "Tx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]", + (hdr->src_domain << 8) | hdr->src_svc, + (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode, + hdr->token); + } + + rc = apr_tal_write(clnt->handle, buf, + (struct apr_pkt_priv *)&svc->pkt_owner, + hdr->pkt_size); + if (rc >= 0) { + w_len = rc; + if (w_len != hdr->pkt_size) { + pr_err("%s: Unable to write whole APR pkt successfully: %d\n", + __func__, rc); + rc = -EINVAL; + } + } else { + pr_err("%s: Write APR pkt failed with error %d\n", + __func__, rc); + } + spin_unlock_irqrestore(&svc->w_lock, flags); + + return rc; +} + +int apr_pkt_config(void *handle, struct apr_pkt_cfg *cfg) +{ + struct apr_svc *svc = (struct apr_svc *)handle; + uint16_t dest_id; + uint16_t client_id; + struct apr_client *clnt; + + if (!handle) { + pr_err("%s: Invalid handle\n", __func__); + return -EINVAL; + } + + if (svc->need_reset) { + pr_err("%s: service need reset\n", __func__); + return -ENETRESET; + } + + svc->pkt_owner = cfg->pkt_owner; + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + return apr_tal_rx_intents_config(clnt->handle, + cfg->intents.num_of_intents, cfg->intents.size); +} + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + struct apr_client *clnt; + int client_id = 0; + int svc_idx = 0; + int svc_id = 0; + int dest_id = 0; + int domain_id = 0; + int temp_port = 0; + struct apr_svc *svc = NULL; + int rc = 0; + bool can_open_channel = true; + + if (!dest || !svc_name || !svc_fn) + return NULL; + + if (!strcmp(dest, "ADSP")) + domain_id = APR_DOMAIN_ADSP; + else if (!strcmp(dest, "MODEM")) { + /* Don't request for SMD channels if destination is MODEM, + * as these channels are no longer used and these clients + * are to listen only for MODEM SSR events + */ + can_open_channel = false; + domain_id = APR_DOMAIN_MODEM; + } else { + pr_err("APR: wrong destination\n"); + goto done; + } + + dest_id = apr_get_dest_id(dest); + + if (dest_id == APR_DEST_QDSP6) { + if (apr_get_q6_state() != APR_SUBSYS_LOADED) { + pr_err("%s: adsp not up\n", __func__); + return NULL; + } + pr_debug("%s: adsp Up\n", __func__); + } else if (dest_id == APR_DEST_MODEM) { + if (apr_get_modem_state() == APR_SUBSYS_DOWN) { + if (is_modem_up) { + pr_err("%s: modem shutdown due to SSR, ret", + __func__); + return NULL; + } + pr_debug("%s: Wait for modem to bootup\n", __func__); + rc = apr_wait_for_device_up(APR_DEST_MODEM); + if (rc == 0) { + pr_err("%s: Modem is not Up\n", __func__); + return NULL; + } + } + pr_debug("%s: modem Up\n", __func__); + } + + if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) { + pr_err("%s: apr_get_svc failed\n", __func__); + goto done; + } + + clnt = &client[dest_id][client_id]; + mutex_lock(&clnt->m_lock); + if (!clnt->handle && can_open_channel) { + clnt->handle = apr_tal_open(client_id, dest_id, + APR_DL_SMD, apr_cb_func, NULL); + if (!clnt->handle) { + svc = NULL; + pr_err("APR: Unable to open handle\n"); + mutex_unlock(&clnt->m_lock); + goto done; + } + } + mutex_unlock(&clnt->m_lock); + svc = &clnt->svc[svc_idx]; + mutex_lock(&svc->m_lock); + clnt->id = client_id; + if (svc->need_reset) { + mutex_unlock(&svc->m_lock); + pr_err("APR: Service needs reset\n"); + goto done; + } + svc->id = svc_id; + svc->dest_id = dest_id; + svc->client_id = client_id; + svc->dest_domain = domain_id; + svc->pkt_owner = APR_PKT_OWNER_DRIVER; + + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", src_port, temp_port); + if (temp_port >= APR_MAX_PORTS || temp_port < 0) { + pr_err("APR: temp_port out of bounds\n"); + mutex_unlock(&svc->m_lock); + return NULL; + } + if (!svc->port_cnt && !svc->svc_cnt) + clnt->svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + } else { + if (!svc->fn) { + if (!svc->port_cnt && !svc->svc_cnt) + clnt->svc_cnt++; + svc->fn = svc_fn; + if (svc->port_cnt) + svc->svc_cnt++; + svc->priv = priv; + } + } + + mutex_unlock(&svc->m_lock); +done: + return svc; +} + + +void apr_cb_func(void *buf, int len, void *priv) +{ + struct apr_client_data data; + struct apr_client *apr_client; + struct apr_svc *c_svc; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + uint16_t clnt; + int i; + int temp_port = 0; + uint32_t *ptr; + + pr_debug("APR2: len = %d\n", len); + ptr = buf; + pr_debug("\n*****************\n"); + for (i = 0; i < len/4; i++) + pr_debug("%x ", ptr[i]); + pr_debug("\n"); + pr_debug("\n*****************\n"); + + if (!buf || len <= APR_HDR_SIZE) { + pr_err("APR: Improper apr pkt received:%pK %d\n", buf, len); + return; + } + hdr = buf; + + ver = hdr->hdr_field; + ver = (ver & 0x000F); + if (ver > APR_PKT_VER + 1) { + pr_err("APR: Wrong version: %d\n", ver); + return; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + pr_err("APR: Wrong hdr size:%d\n", hdr_size); + return; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + pr_err("APR: Wrong paket size\n"); + return; + } + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) { + pr_err("APR: Wrong message type: %d\n", msg_type); + return; + } + + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX || + hdr->dest_svc >= APR_SVC_MAX) { + pr_err("APR: Wrong APR header\n"); + return; + } + + svc = hdr->dest_svc; + if (hdr->src_domain == APR_DOMAIN_MODEM) { + if (svc == APR_SVC_MVS || svc == APR_SVC_MVM || + svc == APR_SVC_CVS || svc == APR_SVC_CVP || + svc == APR_SVC_TEST_CLIENT) + clnt = APR_CLIENT_VOICE; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else if (hdr->src_domain == APR_DOMAIN_ADSP) { + if (svc == APR_SVC_AFE || svc == APR_SVC_ASM || + svc == APR_SVC_VSM || svc == APR_SVC_VPM || + svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE || + svc == APR_SVC_USM || + svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM || + svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP || + svc == APR_SVC_LSM) + clnt = APR_CLIENT_AUDIO; + else if (svc == APR_SVC_VIDC) + clnt = APR_CLIENT_AUDIO; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else { + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return; + } + + src = apr_get_data_src(hdr); + if (src == APR_DEST_MAX) + return; + + pr_debug("src =%d clnt = %d\n", src, clnt); + apr_client = &client[src][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) + if (apr_client->svc[i].id == svc) { + pr_debug("%d\n", apr_client->svc[i].id); + c_svc = &apr_client->svc[i]; + break; + } + + if (i == APR_SVC_MAX) { + pr_err("APR: service is not registered\n"); + return; + } + pr_debug("svc_idx = %d\n", i); + pr_debug("%x %x %x %pK %pK\n", c_svc->id, c_svc->dest_id, + c_svc->client_id, c_svc->fn, c_svc->priv); + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + data.payload = NULL; + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + if (unlikely(apr_cf_debug)) { + if (hdr->opcode == APR_BASIC_RSP_RESULT && data.payload) { + uint32_t *ptr = data.payload; + + APR_PKT_INFO( + "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X] rc[0x%X]", + (hdr->src_domain << 8) | hdr->src_svc, + (hdr->dest_domain << 8) | hdr->dest_svc, + hdr->opcode, hdr->token, ptr[1]); + } else { + APR_PKT_INFO( + "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]", + (hdr->src_domain << 8) | hdr->src_svc, + (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode, + hdr->token); + } + } + + temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF); + pr_debug("port = %d t_port = %d\n", data.src_port, temp_port); + if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + pr_err("APR: Rxed a packet for NULL callback\n"); +} + +int apr_get_svc(const char *svc_name, int domain_id, int *client_id, + int *svc_idx, int *svc_id) +{ + int i; + int size; + struct apr_svc_table *tbl; + int ret = 0; + + if (domain_id == APR_DOMAIN_ADSP) { + tbl = (struct apr_svc_table *)&svc_tbl_qdsp6; + size = ARRAY_SIZE(svc_tbl_qdsp6); + } else { + tbl = (struct apr_svc_table *)&svc_tbl_voice; + size = ARRAY_SIZE(svc_tbl_voice); + } + + for (i = 0; i < size; i++) { + if (!strcmp(svc_name, tbl[i].name)) { + *client_id = tbl[i].client_id; + *svc_idx = tbl[i].idx; + *svc_id = tbl[i].id; + break; + } + } + + pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n", + __func__, svc_name, *client_id, domain_id); + if (i == size) { + pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name); + ret = -EINVAL; + } + + return ret; +} + +static void apr_reset_deregister(struct work_struct *work) +{ + struct apr_svc *handle = NULL; + struct apr_reset_work *apr_reset = + container_of(work, struct apr_reset_work, work); + + handle = apr_reset->handle; + pr_debug("%s:handle[%pK]\n", __func__, handle); + apr_deregister(handle); + kfree(apr_reset); +} + +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + uint16_t dest_id; + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + else if (svc->svc_cnt) + svc->svc_cnt--; + if (!svc->port_cnt && !svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + svc->need_reset = 0x0; + } + } else if (client[dest_id][client_id].svc_cnt > 0) { + client[dest_id][client_id].svc_cnt--; + if (!client[dest_id][client_id].svc_cnt) { + svc->need_reset = 0x0; + pr_debug("%s: service is reset %pK\n", __func__, svc); + } + } + + if (!svc->port_cnt && !svc->svc_cnt) { + svc->priv = NULL; + svc->id = 0; + svc->fn = NULL; + svc->dest_id = 0; + svc->client_id = 0; + svc->need_reset = 0x0; + } + if (client[dest_id][client_id].handle && + !client[dest_id][client_id].svc_cnt) { + apr_tal_close(client[dest_id][client_id].handle); + client[dest_id][client_id].handle = NULL; + } + mutex_unlock(&svc->m_lock); + + return 0; +} + +void apr_reset(void *handle) +{ + struct apr_reset_work *apr_reset_worker = NULL; + + if (!handle) + return; + pr_debug("%s: handle[%pK]\n", __func__, handle); + + if (apr_reset_workqueue == NULL) { + pr_err("%s: apr_reset_workqueue is NULL\n", __func__); + return; + } + + apr_reset_worker = kzalloc(sizeof(struct apr_reset_work), + GFP_ATOMIC); + + if (apr_reset_worker == NULL) { + pr_err("%s: mem failure\n", __func__); + return; + } + + apr_reset_worker->handle = handle; + INIT_WORK(&apr_reset_worker->work, apr_reset_deregister); + queue_work(apr_reset_workqueue, &apr_reset_worker->work); +} + +/* Dispatch the Reset events to Modem and audio clients */ +static void dispatch_event(unsigned long code, uint16_t proc) +{ + struct apr_client *apr_client; + struct apr_client_data data; + struct apr_svc *svc; + uint16_t clnt; + int i, j; + + data.opcode = RESET_EVENTS; + data.reset_event = code; + + /* Service domain can be different from the processor */ + data.reset_proc = apr_get_reset_domain(proc); + + clnt = APR_CLIENT_AUDIO; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } + + clnt = APR_CLIENT_VOICE; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } +} + +static int apr_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + struct audio_notifier_cb_data *cb_data = data; + + if (cb_data == NULL) { + pr_err("%s: Callback data is NULL!\n", __func__); + goto done; + } + + pr_debug("%s: Service opcode 0x%lx, domain %d\n", + __func__, opcode, cb_data->domain); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore down notifications during + * initial boot. There is no benefit from error + * recovery notifications during initial boot + * up since everything is expected to be down. + */ + if (is_initial_boot) + break; + if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) + apr_modem_down(opcode); + else + apr_adsp_down(opcode); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + is_initial_boot = false; + if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) + apr_modem_up(); + else + apr_adsp_up(); + break; + default: + break; + } +done: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = apr_notifier_service_cb, + .priority = 0, +}; + +static int __init apr_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DEST_MAX; i++) + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_init(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) { + mutex_init(&client[i][j].svc[k].m_lock); + spin_lock_init(&client[i][j].svc[k].w_lock); + } + } + apr_set_subsys_state(); + mutex_init(&q6.lock); + apr_reset_workqueue = create_singlethread_workqueue("apr_driver"); + if (!apr_reset_workqueue) + return -ENOMEM; + + apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT, + "apr", 0); + if (!apr_pkt_ctx) + pr_err("%s: Unable to create ipc log context\n", __func__); + + is_initial_boot = true; + subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN, + &service_nb); + + return 0; +} +device_initcall(apr_init); + +static int __init apr_late_init(void) +{ + int ret = 0; + + init_waitqueue_head(&dsp_wait); + init_waitqueue_head(&modem_wait); + + return ret; +} +late_initcall(apr_late_init); + +#ifdef CONFIG_DEBUG_FS +static int __init apr_debug_init(void) +{ + debugfs_apr_debug = debugfs_create_file("msm_apr_debug", + S_IFREG | 0444, NULL, NULL, + &apr_debug_ops); + return 0; +} +device_initcall(apr_debug_init); +#endif diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal.c b/drivers/soc/qcom/qdsp6v2/apr_tal.c new file mode 100644 index 000000000000..5c296f661073 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_tal.c @@ -0,0 +1,298 @@ +/* Copyright (c) 2010-2011, 2013-2014, 2016-2017 The Linux Foundation. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = { + { + "apr_audio_svc", + "apr_voice_svc", + }, + { + "apr_audio_svc", + "apr_voice_svc", + }, +}; + +struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX]; + +int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len) +{ + int w_len; + unsigned long flags; + + spin_lock_irqsave(&apr_ch->w_lock, flags); + if (smd_write_avail(apr_ch->ch) < len) { + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + return -EAGAIN; + } + + w_len = smd_write(apr_ch->ch, data, len); + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + + pr_debug("apr_tal:w_len = %d\n", w_len); + + if (w_len != len) { + pr_err("apr_tal: Error in write\n"); + return -ENETRESET; + } + return w_len; +} + +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len) +{ + int rc = 0, retries = 0; + + if (!apr_ch->ch) + return -EINVAL; + + do { + if (rc == -EAGAIN) + udelay(50); + + rc = __apr_tal_write(apr_ch, data, pkt_priv, len); + } while (rc == -EAGAIN && retries++ < 300); + + if (rc == -EAGAIN) + pr_err("apr_tal: TIMEOUT for write\n"); + + return rc; +} + +static void apr_tal_notify(void *priv, unsigned int event) +{ + struct apr_svc_ch_dev *apr_ch = priv; + int len, r_len, sz; + int pkt_cnt = 0; + unsigned long flags; + + pr_debug("event = %d\n", event); + switch (event) { + case SMD_EVENT_DATA: + pkt_cnt = 0; + spin_lock_irqsave(&apr_ch->lock, flags); +check_pending: + len = smd_read_avail(apr_ch->ch); + if (len < 0) { + pr_err("apr_tal: Invalid Read Event :%d\n", len); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + sz = smd_cur_packet_size(apr_ch->ch); + if (sz < 0) { + pr_debug("pkt size is zero\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + if (!len && !sz && !pkt_cnt) + goto check_write_avail; + if (!len) { + pr_debug("len = %d pkt_cnt = %d\n", len, pkt_cnt); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + r_len = smd_read_from_cb(apr_ch->ch, apr_ch->data, len); + if (len != r_len) { + pr_err("apr_tal: Invalid Read\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + pkt_cnt++; + pr_debug("%d %d %d\n", len, sz, pkt_cnt); + if (apr_ch->func) + apr_ch->func(apr_ch->data, r_len, apr_ch->priv); + goto check_pending; +check_write_avail: + if (smd_write_avail(apr_ch->ch)) + wake_up(&apr_ch->wait); + spin_unlock_irqrestore(&apr_ch->lock, flags); + break; + case SMD_EVENT_OPEN: + pr_debug("apr_tal: SMD_EVENT_OPEN\n"); + apr_ch->smd_state = 1; + wake_up(&apr_ch->wait); + break; + case SMD_EVENT_CLOSE: + pr_debug("apr_tal: SMD_EVENT_CLOSE\n"); + break; + } +} + +int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch, + int num_of_intents, uint32_t size) +{ + /* Rx intents configuration is required for Glink + * but not for SMD. No-op for this function. + */ + return 0; +} + +struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv) +{ + int rc; + + if ((clnt >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) || + (dl >= APR_DL_MAX)) { + pr_err("apr_tal: Invalid params\n"); + return NULL; + } + + if (apr_svc_ch[dl][dest][clnt].ch) { + pr_err("apr_tal: This channel alreday openend\n"); + return NULL; + } + + mutex_lock(&apr_svc_ch[dl][dest][clnt].m_lock); + if (!apr_svc_ch[dl][dest][clnt].dest_state) { + rc = wait_event_timeout(apr_svc_ch[dl][dest][clnt].dest, + apr_svc_ch[dl][dest][clnt].dest_state, + msecs_to_jiffies(APR_OPEN_TIMEOUT_MS)); + if (rc == 0) { + pr_err("apr_tal:open timeout\n"); + mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock); + return NULL; + } + pr_debug("apr_tal:Wakeup done\n"); + apr_svc_ch[dl][dest][clnt].dest_state = 0; + } + rc = smd_named_open_on_edge(svc_names[dest][clnt], dest, + &apr_svc_ch[dl][dest][clnt].ch, + &apr_svc_ch[dl][dest][clnt], + apr_tal_notify); + if (rc < 0) { + pr_err("apr_tal: smd_open failed %s\n", + svc_names[dest][clnt]); + mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock); + return NULL; + } + rc = wait_event_timeout(apr_svc_ch[dl][dest][clnt].wait, + (apr_svc_ch[dl][dest][clnt].smd_state == 1), 5 * HZ); + if (rc == 0) { + pr_err("apr_tal:TIMEOUT for OPEN event\n"); + mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock); + apr_tal_close(&apr_svc_ch[dl][dest][clnt]); + return NULL; + } + + smd_disable_read_intr(apr_svc_ch[dl][dest][clnt].ch); + + if (!apr_svc_ch[dl][dest][clnt].dest_state) { + apr_svc_ch[dl][dest][clnt].dest_state = 1; + pr_debug("apr_tal:Waiting for apr svc init\n"); + msleep(200); + pr_debug("apr_tal:apr svc init done\n"); + } + apr_svc_ch[dl][dest][clnt].smd_state = 0; + + apr_svc_ch[dl][dest][clnt].func = func; + apr_svc_ch[dl][dest][clnt].priv = priv; + mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock); + + return &apr_svc_ch[dl][dest][clnt]; +} + +int apr_tal_close(struct apr_svc_ch_dev *apr_ch) +{ + int r; + + if (!apr_ch->ch) + return -EINVAL; + + mutex_lock(&apr_ch->m_lock); + r = smd_close(apr_ch->ch); + apr_ch->ch = NULL; + apr_ch->func = NULL; + apr_ch->priv = NULL; + mutex_unlock(&apr_ch->m_lock); + return r; +} + +static int apr_smd_probe(struct platform_device *pdev) +{ + int dest; + int clnt; + + if (pdev->id == APR_DEST_MODEM) { + pr_info("apr_tal:Modem Is Up\n"); + dest = APR_DEST_MODEM; + if (!strcmp(pdev->name, "apr_audio_svc")) + clnt = APR_CLIENT_AUDIO; + else + clnt = APR_CLIENT_VOICE; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else if (pdev->id == APR_DEST_QDSP6) { + pr_info("apr_tal:Q6 Is Up\n"); + dest = APR_DEST_QDSP6; + clnt = APR_CLIENT_AUDIO; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else + pr_err("apr_tal:Invalid Dest Id: %d\n", pdev->id); + + return 0; +} + +static struct platform_driver apr_q6_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_audio_svc", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver apr_modem_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_voice_svc", + .owner = THIS_MODULE, + }, +}; + +static int __init apr_tal_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DL_MAX; i++) + for (j = 0; j < APR_DEST_MAX; j++) + for (k = 0; k < APR_CLIENT_MAX; k++) { + init_waitqueue_head(&apr_svc_ch[i][j][k].wait); + init_waitqueue_head(&apr_svc_ch[i][j][k].dest); + spin_lock_init(&apr_svc_ch[i][j][k].lock); + spin_lock_init(&apr_svc_ch[i][j][k].w_lock); + mutex_init(&apr_svc_ch[i][j][k].m_lock); + } + platform_driver_register(&apr_q6_driver); + platform_driver_register(&apr_modem_driver); + return 0; +} +device_initcall(apr_tal_init); diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c new file mode 100644 index 000000000000..b32ed9ee9d68 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -0,0 +1,514 @@ +/* Copyright (c) 2016-2017 The Linux Foundation. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APR_MAXIMUM_NUM_OF_RETRIES 2 + +struct apr_tx_buf { + struct list_head list; + struct apr_pkt_priv pkt_priv; + char buf[APR_MAX_BUF]; +}; + +struct apr_buf_list { + struct list_head list; + spinlock_t lock; +}; + +struct link_state { + uint32_t dest; + void *handle; + enum glink_link_state link_state; + wait_queue_head_t wait; +}; + +static struct link_state link_state[APR_DEST_MAX]; +static struct apr_buf_list buf_list; + +static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = { + { + "apr_audio_svc", + "apr_voice_svc", + }, + { + "apr_audio_svc", + "apr_voice_svc", + }, +}; + +static struct apr_svc_ch_dev + apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX]; + +static struct apr_tx_buf *apr_get_free_buf(int len) +{ + struct apr_tx_buf *tx_buf; + unsigned long flags; + + if (len > APR_MAX_BUF) { + pr_err("%s: buf too large [%d]\n", __func__, len); + return ERR_PTR(-EINVAL); + } + + spin_lock_irqsave(&buf_list.lock, flags); + if (list_empty(&buf_list.list)) { + spin_unlock_irqrestore(&buf_list.lock, flags); + pr_err("%s: No buf available\n", __func__); + return ERR_PTR(-ENOMEM); + } + + tx_buf = list_first_entry(&buf_list.list, struct apr_tx_buf, list); + list_del(&tx_buf->list); + spin_unlock_irqrestore(&buf_list.lock, flags); + + return tx_buf; +} + +static void apr_buf_add_tail(const void *buf) +{ + struct apr_tx_buf *list; + unsigned long flags; + + if (!buf) + return; + + spin_lock_irqsave(&buf_list.lock, flags); + list = container_of((void *)buf, struct apr_tx_buf, buf); + list_add_tail(&list->list, &buf_list.list); + spin_unlock_irqrestore(&buf_list.lock, flags); +} + +static int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len) +{ + int rc = 0; + unsigned long flags; + + spin_lock_irqsave(&apr_ch->w_lock, flags); + rc = glink_tx(apr_ch->handle, pkt_priv, data, len, + GLINK_TX_REQ_INTENT | GLINK_TX_ATOMIC); + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + + if (rc) + pr_err("%s: glink_tx failed, rc[%d]\n", __func__, rc); + else + rc = len; + + return rc; +} + +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len) +{ + int rc = 0, retries = 0; + void *pkt_data = NULL; + struct apr_tx_buf *tx_buf; + struct apr_pkt_priv *pkt_priv_ptr = pkt_priv; + + if (!apr_ch->handle || !pkt_priv) + return -EINVAL; + + if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) { + tx_buf = apr_get_free_buf(len); + if (IS_ERR_OR_NULL(tx_buf)) { + rc = -EINVAL; + goto exit; + } + memcpy(tx_buf->buf, data, len); + memcpy(&tx_buf->pkt_priv, pkt_priv, sizeof(tx_buf->pkt_priv)); + pkt_priv_ptr = &tx_buf->pkt_priv; + pkt_data = tx_buf->buf; + } else { + pkt_data = data; + } + + do { + if (rc == -EAGAIN) + udelay(50); + + rc = __apr_tal_write(apr_ch, pkt_data, pkt_priv_ptr, len); + } while (rc == -EAGAIN && retries++ < APR_MAXIMUM_NUM_OF_RETRIES); + + if (rc < 0) { + pr_err("%s: Unable to send the packet, rc:%d\n", __func__, rc); + if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) + apr_buf_add_tail(pkt_data); + } +exit: + return rc; +} + +void apr_tal_notify_rx(void *handle, const void *priv, const void *pkt_priv, + const void *ptr, size_t size) +{ + struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv; + unsigned long flags; + + if (!apr_ch || !ptr) { + pr_err("%s: Invalid apr_ch or ptr\n", __func__); + return; + } + + pr_debug("%s: Rx packet received\n", __func__); + + spin_lock_irqsave(&apr_ch->r_lock, flags); + if (apr_ch->func) + apr_ch->func((void *)ptr, size, (void *)pkt_priv); + spin_unlock_irqrestore(&apr_ch->r_lock, flags); + glink_rx_done(apr_ch->handle, ptr, true); +} + +static void apr_tal_notify_tx_abort(void *handle, const void *priv, + const void *pkt_priv) +{ + struct apr_pkt_priv *apr_pkt_priv_ptr = + (struct apr_pkt_priv *)pkt_priv; + struct apr_tx_buf *list_node; + + if (!apr_pkt_priv_ptr) { + pr_err("%s: Invalid pkt_priv\n", __func__); + return; + } + + pr_debug("%s: tx_abort received for apr_pkt_priv_ptr:%pK\n", + __func__, apr_pkt_priv_ptr); + + if (apr_pkt_priv_ptr->pkt_owner == APR_PKT_OWNER_DRIVER) { + list_node = container_of(apr_pkt_priv_ptr, + struct apr_tx_buf, pkt_priv); + apr_buf_add_tail(list_node->buf); + } +} + +void apr_tal_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + struct apr_pkt_priv *apr_pkt_priv = (struct apr_pkt_priv *)pkt_priv; + + if (!pkt_priv || !ptr) { + pr_err("%s: Invalid pkt_priv or ptr\n", __func__); + return; + } + + pr_debug("%s: tx_done received\n", __func__); + + if (apr_pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) + apr_buf_add_tail(ptr); +} + +bool apr_tal_notify_rx_intent_req(void *handle, const void *priv, + size_t req_size) +{ + struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv; + + if (!apr_ch) { + pr_err("%s: Invalid apr_ch\n", __func__); + return false; + } + + pr_err("%s: No rx intents queued, unable to receive\n", __func__); + return false; +} + +static void apr_tal_notify_remote_rx_intent(void *handle, const void *priv, + size_t size) +{ + struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv; + + if (!apr_ch) { + pr_err("%s: Invalid apr_ch\n", __func__); + return; + } + /* + * This is to make sure that the far end has queued at least one intent + * before we attmpt any IPC. A simple bool flag is used here instead of + * a counter, as the far end is required to guarantee intent + * availability for all use cases once the channel is fully opened. + */ + pr_debug("%s: remote queued an intent\n", __func__); + apr_ch->if_remote_intent_ready = true; +} + +void apr_tal_notify_state(void *handle, const void *priv, unsigned int event) +{ + struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv; + + if (!apr_ch) { + pr_err("%s: Invalid apr_ch\n", __func__); + return; + } + + apr_ch->channel_state = event; + pr_info("%s: Channel state[%d]\n", __func__, event); + + if (event == GLINK_CONNECTED) + wake_up(&apr_ch->wait); +} + +int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch, + int num_of_intents, uint32_t size) +{ + int i; + int rc; + + if (!apr_ch || !num_of_intents || !size) { + pr_err("%s: Invalid parameter\n", __func__); + return -EINVAL; + } + + for (i = 0; i < num_of_intents; i++) { + rc = glink_queue_rx_intent(apr_ch->handle, apr_ch, size); + if (rc) { + pr_err("%s: Failed to queue rx intent, iteration[%d]\n", + __func__, i); + break; + } + } + + return rc; +} + +struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl, + apr_svc_cb_fn func, void *priv) +{ + int rc; + struct glink_open_config open_cfg; + struct apr_svc_ch_dev *apr_ch; + + if ((clnt >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) || + (dl >= APR_DL_MAX)) { + pr_err("%s: Invalid params, clnt:%d, dest:%d, dl:%d\n", + __func__, clnt, dest, dl); + return NULL; + } + + apr_ch = &apr_svc_ch[dl][dest][clnt]; + mutex_lock(&apr_ch->m_lock); + if (apr_ch->handle) { + pr_err("%s: This channel is already opened\n", __func__); + rc = -EBUSY; + goto unlock; + } + + if (link_state[dest].link_state != GLINK_LINK_STATE_UP) { + rc = wait_event_timeout(link_state[dest].wait, + link_state[dest].link_state == GLINK_LINK_STATE_UP, + msecs_to_jiffies(APR_OPEN_TIMEOUT_MS)); + if (rc == 0) { + pr_err("%s: Open timeout, dest:%d\n", __func__, dest); + rc = -ETIMEDOUT; + goto unlock; + } + pr_debug("%s: Wakeup done, dest:%d\n", __func__, dest); + } + + memset(&open_cfg, 0, sizeof(struct glink_open_config)); + open_cfg.options = GLINK_OPT_INITIAL_XPORT; + if (dest == APR_DEST_MODEM) + open_cfg.edge = "mpss"; + else + open_cfg.edge = "lpass"; + + open_cfg.name = svc_names[dest][clnt]; + open_cfg.notify_rx = apr_tal_notify_rx; + open_cfg.notify_tx_done = apr_tal_notify_tx_done; + open_cfg.notify_state = apr_tal_notify_state; + open_cfg.notify_rx_intent_req = apr_tal_notify_rx_intent_req; + open_cfg.notify_remote_rx_intent = apr_tal_notify_remote_rx_intent; + open_cfg.notify_tx_abort = apr_tal_notify_tx_abort; + open_cfg.priv = apr_ch; + open_cfg.transport = "smem"; + + apr_ch->channel_state = GLINK_REMOTE_DISCONNECTED; + apr_ch->handle = glink_open(&open_cfg); + if (IS_ERR_OR_NULL(apr_ch->handle)) { + pr_err("%s: glink_open failed %s\n", __func__, + svc_names[dest][clnt]); + apr_ch->handle = NULL; + rc = -EINVAL; + goto unlock; + } + + rc = wait_event_timeout(apr_ch->wait, + (apr_ch->channel_state == GLINK_CONNECTED), 5 * HZ); + if (rc == 0) { + pr_err("%s: TIMEOUT for OPEN event\n", __func__); + rc = -ETIMEDOUT; + goto close_link; + } + + /* + * Remote intent is not required for GLINK <--> SMD IPC, so this is + * designed not to fail the open call. + */ + rc = wait_event_timeout(apr_ch->wait, + apr_ch->if_remote_intent_ready, 5 * HZ); + if (rc == 0) + pr_err("%s: TIMEOUT for remote intent readiness\n", __func__); + + rc = apr_tal_rx_intents_config(apr_ch, APR_DEFAULT_NUM_OF_INTENTS, + APR_MAX_BUF); + if (rc) { + pr_err("%s: Unable to queue intents\n", __func__); + goto close_link; + } + + apr_ch->func = func; + apr_ch->priv = priv; + +close_link: + if (rc) { + glink_close(apr_ch->handle); + apr_ch->handle = NULL; + } +unlock: + mutex_unlock(&apr_ch->m_lock); + + return rc ? NULL : apr_ch; +} + +int apr_tal_close(struct apr_svc_ch_dev *apr_ch) +{ + int rc; + + if (!apr_ch || !apr_ch->handle) { + rc = -EINVAL; + goto exit; + } + + mutex_lock(&apr_ch->m_lock); + rc = glink_close(apr_ch->handle); + apr_ch->handle = NULL; + apr_ch->func = NULL; + apr_ch->priv = NULL; + apr_ch->if_remote_intent_ready = false; + mutex_unlock(&apr_ch->m_lock); +exit: + return rc; +} + +static void apr_tal_link_state_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + uint32_t dest; + + if (!cb_info) { + pr_err("%s: Invalid cb_info\n", __func__); + return; + } + + if (!strcmp(cb_info->edge, "mpss")) + dest = APR_DEST_MODEM; + else if (!strcmp(cb_info->edge, "lpass")) + dest = APR_DEST_QDSP6; + else { + pr_err("%s:Unknown edge[%s]\n", __func__, cb_info->edge); + return; + } + + pr_info("%s: edge[%s] link state[%d]\n", __func__, cb_info->edge, + cb_info->link_state); + + link_state[dest].link_state = cb_info->link_state; + if (link_state[dest].link_state == GLINK_LINK_STATE_UP) + wake_up(&link_state[dest].wait); +} + +static struct glink_link_info mpss_link_info = { + .transport = "smem", + .edge = "mpss", + .glink_link_state_notif_cb = apr_tal_link_state_cb, +}; + +static struct glink_link_info lpass_link_info = { + .transport = "smem", + .edge = "lpass", + .glink_link_state_notif_cb = apr_tal_link_state_cb, +}; + +static int __init apr_tal_init(void) +{ + int i, j, k; + struct apr_tx_buf *buf; + struct list_head *ptr, *next; + + for (i = 0; i < APR_DL_MAX; i++) { + for (j = 0; j < APR_DEST_MAX; j++) { + for (k = 0; k < APR_CLIENT_MAX; k++) { + init_waitqueue_head(&apr_svc_ch[i][j][k].wait); + spin_lock_init(&apr_svc_ch[i][j][k].w_lock); + spin_lock_init(&apr_svc_ch[i][j][k].r_lock); + mutex_init(&apr_svc_ch[i][j][k].m_lock); + } + } + } + + for (i = 0; i < APR_DEST_MAX; i++) + init_waitqueue_head(&link_state[i].wait); + + spin_lock_init(&buf_list.lock); + INIT_LIST_HEAD(&buf_list.list); + for (i = 0; i < APR_NUM_OF_TX_BUF; i++) { + buf = kzalloc(sizeof(struct apr_tx_buf), GFP_KERNEL); + if (!buf) { + pr_err("%s: Unable to allocate tx buf\n", __func__); + goto tx_buf_alloc_fail; + } + + INIT_LIST_HEAD(&buf->list); + spin_lock(&buf_list.lock); + list_add_tail(&buf->list, &buf_list.list); + spin_unlock(&buf_list.lock); + } + + link_state[APR_DEST_MODEM].link_state = GLINK_LINK_STATE_DOWN; + link_state[APR_DEST_MODEM].handle = + glink_register_link_state_cb(&mpss_link_info, NULL); + if (!link_state[APR_DEST_MODEM].handle) + pr_err("%s: Unable to register mpss link state\n", __func__); + + link_state[APR_DEST_QDSP6].link_state = GLINK_LINK_STATE_DOWN; + link_state[APR_DEST_QDSP6].handle = + glink_register_link_state_cb(&lpass_link_info, NULL); + if (!link_state[APR_DEST_QDSP6].handle) + pr_err("%s: Unable to register lpass link state\n", __func__); + + return 0; + +tx_buf_alloc_fail: + list_for_each_safe(ptr, next, &buf_list.list) { + buf = list_entry(ptr, struct apr_tx_buf, list); + list_del(&buf->list); + kfree(buf); + } + return -ENOMEM; +} +device_initcall(apr_tal_init); diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/drivers/soc/qcom/qdsp6v2/apr_v2.c new file mode 100644 index 000000000000..4ddf39b1a097 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_v2.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum apr_subsys_state apr_get_subsys_state(void) +{ + return apr_get_q6_state(); +} + +void apr_set_subsys_state(void) +{ + apr_set_q6_state(APR_SUBSYS_DOWN); + apr_set_modem_state(APR_SUBSYS_UP); +} + +uint16_t apr_get_data_src(struct apr_hdr *hdr) +{ + if (hdr->src_domain == APR_DOMAIN_MODEM) + return APR_DEST_MODEM; + else if (hdr->src_domain == APR_DOMAIN_ADSP) + return APR_DEST_QDSP6; + + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return APR_DEST_MAX; /*RETURN INVALID VALUE*/ +} + +int apr_get_dest_id(char *dest) +{ + if (!strcmp(dest, "ADSP")) + return APR_DEST_QDSP6; + else + return APR_DEST_MODEM; +} + +void subsys_notif_register(char *client_name, int domain, + struct notifier_block *nb) +{ + int ret; + + ret = audio_notifier_register(client_name, domain, nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed for domain %d ret = %d\n", + __func__, domain, ret); +} + +uint16_t apr_get_reset_domain(uint16_t proc) +{ + return proc; +} diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/drivers/soc/qcom/qdsp6v2/apr_v3.c new file mode 100644 index 000000000000..2bfc518841c9 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_v3.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEST_ID APR_DEST_MODEM + +enum apr_subsys_state apr_get_subsys_state(void) +{ + return apr_get_modem_state(); +} + +void apr_set_subsys_state(void) +{ + apr_set_modem_state(APR_SUBSYS_DOWN); +} + +uint16_t apr_get_data_src(struct apr_hdr *hdr) +{ + return DEST_ID; +} + +int apr_get_dest_id(char *dest) +{ + return DEST_ID; +} + +void subsys_notif_register(char *client_name, int domain, + struct notifier_block *nb) +{ + int ret; + + if (domain != AUDIO_NOTIFIER_MODEM_DOMAIN) { + pr_debug("%s: Unused domain %d not registering with notifier\n", + __func__, domain); + return; + } + + ret = audio_notifier_register(client_name, domain, nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed for domain %d ret = %d\n", + __func__, domain, ret); +} + +uint16_t apr_get_reset_domain(uint16_t proc) +{ + return APR_DEST_QDSP6; +} diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c new file mode 100644 index 000000000000..9119e8baabfe --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c @@ -0,0 +1,635 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Audio states internal to notifier. Client */ +/* used states defined in audio_notifier.h */ +/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */ +#define NO_SERVICE -2 +#define UNINIT_SERVICE -1 + +/* + * Used for each client registered with audio notifier + */ +struct client_data { + struct list_head list; + /* Notifier block given by client */ + struct notifier_block *nb; + char client_name[20]; + int service; + int domain; +}; + +/* + * Used for each service and domain combination + * Tracks information specific to the underlying + * service. + */ +struct service_info { + const char name[20]; + int domain_id; + int state; + void *handle; + /* Notifier block registered to service */ + struct notifier_block *nb; + /* Used to determine when to register and deregister service */ + int num_of_clients; + /* List of all clients registered to the service and domain */ + struct srcu_notifier_head client_nb_list; +}; + +static int audio_notifer_ssr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data); +static int audio_notifer_ssr_modem_cb(struct notifier_block *this, + unsigned long opcode, void *data); +static int audio_notifer_pdr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data); + +static struct notifier_block notifier_ssr_adsp_nb = { + .notifier_call = audio_notifer_ssr_adsp_cb, + .priority = 0, +}; + +static struct notifier_block notifier_ssr_modem_nb = { + .notifier_call = audio_notifer_ssr_modem_cb, + .priority = 0, +}; + +static struct notifier_block notifier_pdr_adsp_nb = { + .notifier_call = audio_notifer_pdr_adsp_cb, + .priority = 0, +}; + +static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES] + [AUDIO_NOTIFIER_MAX_DOMAINS] = { + + {{ + .name = "SSR_ADSP", + .domain_id = AUDIO_SSR_DOMAIN_ADSP, + .state = AUDIO_NOTIFIER_SERVICE_DOWN, + .nb = ¬ifier_ssr_adsp_nb + }, + { + .name = "SSR_MODEM", + .domain_id = AUDIO_SSR_DOMAIN_MODEM, + .state = AUDIO_NOTIFIER_SERVICE_DOWN, + .nb = ¬ifier_ssr_modem_nb + } }, + + {{ + .name = "PDR_ADSP", + .domain_id = AUDIO_PDR_DOMAIN_ADSP, + .state = UNINIT_SERVICE, + .nb = ¬ifier_pdr_adsp_nb + }, + { /* PDR MODEM service not enabled */ + .name = "INVALID", + .state = NO_SERVICE, + .nb = NULL + } } +}; + +/* Master list of all audio notifier clients */ +struct list_head client_list; +struct mutex notifier_mutex; + +static int audio_notifer_get_default_service(int domain) +{ + int service = NO_SERVICE; + + /* initial service to connect per domain */ + switch (domain) { + case AUDIO_NOTIFIER_ADSP_DOMAIN: + service = AUDIO_NOTIFIER_PDR_SERVICE; + break; + case AUDIO_NOTIFIER_MODEM_DOMAIN: + service = AUDIO_NOTIFIER_SSR_SERVICE; + break; + } + + return service; +} + +static void audio_notifer_disable_service(int service) +{ + int i; + + for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) + service_data[service][i].state = NO_SERVICE; +} + +static bool audio_notifer_is_service_enabled(int service) +{ + int i; + + for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) + if (service_data[service][i].state != NO_SERVICE) + return true; + return false; +} + +static void audio_notifer_init_service(int service) +{ + int i; + + for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) { + if (service_data[service][i].state == UNINIT_SERVICE) + service_data[service][i].state = + AUDIO_NOTIFIER_SERVICE_DOWN; + } +} + +static int audio_notifer_reg_service(int service, int domain) +{ + void *handle; + int ret = 0; + int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN; + + switch (service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + handle = audio_ssr_register( + service_data[service][domain].domain_id, + service_data[service][domain].nb); + break; + case AUDIO_NOTIFIER_PDR_SERVICE: + handle = audio_pdr_service_register( + service_data[service][domain].domain_id, + service_data[service][domain].nb, &curr_state); + + if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01) + curr_state = AUDIO_NOTIFIER_SERVICE_UP; + else + curr_state = AUDIO_NOTIFIER_SERVICE_DOWN; + break; + default: + pr_err("%s: Invalid service %d\n", + __func__, service); + ret = -EINVAL; + goto done; + } + if (IS_ERR_OR_NULL(handle)) { + pr_err("%s: handle is incorrect for service %s\n", + __func__, service_data[service][domain].name); + ret = -EINVAL; + goto done; + } + service_data[service][domain].state = curr_state; + service_data[service][domain].handle = handle; + + pr_info("%s: service %s is in use\n", + __func__, service_data[service][domain].name); + pr_debug("%s: service %s has current state %d, handle 0x%pK\n", + __func__, service_data[service][domain].name, + service_data[service][domain].state, + service_data[service][domain].handle); +done: + return ret; +} + +static int audio_notifer_dereg_service(int service, int domain) +{ + int ret; + + switch (service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + ret = audio_ssr_deregister( + service_data[service][domain].handle, + service_data[service][domain].nb); + break; + case AUDIO_NOTIFIER_PDR_SERVICE: + ret = audio_pdr_service_deregister( + service_data[service][domain].handle, + service_data[service][domain].nb); + break; + default: + pr_err("%s: Invalid service %d\n", + __func__, service); + ret = -EINVAL; + goto done; + } + if (IS_ERR_VALUE(ret)) { + pr_err("%s: deregister failed for service %s, ret %d\n", + __func__, service_data[service][domain].name, ret); + goto done; + } + + pr_debug("%s: service %s with handle 0x%pK deregistered\n", + __func__, service_data[service][domain].name, + service_data[service][domain].handle); + + service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN; + service_data[service][domain].handle = NULL; +done: + return ret; +} + +static int audio_notifer_reg_client_service(struct client_data *client_data, + int service) +{ + int ret = 0; + int domain = client_data->domain; + struct audio_notifier_cb_data data; + + switch (service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + case AUDIO_NOTIFIER_PDR_SERVICE: + if (service_data[service][domain].num_of_clients == 0) + ret = audio_notifer_reg_service(service, domain); + break; + default: + pr_err("%s: Invalid service for client %s, service %d, domain %d\n", + __func__, client_data->client_name, service, domain); + ret = -EINVAL; + goto done; + } + + if (IS_ERR_VALUE(ret)) { + pr_err("%s: service registration failed on service %s for client %s\n", + __func__, service_data[service][domain].name, + client_data->client_name); + goto done; + } + + client_data->service = service; + srcu_notifier_chain_register( + &service_data[service][domain].client_nb_list, + client_data->nb); + service_data[service][domain].num_of_clients++; + + pr_debug("%s: registered client %s on service %s, current state 0x%x\n", + __func__, client_data->client_name, + service_data[service][domain].name, + service_data[service][domain].state); + + /* + * PDR registration returns current state + * Force callback of client with current state for PDR + */ + if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) { + data.service = service; + data.domain = domain; + (void)client_data->nb->notifier_call(client_data->nb, + service_data[service][domain].state, &data); + } +done: + return ret; +} + +static int audio_notifer_reg_client(struct client_data *client_data) +{ + int ret = 0; + int service; + int domain = client_data->domain; + + service = audio_notifer_get_default_service(domain); + if (service < 0) { + pr_err("%s: service %d is incorrect\n", __func__, service); + ret = -EINVAL; + goto done; + } + + /* Search through services to find a valid one to register client on. */ + for (; service >= 0; service--) { + /* If a service is not initialized, wait for it to come up. */ + if (service_data[service][domain].state == UNINIT_SERVICE) + goto done; + /* Skip unsupported service and domain combinations. */ + if (service_data[service][domain].state < 0) + continue; + /* Only register clients who have not acquired a service. */ + if (client_data->service != NO_SERVICE) + continue; + + /* + * Only register clients, who have not acquired a service, on + * the best available service for their domain. Uninitialized + * services will try to register all of their clients after + * they initialize correctly or will disable their service and + * register clients on the next best avaialable service. + */ + pr_debug("%s: register client %s on service %s", + __func__, client_data->client_name, + service_data[service][domain].name); + + ret = audio_notifer_reg_client_service(client_data, service); + if (IS_ERR_VALUE(ret)) + pr_err("%s: client %s failed to register on service %s", + __func__, client_data->client_name, + service_data[service][domain].name); + } + +done: + return ret; +} + +static int audio_notifer_dereg_client(struct client_data *client_data) +{ + int ret = 0; + int service = client_data->service; + int domain = client_data->domain; + + switch (client_data->service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + case AUDIO_NOTIFIER_PDR_SERVICE: + if (service_data[service][domain].num_of_clients == 1) + ret = audio_notifer_dereg_service(service, domain); + break; + case NO_SERVICE: + goto done; + default: + pr_err("%s: Invalid service for client %s, service %d\n", + __func__, client_data->client_name, + client_data->service); + ret = -EINVAL; + goto done; + } + + if (IS_ERR_VALUE(ret)) { + pr_err("%s: deregister failed for client %s on service %s, ret %d\n", + __func__, client_data->client_name, + service_data[service][domain].name, ret); + goto done; + } + + ret = srcu_notifier_chain_unregister(&service_data[service][domain]. + client_nb_list, client_data->nb); + if (IS_ERR_VALUE(ret)) { + pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n", + __func__, ret); + goto done; + } + + pr_debug("%s: deregistered client %s on service %s\n", + __func__, client_data->client_name, + service_data[service][domain].name); + + client_data->service = NO_SERVICE; + if (service_data[service][domain].num_of_clients > 0) + service_data[service][domain].num_of_clients--; +done: + return ret; +} + +static void audio_notifer_reg_all_clients(void) +{ + struct list_head *ptr, *next; + struct client_data *client_data; + int ret; + + list_for_each_safe(ptr, next, &client_list) { + client_data = list_entry(ptr, + struct client_data, list); + ret = audio_notifer_reg_client(client_data); + if (IS_ERR_VALUE(ret)) + pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n", + __func__, client_data->client_name, + ret); + } +} + +static int audio_notifer_pdr_callback(struct notifier_block *this, + unsigned long opcode, void *data) +{ + pr_debug("%s: Audio PDR framework state 0x%lx\n", + __func__, opcode); + mutex_lock(¬ifier_mutex); + if (opcode == AUDIO_PDR_FRAMEWORK_DOWN) + audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE); + else + audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE); + + audio_notifer_reg_all_clients(); + mutex_unlock(¬ifier_mutex); + return 0; +} + +static struct notifier_block pdr_nb = { + .notifier_call = audio_notifer_pdr_callback, + .priority = 0, +}; + +static int audio_notifer_convert_opcode(unsigned long opcode, + unsigned long *notifier_opcode) +{ + int ret = 0; + + switch (opcode) { + case SUBSYS_BEFORE_SHUTDOWN: + case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: + *notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN; + break; + case SUBSYS_AFTER_POWERUP: + case SERVREG_NOTIF_SERVICE_STATE_UP_V01: + *notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP; + break; + default: + pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode); + ret = -EINVAL; + } + + return ret; +} + +static int audio_notifer_service_cb(unsigned long opcode, + int service, int domain) +{ + int ret = 0; + unsigned long notifier_opcode; + struct audio_notifier_cb_data data; + + if (audio_notifer_convert_opcode(opcode, ¬ifier_opcode) < 0) + goto done; + + data.service = service; + data.domain = domain; + + pr_debug("%s: service %s, opcode 0x%lx\n", + __func__, service_data[service][domain].name, notifier_opcode); + + mutex_lock(¬ifier_mutex); + + service_data[service][domain].state = notifier_opcode; + ret = srcu_notifier_call_chain(&service_data[service][domain]. + client_nb_list, notifier_opcode, &data); + if (IS_ERR_VALUE(ret)) + pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n", + __func__, ret, service_data[service][domain].name, + notifier_opcode); + + mutex_unlock(¬ifier_mutex); +done: + return NOTIFY_OK; +} + +static int audio_notifer_pdr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + return audio_notifer_service_cb(opcode, + AUDIO_NOTIFIER_PDR_SERVICE, + AUDIO_NOTIFIER_ADSP_DOMAIN); +} + +static int audio_notifer_ssr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + if (opcode == SUBSYS_BEFORE_SHUTDOWN) + audio_ssr_send_nmi(data); + + return audio_notifer_service_cb(opcode, + AUDIO_NOTIFIER_SSR_SERVICE, + AUDIO_NOTIFIER_ADSP_DOMAIN); +} + +static int audio_notifer_ssr_modem_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + return audio_notifer_service_cb(opcode, + AUDIO_NOTIFIER_SSR_SERVICE, + AUDIO_NOTIFIER_MODEM_DOMAIN); +} + +int audio_notifier_deregister(char *client_name) +{ + int ret = 0; + int ret2; + struct list_head *ptr, *next; + struct client_data *client_data; + + if (client_name == NULL) { + pr_err("%s: client_name is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + mutex_lock(¬ifier_mutex); + list_for_each_safe(ptr, next, &client_data->list) { + client_data = list_entry(ptr, struct client_data, + list); + if (!strcmp(client_name, client_data->client_name)) { + ret2 = audio_notifer_dereg_client(client_data); + if (ret2 < 0) { + pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d", + __func__, ret2, client_data->service, + client_data->domain); + ret = ret2; + continue; + } + list_del(&client_data->list); + kfree(client_data); + } + } + mutex_unlock(¬ifier_mutex); +done: + return ret; +} +EXPORT_SYMBOL(audio_notifier_deregister); + +int audio_notifier_register(char *client_name, int domain, + struct notifier_block *nb) +{ + int ret; + struct client_data *client_data; + + if (client_name == NULL) { + pr_err("%s: client_name is NULL\n", __func__); + ret = -EINVAL; + goto done; + } else if (nb == NULL) { + pr_err("%s: Notifier block is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + client_data = kmalloc(sizeof(*client_data), GFP_KERNEL); + if (client_data == NULL) { + ret = -ENOMEM; + goto done; + } + INIT_LIST_HEAD(&client_data->list); + client_data->nb = nb; + strlcpy(client_data->client_name, client_name, + sizeof(client_data->client_name)); + client_data->service = NO_SERVICE; + client_data->domain = domain; + + mutex_lock(¬ifier_mutex); + ret = audio_notifer_reg_client(client_data); + if (IS_ERR_VALUE(ret)) { + mutex_unlock(¬ifier_mutex); + pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n", + __func__, client_data->client_name, + ret); + kfree(client_data); + goto done; + } + list_add_tail(&client_data->list, &client_list); + mutex_unlock(¬ifier_mutex); +done: + return ret; +} +EXPORT_SYMBOL(audio_notifier_register); + +static int __init audio_notifier_subsys_init(void) +{ + int i, j; + + mutex_init(¬ifier_mutex); + INIT_LIST_HEAD(&client_list); + for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) { + for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) { + if (service_data[i][j].state <= NO_SERVICE) + continue; + + srcu_init_notifier_head( + &service_data[i][j].client_nb_list); + } + } + + return 0; +} +subsys_initcall(audio_notifier_subsys_init); + +static int __init audio_notifier_init(void) +{ + int ret; + + ret = audio_pdr_register(&pdr_nb); + if (IS_ERR_VALUE(ret)) { + pr_debug("%s: PDR register failed, ret = %d, disable service\n", + __func__, ret); + audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE); + } + + /* Do not return error since PDR enablement is not critical */ + return 0; +} +module_init(audio_notifier_init); + +static int __init audio_notifier_late_init(void) +{ + /* + * If pdr registration failed, register clients on next service + * Do in late init to ensure that SSR subsystem is initialized + */ + if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE)) + audio_notifer_reg_all_clients(); + + return 0; +} +late_initcall(audio_notifier_late_init); diff --git a/drivers/soc/qcom/qdsp6v2/audio_pdr.c b/drivers/soc/qcom/qdsp6v2/audio_pdr.c new file mode 100644 index 000000000000..0270e1fc243a --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/audio_pdr.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = { + { /* AUDIO_PDR_DOMAIN_ADSP */ + .client_name = "audio_pdr_adsp", + .service_name = "avs/audio" + } +}; + +struct srcu_notifier_head audio_pdr_cb_list; + +static int audio_pdr_locator_callback(struct notifier_block *this, + unsigned long opcode, void *data) +{ + unsigned long pdr_state = AUDIO_PDR_FRAMEWORK_DOWN; + + if (opcode == LOCATOR_DOWN) { + pr_debug("%s: Service %s is down!", __func__, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]. + service_name); + goto done; + } + + memcpy(&audio_pdr_services, data, + sizeof(audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP])); + if (audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].total_domains == 1) { + pr_debug("%s: Service %s, returned total domains %d, ", + __func__, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]. + total_domains); + pdr_state = AUDIO_PDR_FRAMEWORK_UP; + goto done; + } else + pr_err("%s: Service %s returned invalid total domains %d", + __func__, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]. + total_domains); +done: + srcu_notifier_call_chain(&audio_pdr_cb_list, pdr_state, NULL); + return NOTIFY_OK; +} + +static struct notifier_block audio_pdr_locator_nb = { + .notifier_call = audio_pdr_locator_callback, + .priority = 0, +}; + +int audio_pdr_register(struct notifier_block *nb) +{ + if (nb == NULL) { + pr_err("%s: Notifier block is NULL\n", __func__); + return -EINVAL; + } + return srcu_notifier_chain_register(&audio_pdr_cb_list, nb); +} +EXPORT_SYMBOL(audio_pdr_register); + +void *audio_pdr_service_register(int domain_id, + struct notifier_block *nb, int *curr_state) +{ + void *handle; + + if ((domain_id < 0) || + (domain_id >= AUDIO_PDR_DOMAIN_MAX)) { + pr_err("%s: Invalid service ID %d\n", __func__, domain_id); + return ERR_PTR(-EINVAL); + } + + handle = service_notif_register_notifier( + audio_pdr_services[domain_id].domain_list[0].name, + audio_pdr_services[domain_id].domain_list[0].instance_id, + nb, curr_state); + if (IS_ERR_OR_NULL(handle)) { + pr_err("%s: Failed to register for service %s, instance %d\n", + __func__, + audio_pdr_services[domain_id].domain_list[0].name, + audio_pdr_services[domain_id].domain_list[0]. + instance_id); + } + return handle; +} +EXPORT_SYMBOL(audio_pdr_service_register); + +int audio_pdr_service_deregister(void *service_handle, + struct notifier_block *nb) +{ + int ret; + + if (service_handle == NULL) { + pr_err("%s: service handle is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ret = service_notif_unregister_notifier( + service_handle, nb); + if (IS_ERR_VALUE(ret)) + pr_err("%s: Failed to deregister service ret %d\n", + __func__, ret); +done: + return ret; +} +EXPORT_SYMBOL(audio_pdr_service_deregister); + +static int __init audio_pdr_subsys_init(void) +{ + srcu_init_notifier_head(&audio_pdr_cb_list); + return 0; +} +subsys_initcall(audio_pdr_subsys_init); + +static int __init audio_pdr_late_init(void) +{ + int ret; + + ret = get_service_location( + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].client_name, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name, + &audio_pdr_locator_nb); + if (IS_ERR_VALUE(ret)) { + pr_err("%s get_service_location failed ret %d\n", + __func__, ret); + srcu_notifier_call_chain(&audio_pdr_cb_list, + AUDIO_PDR_FRAMEWORK_DOWN, NULL); + } + + return ret; +} +late_initcall(audio_pdr_late_init); diff --git a/drivers/soc/qcom/qdsp6v2/audio_ssr.c b/drivers/soc/qcom/qdsp6v2/audio_ssr.c new file mode 100644 index 000000000000..a66fb2a63fae --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/audio_ssr.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define SCM_Q6_NMI_CMD 0x1 + +static char *audio_ssr_domains[] = { + "adsp", + "modem" +}; + +void *audio_ssr_register(int domain_id, struct notifier_block *nb) +{ + if ((domain_id < 0) || + (domain_id >= AUDIO_SSR_DOMAIN_MAX)) { + pr_err("%s: Invalid service ID %d\n", __func__, domain_id); + return ERR_PTR(-EINVAL); + } + + return subsys_notif_register_notifier( + audio_ssr_domains[domain_id], nb); +} +EXPORT_SYMBOL(audio_ssr_register); + +int audio_ssr_deregister(void *handle, struct notifier_block *nb) +{ + return subsys_notif_unregister_notifier(handle, nb); +} +EXPORT_SYMBOL(audio_ssr_deregister); + +void audio_ssr_send_nmi(void *ssr_cb_data) +{ + struct notif_data *data = (struct notif_data *)ssr_cb_data; + struct scm_desc desc; + + if (data && data->crashed) { + /* Send NMI to QDSP6 via an SCM call. */ + if (!is_scm_armv8()) { + scm_call_atomic1(SCM_SVC_UTIL, + SCM_Q6_NMI_CMD, 0x1); + } else { + desc.args[0] = 0x1; + desc.arginfo = SCM_ARGS(1); + scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL, + SCM_Q6_NMI_CMD), &desc); + } + /* The write should go through before q6 is shutdown */ + mb(); + pr_debug("%s: Q6 NMI was sent.\n", __func__); + } +} +EXPORT_SYMBOL(audio_ssr_send_nmi); diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c new file mode 100644 index 000000000000..a0217591f382 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_AUDIO_ION_PROBED (1 << 0) + +#define MSM_AUDIO_ION_PHYS_ADDR(alloc_data) \ + alloc_data->table->sgl->dma_address + +#define MSM_AUDIO_ION_VA_START 0x10000000 +#define MSM_AUDIO_ION_VA_LEN 0x0FFFFFFF + +#define MSM_AUDIO_SMMU_SID_OFFSET 32 + +struct addr_range { + dma_addr_t start; + size_t size; +}; + +struct context_bank_info { + const char *name; + struct addr_range addr_range; +}; + +struct msm_audio_ion_private { + bool smmu_enabled; + bool audioheap_enabled; + struct device *cb_dev; + struct dma_iommu_mapping *mapping; + u8 device_status; + struct list_head alloc_list; + struct mutex list_mutex; + u64 smmu_sid_bits; + u32 smmu_version; +}; + +struct msm_audio_alloc_data { + struct ion_client *client; + struct ion_handle *handle; + size_t len; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *table; + struct list_head list; +}; + +static struct msm_audio_ion_private msm_audio_ion_data = {0,}; + +static int msm_audio_ion_get_phys(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + +static int msm_audio_dma_buf_map(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + +static int msm_audio_dma_buf_unmap(struct ion_client *client, + struct ion_handle *handle); + +static void msm_audio_ion_add_allocation( + struct msm_audio_ion_private *msm_audio_ion_data, + struct msm_audio_alloc_data *alloc_data) +{ + /* + * Since these APIs can be invoked by multiple + * clients, there is need to make sure the list + * of allocations is always protected + */ + mutex_lock(&(msm_audio_ion_data->list_mutex)); + list_add_tail(&(alloc_data->list), + &(msm_audio_ion_data->alloc_list)); + mutex_unlock(&(msm_audio_ion_data->list_mutex)); +} + +int msm_audio_ion_alloc(const char *name, struct ion_client **client, + struct ion_handle **handle, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = -EINVAL; + unsigned long err_ion_ptr = 0; + + if ((msm_audio_ion_data.smmu_enabled == true) && + !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + if (!name || !client || !handle || !paddr || !vaddr + || !bufsz || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + *client = msm_audio_ion_client_create(name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client for AUDIO failed\n", __func__); + goto err; + } + + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_AUDIO_HEAP_ID), 0); + if (IS_ERR_OR_NULL((void *) (*handle))) { + if (msm_audio_ion_data.smmu_enabled == true) { + pr_debug("system heap is used"); + msm_audio_ion_data.audioheap_enabled = 0; + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_SYSTEM_HEAP_ID), 0); + } + if (IS_ERR_OR_NULL((void *) (*handle))) { + if (IS_ERR((void *)(*handle))) + err_ion_ptr = PTR_ERR((int *)(*handle)); + pr_err("%s:ION alloc fail err ptr=%ld, smmu_enabled=%d\n", + __func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled); + rc = -ENOMEM; + goto err_ion_client; + } + } else { + pr_debug("audio heap is used"); + msm_audio_ion_data.audioheap_enabled = 1; + } + + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_ion_handle; + } + + *vaddr = ion_map_kernel(*client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + goto err_ion_handle; + } + pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, + *vaddr, bufsz); + + if (bufsz != 0) { + pr_debug("%s: memset to 0 %pK %zd\n", __func__, *vaddr, bufsz); + memset((void *)*vaddr, 0, bufsz); + } + + return rc; + +err_ion_handle: + ion_free(*client, *handle); +err_ion_client: + msm_audio_ion_client_destroy(*client); + *handle = NULL; + *client = NULL; +err: + return rc; +} +EXPORT_SYMBOL(msm_audio_ion_alloc); + +int msm_audio_ion_import(const char *name, struct ion_client **client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + + if ((msm_audio_ion_data.smmu_enabled == true) && + !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + + if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + rc = -EINVAL; + goto err; + } + + *client = msm_audio_ion_client_create(name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client for AUDIO failed\n", __func__); + rc = -EINVAL; + goto err; + } + + /* name should be audio_acdb_client or Audio_Dec_Client, + * bufsz should be 0 and fd shouldn't be 0 as of now + */ + *handle = ion_import_dma_buf(*client, fd); + pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__, + name, fd, *handle); + if (IS_ERR_OR_NULL((void *) (*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + rc = -EINVAL; + goto err_destroy_client; + } + + if (ionflag != NULL) { + rc = ion_handle_get_flags(*client, *handle, ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", + __func__); + goto err_ion_handle; + } + } + + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_ion_handle; + } + + *vaddr = ion_map_kernel(*client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -ENOMEM; + goto err_ion_handle; + } + pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, + *vaddr, bufsz); + + return 0; + +err_ion_handle: + ion_free(*client, *handle); +err_destroy_client: + msm_audio_ion_client_destroy(*client); + *client = NULL; + *handle = NULL; +err: + return rc; +} + +int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle) +{ + if (!client || !handle) { + pr_err("%s Invalid params\n", __func__); + return -EINVAL; + } + if (msm_audio_ion_data.smmu_enabled) + msm_audio_dma_buf_unmap(client, handle); + + ion_unmap_kernel(client, handle); + + ion_free(client, handle); + msm_audio_ion_client_destroy(client); + return 0; +} +EXPORT_SYMBOL(msm_audio_ion_free); + +int msm_audio_ion_mmap(struct audio_buffer *ab, + struct vm_area_struct *vma) +{ + struct sg_table *table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + unsigned int i; + struct page *page; + int ret; + + pr_debug("%s\n", __func__); + + table = ion_sg_table(ab->client, ab->handle); + + if (IS_ERR(table)) { + pr_err("%s: Unable to get sg_table from ion: %ld\n", + __func__, PTR_ERR(table)); + return PTR_ERR(table); + } else if (!table) { + pr_err("%s: sg_list is NULL\n", __func__); + return -EINVAL; + } + + /* uncached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + /* We need to check if a page is associated with this sg list because: + * If the allocation came from a carveout we currently don't have + * pages associated with carved out memory. This might change in the + * future and we can remove this check and the else statement. + */ + page = sg_page(table->sgl); + if (page) { + pr_debug("%s: page is NOT null\n", __func__); + for_each_sg(table->sgl, sg, table->nents, i) { + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg->length; + + page = sg_page(sg); + + if (offset >= len) { + offset -= len; + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len -= offset; + offset = 0; + } + len = min(len, remainder); + pr_debug("vma=%pK, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n", + vma, (unsigned int)addr, len, + (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, + (unsigned long int)vma->vm_page_prot); + remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + addr += len; + if (addr >= vma->vm_end) + return 0; + } + } else { + ion_phys_addr_t phys_addr; + size_t phys_len; + size_t va_len = 0; + + pr_debug("%s: page is NULL\n", __func__); + ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len); + if (ret) { + pr_err("%s: Unable to get phys address from ION buffer: %d\n" + , __func__, ret); + return ret; + } + pr_debug("phys=%pKK len=%zd\n", &phys_addr, phys_len); + pr_debug("vma=%pK, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n", + vma, (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, vma->vm_pgoff, + (unsigned long int)vma->vm_page_prot); + va_len = vma->vm_end - vma->vm_start; + if ((offset > phys_len) || (va_len > phys_len-offset)) { + pr_err("wrong offset size %ld, lens= %zd, va_len=%zd\n", + offset, phys_len, va_len); + return -EINVAL; + } + ret = remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(phys_addr) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + } + return 0; +} + + +bool msm_audio_ion_is_smmu_available(void) +{ + return msm_audio_ion_data.smmu_enabled; +} + +/* move to static section again */ +struct ion_client *msm_audio_ion_client_create(const char *name) +{ + struct ion_client *pclient = NULL; + + pclient = msm_ion_client_create(name); + return pclient; +} + + +void msm_audio_ion_client_destroy(struct ion_client *client) +{ + pr_debug("%s: client = %pK smmu_enabled = %d\n", __func__, + client, msm_audio_ion_data.smmu_enabled); + + ion_client_destroy(client); +} + +int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + + if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + rc = -EINVAL; + goto err; + } + /* client is already created for legacy and given + * name should be audio_acdb_client or Audio_Dec_Client, + * bufsz should be 0 and fd shouldn't be 0 as of now + */ + *handle = ion_import_dma_buf(client, fd); + pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__, + name, fd, *handle); + if (IS_ERR_OR_NULL((void *)(*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + rc = -EINVAL; + goto err; + } + + if (ionflag != NULL) { + rc = ion_handle_get_flags(client, *handle, ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", + __func__); + rc = -EINVAL; + goto err_ion_handle; + } + } + + rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + rc = -EINVAL; + goto err_ion_handle; + } + + /*Need to add condition SMMU enable or not */ + *vaddr = ion_map_kernel(client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -EINVAL; + goto err_ion_handle; + } + + if (bufsz != 0) + memset((void *)*vaddr, 0, bufsz); + + return 0; + +err_ion_handle: + ion_free(client, *handle); +err: + return rc; +} + +int msm_audio_ion_free_legacy(struct ion_client *client, + struct ion_handle *handle) +{ + if (msm_audio_ion_data.smmu_enabled) + msm_audio_dma_buf_unmap(client, handle); + + ion_unmap_kernel(client, handle); + + ion_free(client, handle); + /* no client_destrody in legacy*/ + return 0; +} + +int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op) +{ + unsigned long ionflag = 0; + int rc = 0; + int msm_cache_ops = 0; + + if (!abuff) { + pr_err("%s: Invalid params: %pK\n", __func__, abuff); + return -EINVAL; + } + rc = ion_handle_get_flags(abuff->client, abuff->handle, + &ionflag); + if (rc) { + pr_err("ion_handle_get_flags failed: %d\n", rc); + goto cache_op_failed; + } + + /* has to be CACHED */ + if (ION_IS_CACHED(ionflag)) { + /* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */ + msm_cache_ops = cache_op; + rc = msm_ion_do_cache_op(abuff->client, + abuff->handle, + (unsigned long *) abuff->data, + (unsigned long)abuff->size, + msm_cache_ops); + if (rc) { + pr_err("cache operation failed %d\n", rc); + goto cache_op_failed; + } + } +cache_op_failed: + return rc; +} + + +static int msm_audio_dma_buf_map(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len) +{ + + struct msm_audio_alloc_data *alloc_data; + struct device *cb_dev; + int rc = 0; + + cb_dev = msm_audio_ion_data.cb_dev; + + /* Data required per buffer mapping */ + alloc_data = kzalloc(sizeof(*alloc_data), GFP_KERNEL); + if (!alloc_data) + return -ENOMEM; + + /* Get the ION handle size */ + ion_handle_get_size(client, handle, len); + + alloc_data->client = client; + alloc_data->handle = handle; + alloc_data->len = *len; + + /* Get the dma_buf handle from ion_handle */ + alloc_data->dma_buf = ion_share_dma_buf(client, handle); + if (IS_ERR(alloc_data->dma_buf)) { + rc = PTR_ERR(alloc_data->dma_buf); + dev_err(cb_dev, + "%s: Fail to get dma_buf handle, rc = %d\n", + __func__, rc); + goto err_dma_buf; + } + + /* Attach the dma_buf to context bank device */ + alloc_data->attach = dma_buf_attach(alloc_data->dma_buf, + cb_dev); + if (IS_ERR(alloc_data->attach)) { + rc = PTR_ERR(alloc_data->attach); + dev_err(cb_dev, + "%s: Fail to attach dma_buf to CB, rc = %d\n", + __func__, rc); + goto err_attach; + } + + /* + * Get the scatter-gather list. + * There is no info as this is a write buffer or + * read buffer, hence the request is bi-directional + * to accommodate both read and write mappings. + */ + alloc_data->table = dma_buf_map_attachment(alloc_data->attach, + DMA_BIDIRECTIONAL); + if (IS_ERR(alloc_data->table)) { + rc = PTR_ERR(alloc_data->table); + dev_err(cb_dev, + "%s: Fail to map attachment, rc = %d\n", + __func__, rc); + goto err_map_attach; + } + + rc = dma_map_sg(cb_dev, alloc_data->table->sgl, + alloc_data->table->nents, + DMA_BIDIRECTIONAL); + if (rc != alloc_data->table->nents) { + dev_err(cb_dev, + "%s: Fail to map SG, rc = %d, nents = %d\n", + __func__, rc, alloc_data->table->nents); + goto err_map_sg; + } + /* Make sure not to return rc from dma_map_sg, as it can be nonzero */ + rc = 0; + + /* physical address from mapping */ + *addr = MSM_AUDIO_ION_PHYS_ADDR(alloc_data); + + msm_audio_ion_add_allocation(&msm_audio_ion_data, + alloc_data); + return rc; + +err_map_sg: + dma_buf_unmap_attachment(alloc_data->attach, + alloc_data->table, + DMA_BIDIRECTIONAL); +err_map_attach: + dma_buf_detach(alloc_data->dma_buf, + alloc_data->attach); +err_attach: + dma_buf_put(alloc_data->dma_buf); + +err_dma_buf: + kfree(alloc_data); + + return rc; +} + +static int msm_audio_dma_buf_unmap(struct ion_client *client, + struct ion_handle *handle) +{ + int rc = 0; + struct msm_audio_alloc_data *alloc_data = NULL; + struct list_head *ptr, *next; + struct device *cb_dev = msm_audio_ion_data.cb_dev; + bool found = false; + + /* + * Though list_for_each_safe is delete safe, lock + * should be explicitly acquired to avoid race condition + * on adding elements to the list. + */ + mutex_lock(&(msm_audio_ion_data.list_mutex)); + list_for_each_safe(ptr, next, + &(msm_audio_ion_data.alloc_list)) { + + alloc_data = list_entry(ptr, struct msm_audio_alloc_data, + list); + + if (alloc_data->handle == handle && + alloc_data->client == client) { + found = true; + dma_unmap_sg(cb_dev, + alloc_data->table->sgl, + alloc_data->table->nents, + DMA_BIDIRECTIONAL); + + dma_buf_unmap_attachment(alloc_data->attach, + alloc_data->table, + DMA_BIDIRECTIONAL); + + dma_buf_detach(alloc_data->dma_buf, + alloc_data->attach); + + dma_buf_put(alloc_data->dma_buf); + + list_del(&(alloc_data->list)); + kfree(alloc_data); + break; + } + } + mutex_unlock(&(msm_audio_ion_data.list_mutex)); + + if (!found) { + dev_err(cb_dev, + "%s: cannot find allocation, ion_handle %pK, ion_client %pK", + __func__, handle, client); + rc = -EINVAL; + } + + return rc; +} + +static int msm_audio_ion_get_phys(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len) +{ + int rc = 0; + + pr_debug("%s: smmu_enabled = %d\n", __func__, + msm_audio_ion_data.smmu_enabled); + + if (msm_audio_ion_data.smmu_enabled) { + rc = msm_audio_dma_buf_map(client, handle, addr, len); + if (rc) { + pr_err("%s: failed to map DMA buf, err = %d\n", + __func__, rc); + goto err; + } + /* Append the SMMU SID information to the IOVA address */ + *addr |= msm_audio_ion_data.smmu_sid_bits; + } else { + rc = ion_phys(client, handle, addr, len); + } + + pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc); +err: + return rc; +} + +static int msm_audio_smmu_init_legacy(struct device *dev) +{ + struct dma_iommu_mapping *mapping; + struct device_node *ctx_node = NULL; + struct context_bank_info *cb; + int ret; + u32 read_val[2]; + + cb = devm_kzalloc(dev, sizeof(struct context_bank_info), GFP_KERNEL); + if (!cb) + return -ENOMEM; + + ctx_node = of_parse_phandle(dev->of_node, "iommus", 0); + if (!ctx_node) { + dev_err(dev, "%s Could not find any iommus for audio\n", + __func__); + return -EINVAL; + } + ret = of_property_read_string(ctx_node, "label", &(cb->name)); + if (ret) { + dev_err(dev, "%s Could not find label\n", __func__); + return -EINVAL; + } + pr_debug("label found : %s\n", cb->name); + ret = of_property_read_u32_array(ctx_node, + "qcom,virtual-addr-pool", + read_val, 2); + if (ret) { + dev_err(dev, "%s Could not read addr pool for group : (%d)\n", + __func__, ret); + return -EINVAL; + } + msm_audio_ion_data.cb_dev = msm_iommu_get_ctx(cb->name); + cb->addr_range.start = (dma_addr_t) read_val[0]; + cb->addr_range.size = (size_t) read_val[1]; + dev_dbg(dev, "%s Legacy iommu usage\n", __func__); + mapping = arm_iommu_create_mapping( + msm_iommu_get_bus(msm_audio_ion_data.cb_dev), + cb->addr_range.start, + cb->addr_range.size); + if (IS_ERR(mapping)) + return PTR_ERR(mapping); + + ret = arm_iommu_attach_device(msm_audio_ion_data.cb_dev, mapping); + if (ret) { + dev_err(dev, "%s: Attach failed, err = %d\n", + __func__, ret); + goto fail_attach; + } + + msm_audio_ion_data.mapping = mapping; + INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list); + mutex_init(&(msm_audio_ion_data.list_mutex)); + + return 0; + +fail_attach: + arm_iommu_release_mapping(mapping); + return ret; +} + +static int msm_audio_smmu_init(struct device *dev) +{ + struct dma_iommu_mapping *mapping; + int ret; + + mapping = arm_iommu_create_mapping( + msm_iommu_get_bus(dev), + MSM_AUDIO_ION_VA_START, + MSM_AUDIO_ION_VA_LEN); + if (IS_ERR(mapping)) + return PTR_ERR(mapping); + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + dev_err(dev, "%s: Attach failed, err = %d\n", + __func__, ret); + goto fail_attach; + } + + msm_audio_ion_data.cb_dev = dev; + msm_audio_ion_data.mapping = mapping; + INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list); + mutex_init(&(msm_audio_ion_data.list_mutex)); + + return 0; + +fail_attach: + arm_iommu_release_mapping(mapping); + return ret; +} + +static const struct of_device_id msm_audio_ion_dt_match[] = { + { .compatible = "qcom,msm-audio-ion" }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); + + +u32 msm_audio_ion_get_smmu_sid_mode32(void) +{ + if (msm_audio_ion_data.smmu_enabled) + return upper_32_bits(msm_audio_ion_data.smmu_sid_bits); + else + return 0; +} + +u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa) +{ + if (sizeof(ion_phys_addr_t) == sizeof(u32)) + return msm_audio_ion_get_smmu_sid_mode32(); + else + return upper_32_bits(pa); +} + +static int msm_audio_ion_probe(struct platform_device *pdev) +{ + int rc = 0; + const char *msm_audio_ion_dt = "qcom,smmu-enabled"; + const char *msm_audio_ion_smmu = "qcom,smmu-version"; + bool smmu_enabled; + enum apr_subsys_state q6_state; + struct device *dev = &pdev->dev; + + if (dev->of_node == NULL) { + dev_err(dev, + "%s: device tree is not found\n", + __func__); + msm_audio_ion_data.smmu_enabled = 0; + return 0; + } + + smmu_enabled = of_property_read_bool(dev->of_node, + msm_audio_ion_dt); + msm_audio_ion_data.smmu_enabled = smmu_enabled; + + if (smmu_enabled) { + rc = of_property_read_u32(dev->of_node, + msm_audio_ion_smmu, + &msm_audio_ion_data.smmu_version); + if (rc) { + dev_err(dev, + "%s: qcom,smmu_version missing in DT node\n", + __func__); + return rc; + } + dev_dbg(dev, "%s: SMMU version is (%d)", __func__, + msm_audio_ion_data.smmu_version); + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + dev_dbg(dev, + "defering %s, adsp_state %d\n", + __func__, q6_state); + return -EPROBE_DEFER; + } + dev_dbg(dev, "%s: adsp is ready\n", __func__); + } + + dev_dbg(dev, "%s: SMMU is %s\n", __func__, + (smmu_enabled) ? "Enabled" : "Disabled"); + + if (smmu_enabled) { + u64 smmu_sid = 0; + struct of_phandle_args iommuspec; + + /* Get SMMU SID information from Devicetree */ + rc = of_parse_phandle_with_args(dev->of_node, "iommus", + "#iommu-cells", 0, &iommuspec); + if (rc) + dev_err(dev, "%s: could not get smmu SID, ret = %d\n", + __func__, rc); + else + smmu_sid = iommuspec.args[0]; + + msm_audio_ion_data.smmu_sid_bits = + smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET; + + if (msm_audio_ion_data.smmu_version == 0x1) { + rc = msm_audio_smmu_init_legacy(dev); + } else if (msm_audio_ion_data.smmu_version == 0x2) { + rc = msm_audio_smmu_init(dev); + } else { + dev_err(dev, "%s: smmu version invalid %d\n", + __func__, msm_audio_ion_data.smmu_version); + rc = -EINVAL; + } + if (rc) + dev_err(dev, "%s: smmu init failed, err = %d\n", + __func__, rc); + } + + if (!rc) + msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED; + + return rc; +} + +static int msm_audio_ion_remove(struct platform_device *pdev) +{ + struct dma_iommu_mapping *mapping; + struct device *audio_cb_dev; + + mapping = msm_audio_ion_data.mapping; + audio_cb_dev = msm_audio_ion_data.cb_dev; + + if (audio_cb_dev && mapping) { + arm_iommu_detach_device(audio_cb_dev); + arm_iommu_release_mapping(mapping); + } + + msm_audio_ion_data.smmu_enabled = 0; + msm_audio_ion_data.device_status = 0; + return 0; +} + +static struct platform_driver msm_audio_ion_driver = { + .driver = { + .name = "msm-audio-ion", + .owner = THIS_MODULE, + .of_match_table = msm_audio_ion_dt_match, + }, + .probe = msm_audio_ion_probe, + .remove = msm_audio_ion_remove, +}; + +static int __init msm_audio_ion_init(void) +{ + return platform_driver_register(&msm_audio_ion_driver); +} +module_init(msm_audio_ion_init); + +static void __exit msm_audio_ion_exit(void) +{ + platform_driver_unregister(&msm_audio_ion_driver); +} +module_exit(msm_audio_ion_exit); + +MODULE_DESCRIPTION("MSM Audio ION module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c new file mode 100644 index 000000000000..07e899157d2b --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c @@ -0,0 +1,792 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MINOR_NUMBER 1 +#define APR_MAX_RESPONSE 10 +#define TIMEOUT_MS 1000 + +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) + +struct voice_svc_device { + struct cdev *cdev; + struct device *dev; + int major; +}; + +struct voice_svc_prvt { + void *apr_q6_mvm; + void *apr_q6_cvs; + uint16_t response_count; + struct list_head response_queue; + wait_queue_head_t response_wait; + spinlock_t response_lock; +}; + +struct apr_data { + struct apr_hdr hdr; + __u8 payload[0]; +} __packed; + +struct apr_response_list { + struct list_head list; + struct voice_svc_cmd_response resp; +}; + +static struct voice_svc_device *voice_svc_dev; +static struct class *voice_svc_class; +static bool reg_dummy_sess; +static void *dummy_q6_mvm; +static void *dummy_q6_cvs; +dev_t device_num; + +static int voice_svc_dummy_reg(void); +static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data, + void *priv); + +static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) +{ + struct voice_svc_prvt *prtd; + struct apr_response_list *response_list; + unsigned long spin_flags; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + + return -EINVAL; + } + + prtd = (struct voice_svc_prvt *)priv; + if (prtd == NULL) { + pr_err("%s: private data is NULL\n", __func__); + + return -EINVAL; + } + + pr_debug("%s: data->opcode %x\n", __func__, + data->opcode); + + if (data->opcode == RESET_EVENTS) { + if (data->reset_proc == APR_DEST_QDSP6) { + pr_debug("%s: Received ADSP reset event\n", __func__); + + if (prtd->apr_q6_mvm != NULL) { + apr_reset(prtd->apr_q6_mvm); + prtd->apr_q6_mvm = NULL; + } + + if (prtd->apr_q6_cvs != NULL) { + apr_reset(prtd->apr_q6_cvs); + prtd->apr_q6_cvs = NULL; + } + } else if (data->reset_proc == APR_DEST_MODEM) { + pr_debug("%s: Received Modem reset event\n", __func__); + } + /* Set the remaining member variables to default values + * for RESET_EVENTS + */ + data->payload_size = 0; + data->payload = NULL; + data->src_port = 0; + data->dest_port = 0; + data->token = 0; + } + + spin_lock_irqsave(&prtd->response_lock, spin_flags); + + if (prtd->response_count < APR_MAX_RESPONSE) { + response_list = kmalloc(sizeof(struct apr_response_list) + + data->payload_size, GFP_ATOMIC); + if (response_list == NULL) { + spin_unlock_irqrestore(&prtd->response_lock, + spin_flags); + return -ENOMEM; + } + + response_list->resp.src_port = data->src_port; + + /* Reverting the bit manipulation done in voice_svc_update_hdr + * to the src_port which is returned to us as dest_port. + */ + response_list->resp.dest_port = ((data->dest_port) >> 8); + response_list->resp.token = data->token; + response_list->resp.opcode = data->opcode; + response_list->resp.payload_size = data->payload_size; + if (data->payload != NULL && data->payload_size > 0) { + memcpy(response_list->resp.payload, data->payload, + data->payload_size); + } + + list_add_tail(&response_list->list, &prtd->response_queue); + prtd->response_count++; + spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + + wake_up(&prtd->response_wait); + } else { + spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + pr_err("%s: Response dropped since the queue is full\n", + __func__); + } + + return 0; +} + +static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data, void *priv) +{ + /* Do Nothing */ + return 0; +} + +static void voice_svc_update_hdr(struct voice_svc_cmd_request *apr_req_data, + struct apr_data *aprdata) +{ + + aprdata->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + /* Bit manipulation is done on src_port so that a unique ID is sent. + * This manipulation can be used in the future where the same service + * is tried to open multiple times with the same src_port. At that + * time 0x0001 can be replaced with other values depending on the + * count. + */ + aprdata->hdr.src_port = ((apr_req_data->src_port) << 8 | 0x0001); + aprdata->hdr.dest_port = apr_req_data->dest_port; + aprdata->hdr.token = apr_req_data->token; + aprdata->hdr.opcode = apr_req_data->opcode; + aprdata->hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + apr_req_data->payload_size); + memcpy(aprdata->payload, apr_req_data->payload, + apr_req_data->payload_size); +} + +static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request, + struct voice_svc_prvt *prtd) +{ + int ret = 0; + void *apr_handle = NULL; + struct apr_data *aprdata = NULL; + uint32_t user_payload_size; + uint32_t payload_size; + + pr_debug("%s\n", __func__); + + if (apr_request == NULL) { + pr_err("%s: apr_request is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + user_payload_size = apr_request->payload_size; + payload_size = sizeof(struct apr_data) + user_payload_size; + + if (payload_size <= user_payload_size) { + pr_err("%s: invalid payload size ( 0x%x ).\n", + __func__, user_payload_size); + ret = -EINVAL; + goto done; + } else { + aprdata = kmalloc(payload_size, GFP_KERNEL); + if (aprdata == NULL) { + ret = -ENOMEM; + goto done; + } + } + + voice_svc_update_hdr(apr_request, aprdata); + + if (!strcmp(apr_request->svc_name, VOICE_SVC_CVS_STR)) { + apr_handle = prtd->apr_q6_cvs; + } else if (!strcmp(apr_request->svc_name, VOICE_SVC_MVM_STR)) { + apr_handle = prtd->apr_q6_mvm; + } else { + pr_err("%s: Invalid service %.*s\n", __func__, + MAX_APR_SERVICE_NAME_LEN, apr_request->svc_name); + + ret = -EINVAL; + goto done; + } + + ret = apr_send_pkt(apr_handle, (uint32_t *)aprdata); + + if (ret < 0) { + pr_err("%s: Fail in sending request %d\n", + __func__, ret); + ret = -EINVAL; + } else { + pr_debug("%s: apr packet sent successfully %d\n", + __func__, ret); + ret = 0; + } + +done: + kfree(aprdata); + return ret; +} +static int voice_svc_reg(char *svc, uint32_t src_port, + struct voice_svc_prvt *prtd, void **handle) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + if (handle == NULL) { + pr_err("%s: handle is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + if (*handle != NULL) { + pr_err("%s: svc handle not NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + if (src_port == (APR_MAX_PORTS - 1)) { + pr_err("%s: SRC port reserved for dummy session\n", __func__); + pr_err("%s: Unable to register %s\n", __func__, svc); + ret = -EINVAL; + goto done; + } + + *handle = apr_register("ADSP", + svc, qdsp_apr_callback, + ((src_port) << 8 | 0x0001), + prtd); + + if (*handle == NULL) { + pr_err("%s: Unable to register %s\n", + __func__, svc); + + ret = -EFAULT; + goto done; + } + pr_debug("%s: Register %s successful\n", + __func__, svc); +done: + return ret; +} + +static int voice_svc_dereg(char *svc, void **handle) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + if (handle == NULL) { + pr_err("%s: handle is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + if (*handle == NULL) { + pr_err("%s: svc handle is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ret = apr_deregister(*handle); + if (ret) { + pr_err("%s: Unable to deregister service %s; error: %d\n", + __func__, svc, ret); + + goto done; + } + *handle = NULL; + pr_debug("%s: deregister %s successful\n", __func__, svc); + +done: + return ret; +} + +static int process_reg_cmd(struct voice_svc_register *apr_reg_svc, + struct voice_svc_prvt *prtd) +{ + int ret = 0; + char *svc = NULL; + void **handle = NULL; + + pr_debug("%s\n", __func__); + + if (!strcmp(apr_reg_svc->svc_name, VOICE_SVC_MVM_STR)) { + svc = VOICE_SVC_MVM_STR; + handle = &prtd->apr_q6_mvm; + } else if (!strcmp(apr_reg_svc->svc_name, VOICE_SVC_CVS_STR)) { + svc = VOICE_SVC_CVS_STR; + handle = &prtd->apr_q6_cvs; + } else { + pr_err("%s: Invalid Service: %.*s\n", __func__, + MAX_APR_SERVICE_NAME_LEN, apr_reg_svc->svc_name); + ret = -EINVAL; + goto done; + } + + if (apr_reg_svc->reg_flag) { + ret = voice_svc_reg(svc, apr_reg_svc->src_port, prtd, + handle); + } else if (!apr_reg_svc->reg_flag) { + ret = voice_svc_dereg(svc, handle); + } + +done: + return ret; +} + +static ssize_t voice_svc_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + struct voice_svc_prvt *prtd; + struct voice_svc_write_msg *data = NULL; + uint32_t cmd; + + pr_debug("%s\n", __func__); + + /* + * Check if enough memory is allocated to parse the message type. + * Will check there is enough to hold the payload later. + */ + if (count >= sizeof(struct voice_svc_write_msg)) { + data = kmalloc(count, GFP_KERNEL); + } else { + pr_debug("%s: invalid data size\n", __func__); + ret = -EINVAL; + goto done; + } + + if (data == NULL) { + pr_err("%s: data kmalloc failed.\n", __func__); + + ret = -ENOMEM; + goto done; + } + + ret = copy_from_user(data, buf, count); + if (ret) { + pr_err("%s: copy_from_user failed %d\n", __func__, ret); + + ret = -EPERM; + goto done; + } + + cmd = data->msg_type; + prtd = (struct voice_svc_prvt *) file->private_data; + if (prtd == NULL) { + pr_err("%s: prtd is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + switch (cmd) { + case MSG_REGISTER: + /* + * Check that count reflects the expected size to ensure + * sufficient memory was allocated. Since voice_svc_register + * has a static size, this should be exact. + */ + if (count == (sizeof(struct voice_svc_write_msg) + + sizeof(struct voice_svc_register))) { + ret = process_reg_cmd( + (struct voice_svc_register *)data->payload, prtd); + if (!ret) + ret = count; + } else { + pr_err("%s: invalid payload size\n", __func__); + ret = -EINVAL; + goto done; + } + break; + case MSG_REQUEST: + /* + * Check that count reflects the expected size to ensure + * sufficient memory was allocated. Since voice_svc_cmd_request + * has a variable size, check the minimum value count must be. + */ + if (count >= (sizeof(struct voice_svc_write_msg) + + sizeof(struct voice_svc_cmd_request))) { + ret = voice_svc_send_req( + (struct voice_svc_cmd_request *)data->payload, prtd); + if (!ret) + ret = count; + } else { + pr_err("%s: invalid payload size\n", __func__); + ret = -EINVAL; + goto done; + } + break; + default: + pr_debug("%s: Invalid command: %u\n", __func__, cmd); + ret = -EINVAL; + } + +done: + kfree(data); + return ret; +} + +static ssize_t voice_svc_read(struct file *file, char __user *arg, + size_t count, loff_t *ppos) +{ + int ret = 0; + struct voice_svc_prvt *prtd; + struct apr_response_list *resp; + unsigned long spin_flags; + int size; + + pr_debug("%s\n", __func__); + + prtd = (struct voice_svc_prvt *)file->private_data; + if (prtd == NULL) { + pr_err("%s: prtd is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&prtd->response_lock, spin_flags); + + if (list_empty(&prtd->response_queue)) { + spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + pr_debug("%s: wait for a response\n", __func__); + + ret = wait_event_interruptible_timeout(prtd->response_wait, + !list_empty(&prtd->response_queue), + msecs_to_jiffies(TIMEOUT_MS)); + if (ret == 0) { + pr_debug("%s: Read timeout\n", __func__); + + ret = -ETIMEDOUT; + goto done; + } else if (ret > 0 && !list_empty(&prtd->response_queue)) { + pr_debug("%s: Interrupt received for response\n", + __func__); + } else if (ret < 0) { + pr_debug("%s: Interrupted by SIGNAL %d\n", + __func__, ret); + + goto done; + } + + spin_lock_irqsave(&prtd->response_lock, spin_flags); + } + + resp = list_first_entry(&prtd->response_queue, + struct apr_response_list, list); + + spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + + size = resp->resp.payload_size + + sizeof(struct voice_svc_cmd_response); + + if (count < size) { + pr_err("%s: Invalid payload size %zd, %d\n", + __func__, count, size); + + ret = -ENOMEM; + goto done; + } + + if (!access_ok(VERIFY_WRITE, arg, size)) { + pr_err("%s: Access denied to write\n", + __func__); + + ret = -EPERM; + goto done; + } + + ret = copy_to_user(arg, &resp->resp, + sizeof(struct voice_svc_cmd_response) + + resp->resp.payload_size); + if (ret) { + pr_err("%s: copy_to_user failed %d\n", __func__, ret); + + ret = -EPERM; + goto done; + } + + spin_lock_irqsave(&prtd->response_lock, spin_flags); + + list_del(&resp->list); + prtd->response_count--; + kfree(resp); + + spin_unlock_irqrestore(&prtd->response_lock, + spin_flags); + + ret = count; + +done: + return ret; +} + +static int voice_svc_dummy_reg(void) +{ + uint32_t src_port = APR_MAX_PORTS - 1; + + pr_debug("%s\n", __func__); + dummy_q6_mvm = apr_register("ADSP", "MVM", + qdsp_dummy_apr_callback, + src_port, + NULL); + if (dummy_q6_mvm == NULL) { + pr_err("%s: Unable to register dummy MVM\n", __func__); + goto err; + } + + dummy_q6_cvs = apr_register("ADSP", "CVS", + qdsp_dummy_apr_callback, + src_port, + NULL); + if (dummy_q6_cvs == NULL) { + pr_err("%s: Unable to register dummy CVS\n", __func__); + goto err; + } + return 0; +err: + if (dummy_q6_mvm != NULL) { + apr_deregister(dummy_q6_mvm); + dummy_q6_mvm = NULL; + } + return -EINVAL; +} + +static int voice_svc_open(struct inode *inode, struct file *file) +{ + struct voice_svc_prvt *prtd = NULL; + + pr_debug("%s\n", __func__); + + prtd = kmalloc(sizeof(struct voice_svc_prvt), GFP_KERNEL); + + if (prtd == NULL) + return -ENOMEM; + + memset(prtd, 0, sizeof(struct voice_svc_prvt)); + prtd->apr_q6_cvs = NULL; + prtd->apr_q6_mvm = NULL; + prtd->response_count = 0; + INIT_LIST_HEAD(&prtd->response_queue); + init_waitqueue_head(&prtd->response_wait); + spin_lock_init(&prtd->response_lock); + file->private_data = (void *)prtd; + + /* Current APR implementation doesn't support session based + * multiple service registrations. The apr_deregister() + * function sets the destination and client IDs to zero, if + * deregister is called for a single service instance. + * To avoid this, register for additional services. + */ + if (!reg_dummy_sess) { + voice_svc_dummy_reg(); + reg_dummy_sess = 1; + } + return 0; +} + +static int voice_svc_release(struct inode *inode, struct file *file) +{ + int ret = 0; + struct apr_response_list *resp = NULL; + unsigned long spin_flags; + struct voice_svc_prvt *prtd = NULL; + char *svc_name = NULL; + void **handle = NULL; + + pr_debug("%s\n", __func__); + + prtd = (struct voice_svc_prvt *)file->private_data; + if (prtd == NULL) { + pr_err("%s: prtd is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (prtd->apr_q6_cvs != NULL) { + svc_name = VOICE_SVC_MVM_STR; + handle = &prtd->apr_q6_cvs; + ret = voice_svc_dereg(svc_name, handle); + if (ret) + pr_err("%s: Failed to dereg CVS %d\n", __func__, ret); + } + + if (prtd->apr_q6_mvm != NULL) { + svc_name = VOICE_SVC_MVM_STR; + handle = &prtd->apr_q6_mvm; + ret = voice_svc_dereg(svc_name, handle); + if (ret) + pr_err("%s: Failed to dereg MVM %d\n", __func__, ret); + } + + spin_lock_irqsave(&prtd->response_lock, spin_flags); + + while (!list_empty(&prtd->response_queue)) { + pr_debug("%s: Remove item from response queue\n", __func__); + + resp = list_first_entry(&prtd->response_queue, + struct apr_response_list, list); + list_del(&resp->list); + prtd->response_count--; + kfree(resp); + } + + spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + + kfree(file->private_data); + file->private_data = NULL; + +done: + return ret; +} + +static const struct file_operations voice_svc_fops = { + .owner = THIS_MODULE, + .open = voice_svc_open, + .read = voice_svc_read, + .write = voice_svc_write, + .release = voice_svc_release, +}; + + +static int voice_svc_probe(struct platform_device *pdev) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + voice_svc_dev = devm_kzalloc(&pdev->dev, + sizeof(struct voice_svc_device), GFP_KERNEL); + if (!voice_svc_dev) { + ret = -ENOMEM; + goto done; + } + + ret = alloc_chrdev_region(&device_num, 0, MINOR_NUMBER, + VOICE_SVC_DRIVER_NAME); + if (ret) { + pr_err("%s: Failed to alloc chrdev\n", __func__); + ret = -ENODEV; + goto chrdev_err; + } + + voice_svc_dev->major = MAJOR(device_num); + voice_svc_class = class_create(THIS_MODULE, VOICE_SVC_DRIVER_NAME); + if (IS_ERR(voice_svc_class)) { + ret = PTR_ERR(voice_svc_class); + pr_err("%s: Failed to create class; err = %d\n", __func__, + ret); + goto class_err; + } + + voice_svc_dev->dev = device_create(voice_svc_class, NULL, device_num, + NULL, VOICE_SVC_DRIVER_NAME); + if (IS_ERR(voice_svc_dev->dev)) { + ret = PTR_ERR(voice_svc_dev->dev); + pr_err("%s: Failed to create device; err = %d\n", __func__, + ret); + goto dev_err; + } + + voice_svc_dev->cdev = cdev_alloc(); + if (!voice_svc_dev->cdev) { + pr_err("%s: Failed to alloc cdev\n", __func__); + ret = -ENOMEM; + goto cdev_alloc_err; + } + + cdev_init(voice_svc_dev->cdev, &voice_svc_fops); + ret = cdev_add(voice_svc_dev->cdev, device_num, MINOR_NUMBER); + if (ret) { + pr_err("%s: Failed to register chrdev; err = %d\n", __func__, + ret); + goto add_err; + } + pr_debug("%s: Device created\n", __func__); + goto done; + +add_err: + cdev_del(voice_svc_dev->cdev); +cdev_alloc_err: + device_destroy(voice_svc_class, device_num); +dev_err: + class_destroy(voice_svc_class); +class_err: + unregister_chrdev_region(0, MINOR_NUMBER); +chrdev_err: + kfree(voice_svc_dev); +done: + return ret; +} + +static int voice_svc_remove(struct platform_device *pdev) +{ + pr_debug("%s\n", __func__); + + cdev_del(voice_svc_dev->cdev); + kfree(voice_svc_dev->cdev); + device_destroy(voice_svc_class, device_num); + class_destroy(voice_svc_class); + unregister_chrdev_region(0, MINOR_NUMBER); + kfree(voice_svc_dev); + + return 0; +} + +static const struct of_device_id voice_svc_of_match[] = { + {.compatible = "qcom,msm-voice-svc"}, + { } +}; +MODULE_DEVICE_TABLE(of, voice_svc_of_match); + +static struct platform_driver voice_svc_driver = { + .probe = voice_svc_probe, + .remove = voice_svc_remove, + .driver = { + .name = "msm-voice-svc", + .owner = THIS_MODULE, + .of_match_table = voice_svc_of_match, + }, +}; + +static int __init voice_svc_init(void) +{ + pr_debug("%s\n", __func__); + + return platform_driver_register(&voice_svc_driver); +} + +static void __exit voice_svc_exit(void) +{ + pr_debug("%s\n", __func__); + + platform_driver_unregister(&voice_svc_driver); +} + +module_init(voice_svc_init); +module_exit(voice_svc_exit); + +MODULE_DESCRIPTION("Soc QDSP6v2 Voice Service driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/clock/qcom,audio-ext-clk.h b/include/dt-bindings/clock/qcom,audio-ext-clk.h new file mode 100644 index 000000000000..c9a8286d7c7f --- /dev/null +++ b/include/dt-bindings/clock/qcom,audio-ext-clk.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __AUDIO_EXT_CLK_H +#define __AUDIO_EXT_CLK_H + +/* Audio External Clocks */ +#ifdef CONFIG_COMMON_CLK_QCOM +#define AUDIO_PMI_CLK 0 +#define AUDIO_PMIC_LNBB_CLK 1 +#define AUDIO_AP_CLK 2 +#define AUDIO_AP_CLK2 3 +#define AUDIO_LPASS_MCLK 4 +#define AUDIO_LPASS_MCLK2 5 +#else +#define clk_audio_ap_clk 0x9b5727cb +#define clk_audio_pmi_clk 0xcbfe416d +#define clk_audio_ap_clk2 0x454d1e91 +#define clk_audio_lpass_mclk 0xf0f2a284 +#define clk_audio_pmi_lnbb_clk 0x57312343 +#endif + +#endif diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h new file mode 100644 index 000000000000..0761b880ca88 --- /dev/null +++ b/include/linux/msm_audio_ion.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_MSM_AUDIO_ION_H +#define _LINUX_MSM_AUDIO_ION_H +#include +#include +#include + + +int msm_audio_ion_alloc(const char *name, struct ion_client **client, + struct ion_handle **handle, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr); + +int msm_audio_ion_import(const char *name, struct ion_client **client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr); +int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle); +int msm_audio_ion_mmap(struct audio_buffer *substream, + struct vm_area_struct *vma); + +bool msm_audio_ion_is_smmu_available(void); +int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op); + +struct ion_client *msm_audio_ion_client_create(const char *name); +void msm_audio_ion_client_destroy(struct ion_client *client); +int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr); +int msm_audio_ion_free_legacy(struct ion_client *client, + struct ion_handle *handle); +u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa); +#endif /* _LINUX_MSM_AUDIO_ION_H */ diff --git a/include/linux/qdsp6v2/apr.h b/include/linux/qdsp6v2/apr.h new file mode 100644 index 000000000000..29deb3ca5ac7 --- /dev/null +++ b/include/linux/qdsp6v2/apr.h @@ -0,0 +1,190 @@ +/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_H_ +#define __APR_H_ + +#include +#include + +enum apr_subsys_state { + APR_SUBSYS_DOWN, + APR_SUBSYS_UP, + APR_SUBSYS_LOADED, +}; + +struct apr_q6 { + void *pil; + atomic_t q6_state; + atomic_t modem_state; + struct mutex lock; +}; + +struct apr_hdr { + uint16_t hdr_field; + uint16_t pkt_size; + uint8_t src_svc; + uint8_t src_domain; + uint16_t src_port; + uint8_t dest_svc; + uint8_t dest_domain; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; +}; + +#define APR_HDR_LEN(hdr_len) ((hdr_len)/4) +#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len)) +#define APR_HDR_FIELD(msg_type, hdr_len, ver)\ + (((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF)) + +#define APR_HDR_SIZE sizeof(struct apr_hdr) + +/* Version */ +#define APR_PKT_VER 0x0 + +/* Command and Response Types */ +#define APR_MSG_TYPE_EVENT 0x0 +#define APR_MSG_TYPE_CMD_RSP 0x1 +#define APR_MSG_TYPE_SEQ_CMD 0x2 +#define APR_MSG_TYPE_NSEQ_CMD 0x3 +#define APR_MSG_TYPE_MAX 0x04 + +/* APR Basic Response Message */ +#define APR_BASIC_RSP_RESULT 0x000110E8 +#define APR_RSP_ACCEPTED 0x000100BE + +/* Domain IDs */ +#define APR_DOMAIN_SIM 0x1 +#define APR_DOMAIN_PC 0x2 +#define APR_DOMAIN_MODEM 0x3 +#define APR_DOMAIN_ADSP 0x4 +#define APR_DOMAIN_APPS 0x5 +#define APR_DOMAIN_MAX 0x6 + +/* ADSP service IDs */ +#define APR_SVC_TEST_CLIENT 0x2 +#define APR_SVC_ADSP_CORE 0x3 +#define APR_SVC_AFE 0x4 +#define APR_SVC_VSM 0x5 +#define APR_SVC_VPM 0x6 +#define APR_SVC_ASM 0x7 +#define APR_SVC_ADM 0x8 +#define APR_SVC_ADSP_MVM 0x09 +#define APR_SVC_ADSP_CVS 0x0A +#define APR_SVC_ADSP_CVP 0x0B +#define APR_SVC_USM 0x0C +#define APR_SVC_LSM 0x0D +#define APR_SVC_VIDC 0x16 +#define APR_SVC_MAX 0x17 + +/* Modem Service IDs */ +#define APR_SVC_MVS 0x3 +#define APR_SVC_MVM 0x4 +#define APR_SVC_CVS 0x5 +#define APR_SVC_CVP 0x6 +#define APR_SVC_SRD 0x7 + +/* APR Port IDs */ +#define APR_MAX_PORTS 0x80 + +#define APR_NAME_MAX 0x40 + +#define RESET_EVENTS 0x000130D7 + +#define LPASS_RESTART_EVENT 0x1000 +#define LPASS_RESTART_READY 0x1001 + +struct apr_client_data { + uint16_t reset_event; + uint16_t reset_proc; + uint16_t payload_size; + uint16_t hdr_len; + uint16_t msg_type; + uint16_t src; + uint16_t dest_svc; + uint16_t src_port; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; + void *payload; +}; + +typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv); + +struct apr_svc { + uint16_t id; + uint16_t dest_id; + uint16_t client_id; + uint16_t dest_domain; + uint8_t rvd; + uint8_t port_cnt; + uint8_t svc_cnt; + uint8_t need_reset; + apr_fn port_fn[APR_MAX_PORTS]; + void *port_priv[APR_MAX_PORTS]; + apr_fn fn; + void *priv; + struct mutex m_lock; + spinlock_t w_lock; + uint8_t pkt_owner; +}; + +struct apr_client { + uint8_t id; + uint8_t svc_cnt; + uint8_t rvd; + struct mutex m_lock; + struct apr_svc_ch_dev *handle; + struct apr_svc svc[APR_SVC_MAX]; +}; + +struct apr_rx_intents { + int num_of_intents; + uint32_t size; +}; + +struct apr_pkt_cfg { + uint8_t pkt_owner; + struct apr_rx_intents intents; +}; + +int apr_load_adsp_image(void); +struct apr_client *apr_get_client(int dest_id, int client_id); +int apr_wait_for_device_up(int dest_id); +int apr_get_svc(const char *svc_name, int dest_id, int *client_id, + int *svc_idx, int *svc_id); +void apr_cb_func(void *buf, int len, void *priv); +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv); +inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port, + uint16_t msg_type, uint16_t dest_port, + uint32_t token, uint32_t opcode, uint16_t len); + +int apr_send_pkt(void *handle, uint32_t *buf); +int apr_deregister(void *handle); +void subsys_notif_register(char *client_name, int domain, + struct notifier_block *nb); +int apr_get_dest_id(char *dest); +uint16_t apr_get_data_src(struct apr_hdr *hdr); +void change_q6_state(int state); +void q6audio_dsp_not_responding(void); +void apr_reset(void *handle); +enum apr_subsys_state apr_get_subsys_state(void); +enum apr_subsys_state apr_get_modem_state(void); +void apr_set_modem_state(enum apr_subsys_state state); +enum apr_subsys_state apr_get_q6_state(void); +int apr_set_q6_state(enum apr_subsys_state state); +void apr_set_subsys_state(void); +const char *apr_get_lpass_subsys_name(void); +uint16_t apr_get_reset_domain(uint16_t proc); +#endif diff --git a/include/linux/qdsp6v2/apr_tal.h b/include/linux/qdsp6v2/apr_tal.h new file mode 100644 index 000000000000..32c977f51622 --- /dev/null +++ b/include/linux/qdsp6v2/apr_tal.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2010-2011, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_TAL_H_ +#define __APR_TAL_H_ + +#include +#include +#include + +/* APR Client IDs */ +#define APR_CLIENT_AUDIO 0x0 +#define APR_CLIENT_VOICE 0x1 +#define APR_CLIENT_MAX 0x2 + +#define APR_DL_SMD 0 +#define APR_DL_MAX 1 + +#define APR_DEST_MODEM 0 +#define APR_DEST_QDSP6 1 +#define APR_DEST_MAX 2 + +#if defined(CONFIG_MSM_QDSP6_APRV2_GLINK) || \ + defined(CONFIG_MSM_QDSP6_APRV3_GLINK) +#define APR_MAX_BUF 512 +#define APR_NUM_OF_TX_BUF 30 +#else +#define APR_MAX_BUF 8092 +#endif + +#define APR_DEFAULT_NUM_OF_INTENTS 20 + +#define APR_OPEN_TIMEOUT_MS 5000 + +enum { + /* If client sets the pkt_owner to APR_PKT_OWNER_DRIVER, APR + * driver will allocate a buffer, where the user packet is + * copied into, for each and every single Tx transmission. + * The buffer is thereafter passed to underlying link layer + * and freed upon the notification received from the link layer + * that the packet has been consumed. + */ + APR_PKT_OWNER_DRIVER, + /* If client sets the pkt_owner to APR_PKT_OWNER_CLIENT, APR + * will pass the user packet memory address directly to underlying + * link layer. In this case it is the client's responsibility to + * make sure the packet is intact until being notified that the + * packet has been consumed. + */ + APR_PKT_OWNER_CLIENT, +}; + +struct apr_pkt_priv { + /* This property is only applicable for APR over Glink. + * It is ignored in APR over SMD cases. + */ + uint8_t pkt_owner; +}; + +typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv); +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv); +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len); +int apr_tal_close(struct apr_svc_ch_dev *apr_ch); +int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch, + int num_of_intents, uint32_t size); + + +#if defined(CONFIG_MSM_QDSP6_APRV2_GLINK) || \ + defined(CONFIG_MSM_QDSP6_APRV3_GLINK) +struct apr_svc_ch_dev { + void *handle; + spinlock_t w_lock; + spinlock_t r_lock; + struct mutex m_lock; + apr_svc_cb_fn func; + wait_queue_head_t wait; + void *priv; + unsigned int channel_state; + bool if_remote_intent_ready; +}; +#else +struct apr_svc_ch_dev { + struct smd_channel *ch; + spinlock_t lock; + spinlock_t w_lock; + struct mutex m_lock; + apr_svc_cb_fn func; + char data[APR_MAX_BUF]; + wait_queue_head_t wait; + void *priv; + uint32_t smd_state; + wait_queue_head_t dest; + uint32_t dest_state; +}; +#endif + +#endif diff --git a/include/linux/qdsp6v2/apr_us.h b/include/linux/qdsp6v2/apr_us.h new file mode 100644 index 000000000000..9a6804a4d634 --- /dev/null +++ b/include/linux/qdsp6v2/apr_us.h @@ -0,0 +1,193 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_US_H__ +#define __APR_US_H__ + +#include + +/* ======================================================================= */ +/* Session Level commands */ + +#define USM_SESSION_CMD_RUN 0x00012306 +struct usm_stream_cmd_run { + struct apr_hdr hdr; + u32 flags; + u32 msw_ts; + u32 lsw_ts; +} __packed; + +/* Stream level commands */ +#define USM_STREAM_CMD_OPEN_READ 0x00012309 +struct usm_stream_cmd_open_read { + struct apr_hdr hdr; + u32 uMode; + u32 src_endpoint; + u32 pre_proc_top; + u32 format; +} __packed; + +#define USM_STREAM_CMD_OPEN_WRITE 0x00011271 +struct usm_stream_cmd_open_write { + struct apr_hdr hdr; + u32 format; +} __packed; + + +#define USM_STREAM_CMD_CLOSE 0x0001230A + +#define USM_STREAM_CMD_SET_PARAM 0x00012731 +struct usm_stream_cmd_set_param { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 module_id; + u32 param_id; +} __packed; + +#define USM_STREAM_CMD_GET_PARAM 0x00012732 +struct usm_stream_cmd_get_param { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 module_id; + u32 param_id; +} __packed; + +/* Encoder configuration definitions */ +#define USM_STREAM_CMD_SET_ENC_PARAM 0x0001230B +/* Decoder configuration definitions */ +#define USM_DATA_CMD_MEDIA_FORMAT_UPDATE 0x00011272 + +/* Encoder/decoder configuration block */ +#define USM_PARAM_ID_ENCDEC_ENC_CFG_BLK 0x0001230D + +/* Max number of static located ports (bytes) */ +#define USM_MAX_PORT_NUMBER 8 + +/* Max number of static located transparent data (bytes) */ +#define USM_MAX_CFG_DATA_SIZE 100 + +/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */ +/* common declarations */ +struct usm_cfg_common { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u32 dev_id; + u8 data_map[USM_MAX_PORT_NUMBER]; +} __packed; + +struct us_encdec_cfg { + u32 format_id; + struct usm_cfg_common cfg_common; + u16 params_size; + u8 *params; +} __packed; + +/* Start/stop US signal detection */ +#define USM_SESSION_CMD_SIGNAL_DETECT_MODE 0x00012719 + +struct usm_session_cmd_detect_info { + struct apr_hdr hdr; + u32 detect_mode; + u32 skip_interval; + u32 algorithm_cfg_size; +} __packed; + +/* US signal detection result */ +#define USM_SESSION_EVENT_SIGNAL_DETECT_RESULT 0x00012720 + +/* ======================================================================= */ +/* Session Level commands */ +#define USM_CMD_SHARED_MEM_MAP_REGION 0x00012728 +struct usm_cmd_memory_map_region { + struct apr_hdr hdr; + u16 mempool_id; + u16 num_regions; + u32 flags; + u32 shm_addr_lsw; + u32 shm_addr_msw; + u32 mem_size_bytes; +} __packed; + +#define USM_CMDRSP_SHARED_MEM_MAP_REGION 0x00012729 +struct usm_cmdrsp_memory_map_region { + u32 mem_map_handle; +} __packed; + +#define USM_CMD_SHARED_MEM_UNMAP_REGION 0x0001272A +struct usm_cmd_memory_unmap_region { + struct apr_hdr hdr; + u32 mem_map_handle; +} __packed; + +#define USM_DATA_CMD_READ 0x00012724 +struct usm_stream_cmd_read { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; + u32 counter; +} __packed; + +#define USM_DATA_EVENT_READ_DONE 0x00012725 + +#define USM_DATA_CMD_WRITE 0x00012726 +struct usm_stream_cmd_write { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; + u32 res0; + u32 res1; + u32 res2; +} __packed; + +#define USM_DATA_EVENT_WRITE_DONE 0x00012727 + +struct usm_stream_media_format_update { + struct apr_hdr hdr; + u32 format_id; + /* = sizeof(usm_cfg_common)+|transp_data| */ + u32 cfg_size; + struct usm_cfg_common cfg_common; + /* Transparent configuration data for specific encoder */ + u8 transp_data[USM_MAX_CFG_DATA_SIZE]; +} __packed; + +struct usm_encode_cfg_blk { + u32 frames_per_buf; + u32 format_id; + /* = sizeof(usm_cfg_common)+|transp_data| */ + u32 cfg_size; + struct usm_cfg_common cfg_common; + /* Transparent configuration data for specific encoder */ + u8 transp_data[USM_MAX_CFG_DATA_SIZE]; +} __packed; + +struct usm_stream_cmd_encdec_cfg_blk { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct usm_encode_cfg_blk enc_blk; +} __packed; + +#endif /* __APR_US_H__ */ diff --git a/include/linux/qdsp6v2/audio_notifier.h b/include/linux/qdsp6v2/audio_notifier.h new file mode 100644 index 000000000000..3587b49a05c6 --- /dev/null +++ b/include/linux/qdsp6v2/audio_notifier.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __AUDIO_NOTIFIER_H_ +#define __AUDIO_NOTIFIER_H_ + +/* State of the notifier domain */ +enum { + AUDIO_NOTIFIER_SERVICE_DOWN, + AUDIO_NOTIFIER_SERVICE_UP +}; + +/* Service order determines connection priority + * Highest number connected first + */ +enum { + AUDIO_NOTIFIER_SSR_SERVICE, + AUDIO_NOTIFIER_PDR_SERVICE, + AUDIO_NOTIFIER_MAX_SERVICES +}; + +enum { + AUDIO_NOTIFIER_ADSP_DOMAIN, + AUDIO_NOTIFIER_MODEM_DOMAIN, + AUDIO_NOTIFIER_MAX_DOMAINS +}; + +/* Structure populated in void *data of nb function + * callback used for audio_notifier_register + */ +struct audio_notifier_cb_data { + int service; + int domain; +}; + +#ifdef CONFIG_MSM_QDSP6_NOTIFIER + +/* + * Use audio_notifier_register to register any audio + * clients who need to be notified of a remote process. + * This API will determine and register the client with + * the best available subsystem (SSR or PDR) for that + * domain (Adsp or Modem). When an event is sent from that + * domain the notifier block callback function will be called. + * + * client_name - A unique user name defined by the client. + * If the same name is used for multiple calls each will + * be tracked & called back separately and a single call + * to deregister will delete them all. + * domain - Domain the client wants to get events from. + * AUDIO_NOTIFIER_ADSP_DOMAIN + * AUDIO_NOTIFIER_MODEM_DOMAIN + * *nb - Pointer to a notifier block. Provide a callback function + * to be notified of an even on that domain. + * + * nb_func(struct notifier_block *this, unsigned long opcode, void *data) + * this - pointer to own nb + * opcode - event from registered domain + * AUDIO_NOTIFIER_SERVICE_DOWN + * AUDIO_NOTIFIER_SERVICE_UP + * *data - pointer to struct audio_notifier_cb_data + * + * Returns: Success: 0 + * Error: -# + */ +int audio_notifier_register(char *client_name, int domain, + struct notifier_block *nb); + +/* + * Use audio_notifier_deregister to deregister the clients from + * all domains registered using audio_notifier_register that + * match the client name. + * + * client_name - Unique user name used in audio_notifier_register. + * Returns: Success: 0 + * Error: -# + */ +int audio_notifier_deregister(char *client_name); + +#else + +static inline int audio_notifier_register(char *client_name, int domain, + struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline int audio_notifier_deregister(char *client_name) +{ + return 0; +} + +#endif /* CONFIG_MSM_QDSP6_PDR */ + +#endif diff --git a/include/linux/qdsp6v2/audio_pdr.h b/include/linux/qdsp6v2/audio_pdr.h new file mode 100644 index 000000000000..ebfd366b1e44 --- /dev/null +++ b/include/linux/qdsp6v2/audio_pdr.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __AUDIO_PDR_H_ +#define __AUDIO_PDR_H_ + +enum { + AUDIO_PDR_DOMAIN_ADSP, + AUDIO_PDR_DOMAIN_MAX +}; + +enum { + AUDIO_PDR_FRAMEWORK_DOWN, + AUDIO_PDR_FRAMEWORK_UP +}; + +#ifdef CONFIG_MSM_QDSP6_PDR + +/* + * Use audio_pdr_register to register with the PDR subsystem this + * should be done before module late init otherwise notification + * of the AUDIO_PDR_FRAMEWORK_UP cannot be guaranteed. + * + * *nb - Pointer to a notifier block. Provide a callback function + * to be notified once the PDR framework has been initialized. + * Callback will receive either the AUDIO_PDR_FRAMEWORK_DOWN + * or AUDIO_PDR_FRAMEWORK_UP ioctl depending on the state of + * the PDR framework. + * + * Returns: Success: 0 + * Failure: Error code + */ +int audio_pdr_register(struct notifier_block *nb); + +/* + * Use audio_pdr_service_register to register with a PDR service + * Function should be called after nb callback registered with + * audio_pdr_register has been called back with the + * AUDIO_PDR_FRAMEWORK_UP ioctl. + * + * domain_id - Domain to use, example: AUDIO_PDR_ADSP + * *nb - Pointer to a notifier block. Provide a callback function + * that will be notified of the state of the domain + * requested. The ioctls received by the callback are + * defined in service-notifier.h. + * + * Returns: Success: Client handle + * Failure: Pointer error code + */ +void *audio_pdr_service_register(int domain_id, + struct notifier_block *nb, int *curr_state); + +/* + * Use audio_pdr_service_deregister to deregister with a PDR + * service that was registered using the audio_pdr_service_register + * API. + * + * *service_handle - Service handle returned by audio_pdr_service_register + * *nb - Pointer to the notifier block. Used in the call to + * audio_pdr_service_register. + * + * Returns: Success: Client handle + * Failure: Error code + */ +int audio_pdr_service_deregister(void *service_handle, + struct notifier_block *nb); + +#else + +static inline int audio_pdr_register(struct notifier_block *nb) +{ + return -ENODEV; +} + + +static inline void *audio_pdr_service_register(int domain_id, + struct notifier_block *nb, + int *curr_state) +{ + return NULL; +} + +static inline int audio_pdr_service_deregister(void *service_handle, + struct notifier_block *nb) +{ + return 0; +} + +#endif /* CONFIG_MSM_QDSP6_PDR */ + +#endif diff --git a/include/linux/qdsp6v2/audio_ssr.h b/include/linux/qdsp6v2/audio_ssr.h new file mode 100644 index 000000000000..a807021ba7ca --- /dev/null +++ b/include/linux/qdsp6v2/audio_ssr.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __AUDIO_SSR_H_ +#define __AUDIO_SSR_H_ + +enum { + AUDIO_SSR_DOMAIN_ADSP, + AUDIO_SSR_DOMAIN_MODEM, + AUDIO_SSR_DOMAIN_MAX +}; + +#ifdef CONFIG_MSM_QDSP6_SSR + +/* + * Use audio_ssr_register to register with the SSR subsystem + * + * domain_id - Service to use, example: AUDIO_SSR_DOMAIN_ADSP + * *nb - Pointer to a notifier block. Provide a callback function + * to be notified of an event for that service. The ioctls + * used by the callback are defined in subsystem_notif.h. + * + * Returns: Success: Client handle + * Failure: Pointer error code + */ +void *audio_ssr_register(int domain_id, struct notifier_block *nb); + +/* + * Use audio_ssr_deregister to register with the SSR subsystem + * + * handle - Handle received from audio_ssr_register + * *nb - Pointer to a notifier block. Callback function + * Used from audio_ssr_register. + * + * Returns: Success: 0 + * Failure: Error code + */ +int audio_ssr_deregister(void *handle, struct notifier_block *nb); + + +/* + * Use audio_ssr_send_nmi to force a RAM dump on ADSP + * down event. + * + * *ssr_cb_data - *data received from notifier callback + */ +void audio_ssr_send_nmi(void *ssr_cb_data); + +#else + +static inline void *audio_ssr_register(int domain_id, + struct notifier_block *nb) +{ + return NULL; +} + +static inline int audio_ssr_deregister(void *handle, struct notifier_block *nb) +{ + return 0; +} + +static inline void audio_ssr_send_nmi(void *ssr_cb_data) +{ +} + +#endif /* CONFIG_MSM_QDSP6_SSR */ + +#endif diff --git a/include/linux/qdsp6v2/dsp_debug.h b/include/linux/qdsp6v2/dsp_debug.h new file mode 100644 index 000000000000..bc1cd9ec8743 --- /dev/null +++ b/include/linux/qdsp6v2/dsp_debug.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2010, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __DSP_DEBUG_H_ +#define __DSP_DEBUG_H_ + +typedef int (*dsp_state_cb)(int state); +int dsp_debug_register(dsp_state_cb ptr); + +#define DSP_STATE_CRASHED 0x0 +#define DSP_STATE_CRASH_DUMP_DONE 0x1 + +#endif diff --git a/include/linux/qdsp6v2/rtac.h b/include/linux/qdsp6v2/rtac.h new file mode 100644 index 000000000000..3e5433b23a51 --- /dev/null +++ b/include/linux/qdsp6v2/rtac.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2011, 2013-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __RTAC_H__ +#define __RTAC_H__ + +#include + +/* Voice Modes */ +#define RTAC_CVP 0 +#define RTAC_CVS 1 +#define RTAC_VOICE_MODES 2 + +#define RTAC_MAX_ACTIVE_DEVICES 4 +#define RTAC_MAX_ACTIVE_POPP 8 + +#define DEFAULT_APP_TYPE 0x00011130 + +enum { + ADM_RTAC_CAL, + ASM_RTAC_CAL, + VOICE_RTAC_CAL, + AFE_RTAC_CAL, + MAX_RTAC_BLOCKS +}; + +struct rtac_cal_mem_map_data { + uint32_t map_size; + uint32_t map_handle; + struct ion_client *ion_client; + struct ion_handle *ion_handle; +}; + +struct rtac_cal_data { + size_t size; + void *kvaddr; + phys_addr_t paddr; +}; + +struct rtac_cal_block_data { + struct rtac_cal_mem_map_data map_data; + struct rtac_cal_data cal_data; +}; + +struct rtac_popp_data { + uint32_t popp; + uint32_t popp_topology; + uint32_t app_type; +}; + +struct rtac_adm_data { + uint32_t topology_id; + uint32_t afe_topology; + uint32_t afe_port; + uint32_t copp; + uint32_t num_of_popp; + uint32_t app_type; + uint32_t acdb_dev_id; + struct rtac_popp_data popp[RTAC_MAX_ACTIVE_POPP]; +}; + +struct rtac_adm { + uint32_t num_of_dev; + struct rtac_adm_data device[RTAC_MAX_ACTIVE_DEVICES]; +}; + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id, + u32 app_type, u32 acdb_dev_id); +void rtac_remove_adm_device(u32 port_id, u32 copp_id); +void rtac_remove_popp_from_adm_devices(u32 popp_id); +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id, u32 session_id); +void rtac_remove_voice(u32 cvs_handle); +void rtac_set_adm_handle(void *handle); +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size); +void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_asm_handle(u32 session_id, void *handle); +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size); +void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_voice_handle(u32 mode, void *handle); +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size); +void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size); +int rtac_clear_mapping(uint32_t cal_type); +bool rtac_make_afe_callback(uint32_t *payload, u32 payload_size); +void rtac_set_afe_handle(void *handle); +void get_rtac_adm_data(struct rtac_adm *adm_data); +#endif diff --git a/include/linux/qdsp6v2/usf.h b/include/linux/qdsp6v2/usf.h new file mode 100644 index 000000000000..544b624c2cda --- /dev/null +++ b/include/linux/qdsp6v2/usf.h @@ -0,0 +1,298 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __USF_H__ +#define __USF_H__ + +#include +#include + +#define USF_IOCTL_MAGIC 'U' + +#define US_SET_TX_INFO _IOW(USF_IOCTL_MAGIC, 0, \ + struct us_tx_info_type) +#define US_START_TX _IO(USF_IOCTL_MAGIC, 1) +#define US_GET_TX_UPDATE _IOWR(USF_IOCTL_MAGIC, 2, \ + struct us_tx_update_info_type) +#define US_SET_RX_INFO _IOW(USF_IOCTL_MAGIC, 3, \ + struct us_rx_info_type) +#define US_SET_RX_UPDATE _IOWR(USF_IOCTL_MAGIC, 4, \ + struct us_rx_update_info_type) +#define US_START_RX _IO(USF_IOCTL_MAGIC, 5) + +#define US_STOP_TX _IO(USF_IOCTL_MAGIC, 6) +#define US_STOP_RX _IO(USF_IOCTL_MAGIC, 7) + +#define US_SET_DETECTION _IOWR(USF_IOCTL_MAGIC, 8, \ + struct us_detect_info_type) + +#define US_GET_VERSION _IOWR(USF_IOCTL_MAGIC, 9, \ + struct us_version_info_type) + +#define US_SET_TX_STREAM_PARAM _IOW(USF_IOCTL_MAGIC, 10, \ + struct us_stream_param_type) +#define US_GET_TX_STREAM_PARAM _IOWR(USF_IOCTL_MAGIC, 11, \ + struct us_stream_param_type) +#define US_SET_RX_STREAM_PARAM _IOW(USF_IOCTL_MAGIC, 12, \ + struct us_stream_param_type) +#define US_GET_RX_STREAM_PARAM _IOWR(USF_IOCTL_MAGIC, 13, \ + struct us_stream_param_type) + +/* Special timeout values */ +#define USF_NO_WAIT_TIMEOUT 0x00000000 +/* Infinitive */ +#define USF_INFINITIVE_TIMEOUT 0xffffffff +/* Default value, used by the driver */ +#define USF_DEFAULT_TIMEOUT 0xfffffffe + +/* US detection place (HW|FW) */ +enum us_detect_place_enum { +/* US is detected in HW */ + US_DETECT_HW, +/* US is detected in FW */ + US_DETECT_FW +}; + +/* US detection mode */ +enum us_detect_mode_enum { +/* US detection is disabled */ + US_DETECT_DISABLED_MODE, +/* US detection is enabled in continue mode */ + US_DETECT_CONTINUE_MODE, +/* US detection is enabled in one shot mode */ + US_DETECT_SHOT_MODE +}; + +/* Encoder (TX), decoder (RX) supported US data formats */ +#define USF_POINT_EPOS_FORMAT 0 +#define USF_RAW_FORMAT 1 + +/* Indexes of event types, produced by the calculators */ +#define USF_TSC_EVENT_IND 0 +#define USF_TSC_PTR_EVENT_IND 1 +#define USF_MOUSE_EVENT_IND 2 +#define USF_KEYBOARD_EVENT_IND 3 +#define USF_TSC_EXT_EVENT_IND 4 +#define USF_MAX_EVENT_IND 5 + +/* Types of events, produced by the calculators */ +#define USF_NO_EVENT 0 +#define USF_TSC_EVENT (1 << USF_TSC_EVENT_IND) +#define USF_TSC_PTR_EVENT (1 << USF_TSC_PTR_EVENT_IND) +#define USF_MOUSE_EVENT (1 << USF_MOUSE_EVENT_IND) +#define USF_KEYBOARD_EVENT (1 << USF_KEYBOARD_EVENT_IND) +#define USF_TSC_EXT_EVENT (1 << USF_TSC_EXT_EVENT_IND) +#define USF_ALL_EVENTS (USF_TSC_EVENT |\ + USF_TSC_PTR_EVENT |\ + USF_MOUSE_EVENT |\ + USF_KEYBOARD_EVENT |\ + USF_TSC_EXT_EVENT) + +/* min, max array dimension */ +#define MIN_MAX_DIM 2 + +/* coordinates (x,y,z) array dimension */ +#define COORDINATES_DIM 3 + +/* tilts (x,y) array dimension */ +#define TILTS_DIM 2 + +/* Max size of the client name */ +#define USF_MAX_CLIENT_NAME_SIZE 20 + +/* Max number of the ports (mics/speakers) */ +#define USF_MAX_PORT_NUM 8 + +/* Info structure common for TX and RX */ +struct us_xx_info_type { +/* Input: general info */ +/* Name of the client - event calculator */ + const char __user *client_name; +/* Selected device identification, accepted in the kernel's CAD */ + uint32_t dev_id; +/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */ + uint32_t stream_format; +/* Required sample rate in Hz */ + uint32_t sample_rate; +/* Size of a buffer (bytes) for US data transfer between the module and USF */ + uint32_t buf_size; +/* Number of the buffers for the US data transfer */ + uint16_t buf_num; +/* Number of the microphones (TX) or speakers(RX) */ + uint16_t port_cnt; +/* Microphones(TX) or speakers(RX) indexes in their enumeration */ + uint8_t port_id[USF_MAX_PORT_NUM]; +/* Bits per sample 16 or 32 */ + uint16_t bits_per_sample; +/* Input: Transparent info for encoder in the LPASS */ +/* Parameters data size in bytes */ + uint16_t params_data_size; +/* Pointer to the parameters */ + uint8_t __user *params_data; +/* Max size of buffer for get and set parameter */ + uint32_t max_get_set_param_buf_size; +}; + +struct us_input_info_type { + /* Touch screen dimensions: min & max;for input module */ + int tsc_x_dim[MIN_MAX_DIM]; + int tsc_y_dim[MIN_MAX_DIM]; + int tsc_z_dim[MIN_MAX_DIM]; + /* Touch screen tilt dimensions: min & max;for input module */ + int tsc_x_tilt[MIN_MAX_DIM]; + int tsc_y_tilt[MIN_MAX_DIM]; + /* Touch screen pressure limits: min & max; for input module */ + int tsc_pressure[MIN_MAX_DIM]; + /* The requested buttons bitmap */ + uint16_t req_buttons_bitmap; + /* Bitmap of types of events (USF_X_EVENT), produced by calculator */ + uint16_t event_types; + /* Bitmap of types of events from devs, conflicting with USF */ + uint16_t conflicting_event_types; +}; + +struct us_tx_info_type { + /* Common info */ + struct us_xx_info_type us_xx_info; + /* Info specific for TX*/ + struct us_input_info_type input_info; +}; + +struct us_rx_info_type { + /* Common info */ + struct us_xx_info_type us_xx_info; + /* Info specific for RX*/ +}; + +struct point_event_type { +/* Pen coordinates (x, y, z) in units, defined by */ + int coordinates[COORDINATES_DIM]; + /* {x;y} in transparent units */ + int inclinations[TILTS_DIM]; +/* [0-1023] (10bits); 0 - pen up */ + uint32_t pressure; +/* Bitmap for button state. 1 - down, 0 - up */ + uint16_t buttons_state_bitmap; +}; + +/* Mouse buttons, supported by USF */ +#define USF_BUTTON_LEFT_MASK 1 +#define USF_BUTTON_MIDDLE_MASK 2 +#define USF_BUTTON_RIGHT_MASK 4 +struct mouse_event_type { +/* The mouse relative movement (dX, dY, dZ) */ + int rels[COORDINATES_DIM]; +/* Bitmap of mouse buttons states: 1 - down, 0 - up; */ + uint16_t buttons_states; +}; + +struct key_event_type { +/* Calculated MS key- see input.h. */ + uint32_t key; +/* Keyboard's key state: 1 - down, 0 - up; */ + uint8_t key_state; +}; + +struct usf_event_type { +/* Event sequence number */ + uint32_t seq_num; +/* Event generation system time */ + uint32_t timestamp; +/* Destination input event type index (e.g. touch screen, mouse, key) */ + uint16_t event_type_ind; + union { + struct point_event_type point_event; + struct mouse_event_type mouse_event; + struct key_event_type key_event; + } event_data; +}; + +struct us_tx_update_info_type { +/* Input general: */ +/* Number of calculated events */ + uint16_t event_counter; +/* Calculated events or NULL */ + struct usf_event_type __user *event; +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +/* Time (sec) to wait for data or special values: */ +/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */ + uint32_t timeout; +/* Events (from conflicting devs) to be disabled/enabled */ + uint16_t event_filters; + +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* Pointer to the parameters */ + uint8_t __user *params_data; +/* Output parameters: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +}; + +struct us_rx_update_info_type { +/* Input general: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* pPointer to the parameters */ + uint8_t __user *params_data; +/* Output parameters: */ +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +}; + +struct us_detect_info_type { +/* US detection place (HW|FW) */ +/* NA in the Active and OFF states */ + enum us_detect_place_enum us_detector; +/* US detection mode */ + enum us_detect_mode_enum us_detect_mode; +/* US data dropped during this time (msec) */ + uint32_t skip_time; +/* Transparent data size */ + uint16_t params_data_size; +/* Pointer to the transparent data */ + uint8_t __user *params_data; +/* Time (sec) to wait for US presence event */ + uint32_t detect_timeout; +/* Out parameter: US presence */ + bool is_us; +}; + +struct us_version_info_type { +/* Size of memory for the version string */ + uint16_t buf_size; +/* Pointer to the memory for the version string */ + char __user *pbuf; +}; + +struct us_stream_param_type { +/* Id of module */ + uint32_t module_id; +/* Id of parameter */ + uint32_t param_id; +/* Size of memory of the parameter buffer */ + uint32_t buf_size; +/* Pointer to the memory of the parameter buffer */ + uint8_t __user *pbuf; +}; + +#endif /* __USF_H__ */ diff --git a/include/soc/qcom/liquid_dock.h b/include/soc/qcom/liquid_dock.h new file mode 100644 index 000000000000..3668224f1938 --- /dev/null +++ b/include/soc/qcom/liquid_dock.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#if IS_ENABLED(CONFIG_QCOM_LIQUID_DOCK) +void register_liquid_dock_notify(struct notifier_block *nb); +void unregister_liquid_dock_notify(struct notifier_block *nb); +#else +static inline void register_liquid_dock_notify(struct notifier_block *nb) { } +static inline void unregister_liquid_dock_notify(struct notifier_block *nb) { } +#endif diff --git a/include/sound/adsp_err.h b/include/sound/adsp_err.h new file mode 100644 index 000000000000..43be91d6ba8f --- /dev/null +++ b/include/sound/adsp_err.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ADSP_ERR__ +#define __ADSP_ERR__ + +int adsp_err_get_lnx_err_code(u32 adsp_error); + +char *adsp_err_get_err_str(u32 adsp_error); + +#endif diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h new file mode 100644 index 000000000000..812ea65e736d --- /dev/null +++ b/include/sound/apr_audio-v2.h @@ -0,0 +1,9901 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#ifndef _APR_AUDIO_V2_H_ +#define _APR_AUDIO_V2_H_ + +#include + +/* size of header needed for passing data out of band */ +#define APR_CMD_OB_HDR_SZ 12 + +/* size of header needed for getting data */ +#define APR_CMD_GET_HDR_SZ 16 + +struct param_outband { + size_t size; + void *kvaddr; + phys_addr_t paddr; +}; + +#define ADSP_ADM_VERSION 0x00070000 + +#define ADM_CMD_SHARED_MEM_MAP_REGIONS 0x00010322 +#define ADM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010323 +#define ADM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010324 + +#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325 +#define ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5 0x0001033D +/* Enumeration for an audio Rx matrix ID.*/ +#define ADM_MATRIX_ID_AUDIO_RX 0 + +#define ADM_MATRIX_ID_AUDIO_TX 1 + +#define ADM_MATRIX_ID_COMPRESSED_AUDIO_RX 2 +/* Enumeration for an audio Tx matrix ID.*/ +#define ADM_MATRIX_ID_AUDIOX 1 + +#define ADM_MAX_COPPS 5 + +/* make sure this matches with msm_audio_calibration */ +#define SP_V2_NUM_MAX_SPKR 2 + +/* Session map node structure. + * Immediately following this structure are num_copps + * entries of COPP IDs. The COPP IDs are 16 bits, so + * there might be a padding 16-bit field if num_copps + * is odd. + */ +struct adm_session_map_node_v5 { + u16 session_id; + /* Handle of the ASM session to be routed. Supported values: 1 + * to 8. + */ + + + u16 num_copps; + /* Number of COPPs to which this session is to be routed. + * Supported values: 0 < num_copps <= ADM_MAX_COPPS. + */ +} __packed; + +/* Payload of the #ADM_CMD_MATRIX_MAP_ROUTINGS_V5 command. + * Immediately following this structure are num_sessions of the session map + * node payload (adm_session_map_node_v5). + */ + +struct adm_cmd_matrix_map_routings_v5 { + struct apr_hdr hdr; + + u32 matrix_id; + /* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx + * (1). Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + u32 num_sessions; + /* Number of sessions being updated by this command (optional). */ +} __packed; + +/* This command allows a client to open a COPP/Voice Proc. TX module + * and sets up the device session: Matrix -> COPP -> AFE on the RX + * and AFE -> COPP -> Matrix on the TX. This enables PCM data to + * be transferred to/from the endpoint (AFEPortID). + * + * @return + * #ADM_CMDRSP_DEVICE_OPEN_V5 with the resulting status and COPP ID. + */ +#define ADM_CMD_DEVICE_OPEN_V5 0x00010326 + +/* Definition for a low latency stream session. */ +#define ADM_LOW_LATENCY_DEVICE_SESSION 0x2000 + +/* Definition for a ultra low latency stream session. */ +#define ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION 0x4000 + +/* Definition for a ultra low latency with Post Processing stream session. */ +#define ADM_ULL_POST_PROCESSING_DEVICE_SESSION 0x8000 + +/* Definition for a legacy device session. */ +#define ADM_LEGACY_DEVICE_SESSION 0 + +/* Indicates that endpoint_id_2 is to be ignored.*/ +#define ADM_CMD_COPP_OPEN_END_POINT_ID_2_IGNORE 0xFFFF + +#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_RX_PATH_COPP 1 + +#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATIONX_PATH_LIVE_COPP 2 + +#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATIONX_PATH_NON_LIVE_COPP 3 + +/* Indicates that an audio COPP is to send/receive a mono PCM + * stream to/from + * END_POINT_ID_1. + */ +#define ADM_CMD_COPP_OPEN_CHANNEL_CONFIG_MONO 1 + +/* Indicates that an audio COPP is to send/receive a + * stereo PCM stream to/from END_POINT_ID_1. + */ +#define ADM_CMD_COPP_OPEN_CHANNEL_CONFIG_STEREO 2 + +/* Sample rate is 8000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_8K 8000 + +/* Sample rate is 16000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_16K 16000 + +/* Sample rate is 48000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_48K 48000 + +/* Definition for a COPP live input flag bitmask.*/ +#define ADM_BIT_MASK_COPP_LIVE_INPUT_FLAG (0x0001U) + +/* Definition for a COPP live shift value bitmask.*/ +#define ADM_SHIFT_COPP_LIVE_INPUT_FLAG 0 + +/* Definition for the COPP ID bitmask.*/ +#define ADM_BIT_MASK_COPP_ID (0x0000FFFFUL) + +/* Definition for the COPP ID shift value.*/ +#define ADM_SHIFT_COPP_ID 0 + +/* Definition for the service ID bitmask.*/ +#define ADM_BIT_MASK_SERVICE_ID (0x00FF0000UL) + +/* Definition for the service ID shift value.*/ +#define ADM_SHIFT_SERVICE_ID 16 + +/* Definition for the domain ID bitmask.*/ +#define ADM_BIT_MASK_DOMAIN_ID (0xFF000000UL) + +/* Definition for the domain ID shift value.*/ +#define ADM_SHIFT_DOMAIN_ID 24 + +/* ADM device open command payload of the + * #ADM_CMD_DEVICE_OPEN_V5 command. + */ +struct adm_cmd_device_open_v5 { + struct apr_hdr hdr; + u16 flags; +/* Reserved for future use. Clients must set this field + * to zero. + */ + + u16 mode_of_operation; +/* Specifies whether the COPP must be opened on the Tx or Rx + * path. Use the ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_* macros for + * supported values and interpretation. + * Supported values: + * - 0x1 -- Rx path COPP + * - 0x2 -- Tx path live COPP + * - 0x3 -- Tx path nonlive COPP + * Live connections cause sample discarding in the Tx device + * matrix if the destination output ports do not pull them + * fast enough. Nonlive connections queue the samples + * indefinitely. + */ + + u16 endpoint_id_1; +/* Logical and physical endpoint ID of the audio path. + * If the ID is a voice processor Tx block, it receives near + * samples. Supported values: Any pseudoport, AFE Rx port, + * or AFE Tx port For a list of valid IDs, refer to + * @xhyperref{Q4,[Q4]}. + * Q4 = Hexagon Multimedia: AFE Interface Specification + */ + + u16 endpoint_id_2; +/* Logical and physical endpoint ID 2 for a voice processor + * Tx block. + * This is not applicable to audio COPP. + * Supported values: + * - AFE Rx port + * - 0xFFFF -- Endpoint 2 is unavailable and the voice + * processor Tx + * block ignores this endpoint + * When the voice processor Tx block is created on the audio + * record path, + * it can receive far-end samples from an AFE Rx port if the + * voice call + * is active. The ID of the AFE port is provided in this + * field. + * For a list of valid IDs, refer @xhyperref{Q4,[Q4]}. + */ + + u32 topology_id; +/* Audio COPP topology ID; 32-bit GUID. */ + + u16 dev_num_channel; +/* Number of channels the audio COPP sends to/receives from + * the endpoint. + * Supported values: 1 to 8. + * The value is ignored for the voice processor Tx block, + * where channel + * configuration is derived from the topology ID. + */ + + u16 bit_width; +/* Bit width (in bits) that the audio COPP sends to/receives + * from the + * endpoint. The value is ignored for the voice processing + * Tx block, + * where the PCM width is 16 bits. + */ + + u32 sample_rate; +/* Sampling rate at which the audio COPP/voice processor + * Tx block + * interfaces with the endpoint. + * Supported values for voice processor Tx: 8000, 16000, + * 48000 Hz + * Supported values for audio COPP: >0 and <=192 kHz + */ + + u8 dev_channel_mapping[8]; +/* Array of channel mapping of buffers that the audio COPP + * sends to the endpoint. Channel[i] mapping describes channel + * I inside the buffer, where 0 < i < dev_num_channel. + * This value is relevant only for an audio Rx COPP. + * For the voice processor block and Tx audio block, this field + * is set to zero and is ignored. + */ +} __packed; + +/* + * This command allows the client to close a COPP and disconnect + * the device session. + */ +#define ADM_CMD_DEVICE_CLOSE_V5 0x00010327 + +/* Sets one or more parameters to a COPP. */ +#define ADM_CMD_SET_PP_PARAMS_V5 0x00010328 + +/* Payload of the #ADM_CMD_SET_PP_PARAMS_V5 command. + * If the data_payload_addr_lsw and data_payload_addr_msw element + * are NULL, a series of adm_param_datastructures immediately + * follows, whose total size is data_payload_size bytes. + */ +struct adm_cmd_set_pp_params_v5 { + struct apr_hdr hdr; + u32 payload_addr_lsw; +/* LSW of parameter data payload address. */ + u32 payload_addr_msw; +/* MSW of parameter data payload address. */ + + u32 mem_map_handle; +/* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS + * command + * + * If mem_map_handle is zero implies the message is in + * the payload + */ + + u32 payload_size; +/* Size in bytes of the variable payload accompanying this + * message or + * in shared memory. This is used for parsing the parameter + * payload. + */ +} __packed; + +/* Payload format for COPP parameter data. + * Immediately following this structure are param_size bytes + * of parameter + * data. + */ +struct adm_param_data_v5 { + u32 module_id; + /* Unique ID of the module. */ + u32 param_id; + /* Unique ID of the parameter. */ + u16 param_size; + /* Data size of the param_id/module_id combination. + * This value is a + * multiple of 4 bytes. + */ + u16 reserved; + /* Reserved for future enhancements. + * This field must be set to zero. + */ +} __packed; + +/* set customized mixing on matrix mixer */ +#define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5 0x00010344 +struct adm_cmd_set_pspd_mtmx_strtr_params_v5 { + struct apr_hdr hdr; + /* LSW of parameter data payload address.*/ + u32 payload_addr_lsw; + /* MSW of parameter data payload address.*/ + u32 payload_addr_msw; + /* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */ + /* command. If mem_map_handle is zero implies the message is in */ + /* the payload */ + u32 mem_map_handle; + /* Size in bytes of the variable payload accompanying this */ + /* message or in shared memory. This is used for parsing the */ + /* parameter payload. */ + u32 payload_size; + u16 direction; + u16 sessionid; + u16 deviceid; + u16 reserved; +} __packed; + +/* Defined specifically for in-band use, includes params */ +struct adm_cmd_set_pp_params_inband_v5 { + struct apr_hdr hdr; + /* LSW of parameter data payload address.*/ + u32 payload_addr_lsw; + /* MSW of parameter data payload address.*/ + u32 payload_addr_msw; + /* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */ + /* command. If mem_map_handle is zero implies the message is in */ + /* the payload */ + u32 mem_map_handle; + /* Size in bytes of the variable payload accompanying this */ + /* message or in shared memory. This is used for parsing the */ + /* parameter payload. */ + u32 payload_size; + /* Parameters passed for in band payload */ + struct adm_param_data_v5 params; +} __packed; + +/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command. + */ +#define ADM_CMDRSP_DEVICE_OPEN_V5 0x00010329 + +/* Payload of the #ADM_CMDRSP_DEVICE_OPEN_V5 message, + * which returns the + * status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command. + */ +struct adm_cmd_rsp_device_open_v5 { + u32 status; + /* Status message (error code).*/ + + u16 copp_id; + /* COPP ID: Supported values: 0 <= copp_id < ADM_MAX_COPPS*/ + + u16 reserved; + /* Reserved. This field must be set to zero.*/ +} __packed; + +/* This command allows a query of one COPP parameter. */ +#define ADM_CMD_GET_PP_PARAMS_V5 0x0001032A + +/* Payload an #ADM_CMD_GET_PP_PARAMS_V5 command. */ +struct adm_cmd_get_pp_params_v5 { + struct apr_hdr hdr; + u32 data_payload_addr_lsw; + /* LSW of parameter data payload address.*/ + + u32 data_payload_addr_msw; + /* MSW of parameter data payload address.*/ + + /* If the mem_map_handle is non zero, + * on ACK, the ParamData payloads begin at + * the address specified (out-of-band). + */ + + u32 mem_map_handle; + /* Memory map handle returned + * by ADM_CMD_SHARED_MEM_MAP_REGIONS command. + * If the mem_map_handle is 0, it implies that + * the ACK's payload will contain the ParamData (in-band). + */ + + u32 module_id; + /* Unique ID of the module. */ + + u32 param_id; + /* Unique ID of the parameter. */ + + u16 param_max_size; + /* Maximum data size of the parameter + *ID/module ID combination. This + * field is a multiple of 4 bytes. + */ + u16 reserved; + /* Reserved for future enhancements. + * This field must be set to zero. + */ +} __packed; + +/* Returns parameter values + * in response to an #ADM_CMD_GET_PP_PARAMS_V5 command. + */ +#define ADM_CMDRSP_GET_PP_PARAMS_V5 0x0001032B + +/* Payload of the #ADM_CMDRSP_GET_PP_PARAMS_V5 message, + * which returns parameter values in response + * to an #ADM_CMD_GET_PP_PARAMS_V5 command. + * Immediately following this + * structure is the adm_param_data_v5 + * structure containing the pre/postprocessing + * parameter data. For an in-band + * scenario, the variable payload depends + * on the size of the parameter. + */ +struct adm_cmd_rsp_get_pp_params_v5 { + u32 status; + /* Status message (error code).*/ +} __packed; + +/* Structure for holding soft stepping volume parameters. */ + +/* + * Payload of the #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS + * parameters used by the Volume Control module. + */ + +struct audproc_softvolume_params { + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +/* + * ID of the Media Format Converter (MFC) module. + * This module supports the following parameter IDs: + * #AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT + * #AUDPROC_CHMIXER_PARAM_ID_COEFF + */ +#define AUDPROC_MODULE_ID_MFC 0x00010912 + +/* ID of the Output Media Format parameters used by AUDPROC_MODULE_ID_MFC. + * + */ +#define AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x00010913 + + +struct audproc_mfc_output_media_fmt { + struct adm_cmd_set_pp_params_v5 params; + struct adm_param_data_v5 data; + uint32_t sampling_rate; + uint16_t bits_per_sample; + uint16_t num_channels; + uint16_t channel_type[8]; +} __packed; + +struct audproc_volume_ctrl_master_gain { + struct adm_cmd_set_pp_params_v5 params; + struct adm_param_data_v5 data; + /* Linear gain in Q13 format. */ + uint16_t master_gain; + /* Clients must set this field to zero. */ + uint16_t reserved; +} __packed; + +struct audproc_soft_step_volume_params { + struct adm_cmd_set_pp_params_v5 params; + struct adm_param_data_v5 data; +/* + * Period in milliseconds. + * Supported values: 0 to 15000 + */ + uint32_t period; +/* + * Step in microseconds. + * Supported values: 0 to 15000000 + */ + uint32_t step; +/* + * Ramping curve type. + * Supported values: + * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_LINEAR + * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_EXP + * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_LOG + */ + uint32_t ramping_curve; +} __packed; + +struct audproc_enable_param_t { + struct adm_cmd_set_pp_params_inband_v5 pp_params; + /* + * Specifies whether the Audio processing module is enabled. + * This parameter is generic/common parameter to configure or + * determine the state of any audio processing module. + + * @values 0 : Disable 1: Enable + */ + uint32_t enable; +}; + +/* + * Allows a client to control the gains on various session-to-COPP paths. + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_V5 0x0001032C + +/* Indicates that the target gain in the + * current adm_session_copp_gain_v5 + * structure is to be applied to all + * the session-to-COPP paths that exist for + * the specified session. + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_COPP_ID_ALL_CONNECTED_COPPS 0xFFFF + +/* Indicates that the target gain is + * to be immediately applied to the + * specified session-to-COPP path, + * without a ramping fashion. + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE 0x0000 + +/* Enumeration for a linear ramping curve.*/ +#define ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR 0x0000 + +/* Payload of the #ADM_CMD_MATRIX_RAMP_GAINS_V5 command. + * Immediately following this structure are num_gains of the + * adm_session_copp_gain_v5structure. + */ +struct adm_cmd_matrix_ramp_gains_v5 { + u32 matrix_id; +/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). + * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + + u16 num_gains; + /* Number of gains being applied. */ + + u16 reserved_for_align; + /* Reserved. This field must be set to zero.*/ +} __packed; + +/* Session-to-COPP path gain structure, used by the + * #ADM_CMD_MATRIX_RAMP_GAINS_V5 command. + * This structure specifies the target + * gain (per channel) that must be applied + * to a particular session-to-COPP path in + * the audio matrix. The structure can + * also be used to apply the gain globally + * to all session-to-COPP paths that + * exist for the given session. + * The aDSP uses device channel mapping to + * determine which channel gains to + * use from this command. For example, + * if the device is configured as stereo, + * the aDSP uses only target_gain_ch_1 and + * target_gain_ch_2, and it ignores + * the others. + */ +struct adm_session_copp_gain_v5 { + u16 session_id; +/* Handle of the ASM session. + * Supported values: 1 to 8. + */ + + u16 copp_id; +/* Handle of the COPP. Gain will be applied on the Session ID + * COPP ID path. + */ + + u16 ramp_duration; +/* Duration (in milliseconds) of the ramp over + * which target gains are + * to be applied. Use + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE + * to indicate that gain must be applied immediately. + */ + + u16 step_duration; +/* Duration (in milliseconds) of each step in the ramp. + * This parameter is ignored if ramp_duration is equal to + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE. + * Supported value: 1 + */ + + u16 ramp_curve; +/* Type of ramping curve. + * Supported value: #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR + */ + + u16 reserved_for_align; + /* Reserved. This field must be set to zero. */ + + u16 target_gain_ch_1; + /* Target linear gain for channel 1 in Q13 format; */ + + u16 target_gain_ch_2; + /* Target linear gain for channel 2 in Q13 format; */ + + u16 target_gain_ch_3; + /* Target linear gain for channel 3 in Q13 format; */ + + u16 target_gain_ch_4; + /* Target linear gain for channel 4 in Q13 format; */ + + u16 target_gain_ch_5; + /* Target linear gain for channel 5 in Q13 format; */ + + u16 target_gain_ch_6; + /* Target linear gain for channel 6 in Q13 format; */ + + u16 target_gain_ch_7; + /* Target linear gain for channel 7 in Q13 format; */ + + u16 target_gain_ch_8; + /* Target linear gain for channel 8 in Q13 format; */ +} __packed; + +/* Allows to set mute/unmute on various session-to-COPP paths. + * For every session-to-COPP path (stream-device interconnection), + * mute/unmute can be set individually on the output channels. + */ +#define ADM_CMD_MATRIX_MUTE_V5 0x0001032D + +/* Indicates that mute/unmute in the + * current adm_session_copp_mute_v5structure + * is to be applied to all the session-to-COPP + * paths that exist for the specified session. + */ +#define ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS 0xFFFF + +/* Payload of the #ADM_CMD_MATRIX_MUTE_V5 command*/ +struct adm_cmd_matrix_mute_v5 { + u32 matrix_id; +/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). + * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + + u16 session_id; +/* Handle of the ASM session. + * Supported values: 1 to 8. + */ + + u16 copp_id; +/* Handle of the COPP. + * Use ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS + * to indicate that mute/unmute must be applied to + * all the COPPs connected to session_id. + * Supported values: + * - 0xFFFF -- Apply mute/unmute to all connected COPPs + * - Other values -- Valid COPP ID + */ + + u8 mute_flag_ch_1; + /* Mute flag for channel 1 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_2; + /* Mute flag for channel 2 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_3; + /* Mute flag for channel 3 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_4; + /* Mute flag for channel 4 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_5; + /* Mute flag for channel 5 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_6; + /* Mute flag for channel 6 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_7; + /* Mute flag for channel 7 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_8; + /* Mute flag for channel 8 is set to unmute (0) or mute (1). */ + + u16 ramp_duration; +/* Period (in milliseconds) over which the soft mute/unmute will be + * applied. + * Supported values: 0 (Default) to 0xFFFF + * The default of 0 means mute/unmute will be applied immediately. + */ + + u16 reserved_for_align; + /* Clients must set this field to zero.*/ +} __packed; + +#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2 (0x00010DD8) + +struct asm_aac_stereo_mix_coeff_selection_param_v2 { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + u32 aac_stereo_mix_coeff_flag; +} __packed; + +/* Allows a client to connect the desired stream to + * the desired AFE port through the stream router + * + * This command allows the client to connect specified session to + * specified AFE port. This is used for compressed streams only + * opened using the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED or + * #ASM_STREAM_CMD_OPEN_READ_COMPRESSED command. + * + * @prerequisites + * Session ID and AFE Port ID must be valid. + * #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED or + * #ASM_STREAM_CMD_OPEN_READ_COMPRESSED + * must have been called on this session. + */ + +#define ADM_CMD_CONNECT_AFE_PORT_V5 0x0001032E +#define ADM_CMD_DISCONNECT_AFE_PORT_V5 0x0001032F +/* Enumeration for the Rx stream router ID.*/ +#define ADM_STRTR_ID_RX 0 +/* Enumeration for the Tx stream router ID.*/ +#define ADM_STRTR_IDX 1 + +/* Payload of the #ADM_CMD_CONNECT_AFE_PORT_V5 command.*/ +struct adm_cmd_connect_afe_port_v5 { + struct apr_hdr hdr; + u8 mode; +/* ID of the stream router (RX/TX). Use the + * ADM_STRTR_ID_RX or ADM_STRTR_IDX macros + * to set this field. + */ + + u8 session_id; + /* Session ID of the stream to connect */ + + u16 afe_port_id; + /* Port ID of the AFE port to connect to.*/ + u32 num_channels; +/* Number of device channels + * Supported values: 2(Audio Sample Packet), + * 8 (HBR Audio Stream Sample Packet) + */ + + u32 sampling_rate; +/* Device sampling rate + * Supported values: Any + */ +} __packed; + + +/* adsp_adm_api.h */ + + +/* Port ID. Update afe_get_port_index + * when a new port is added here. + */ +#define PRIMARY_I2S_RX 0 +#define PRIMARY_I2S_TX 1 +#define SECONDARY_I2S_RX 4 +#define SECONDARY_I2S_TX 5 +#define MI2S_RX 6 +#define MI2S_TX 7 +#define HDMI_RX 8 +#define RSVD_2 9 +#define RSVD_3 10 +#define DIGI_MIC_TX 11 +#define VOICE2_PLAYBACK_TX 0x8002 +#define VOICE_RECORD_RX 0x8003 +#define VOICE_RECORD_TX 0x8004 +#define VOICE_PLAYBACK_TX 0x8005 + +/* Slimbus Multi channel port id pool */ +#define SLIMBUS_0_RX 0x4000 +#define SLIMBUS_0_TX 0x4001 +#define SLIMBUS_1_RX 0x4002 +#define SLIMBUS_1_TX 0x4003 +#define SLIMBUS_2_RX 0x4004 +#define SLIMBUS_2_TX 0x4005 +#define SLIMBUS_3_RX 0x4006 +#define SLIMBUS_3_TX 0x4007 +#define SLIMBUS_4_RX 0x4008 +#define SLIMBUS_4_TX 0x4009 +#define SLIMBUS_5_RX 0x400a +#define SLIMBUS_5_TX 0x400b +#define SLIMBUS_6_RX 0x400c +#define SLIMBUS_6_TX 0x400d +#define SLIMBUS_7_RX 0x400e +#define SLIMBUS_7_TX 0x400f +#define SLIMBUS_8_RX 0x4010 +#define SLIMBUS_8_TX 0x4011 +#define SLIMBUS_PORT_LAST SLIMBUS_8_TX +#define INT_BT_SCO_RX 0x3000 +#define INT_BT_SCO_TX 0x3001 +#define INT_BT_A2DP_RX 0x3002 +#define INT_FM_RX 0x3004 +#define INT_FM_TX 0x3005 +#define RT_PROXY_PORT_001_RX 0x2000 +#define RT_PROXY_PORT_001_TX 0x2001 +#define DISPLAY_PORT_RX 0x6020 + +#define AFE_PORT_INVALID 0xFFFF +#define SLIMBUS_INVALID AFE_PORT_INVALID + +#define AFE_PORT_CMD_START 0x000100ca + +#define AFE_EVENT_RTPORT_START 0 +#define AFE_EVENT_RTPORT_STOP 1 +#define AFE_EVENT_RTPORT_LOW_WM 2 +#define AFE_EVENT_RTPORT_HI_WM 3 + +#define ADSP_AFE_VERSION 0x00200000 + +/* Size of the range of port IDs for the audio interface. */ +#define AFE_PORT_ID_AUDIO_IF_PORT_RANGE_SIZE 0xF + +/* Size of the range of port IDs for internal BT-FM ports. */ +#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_SIZE 0x6 + +/* Size of the range of port IDs for SLIMbus® + * multichannel + * ports. + */ +#define AFE_PORT_ID_SLIMBUS_RANGE_SIZE 0xA + +/* Size of the range of port IDs for real-time proxy ports. */ +#define AFE_PORT_ID_RT_PROXY_PORT_RANGE_SIZE 0x2 + +/* Size of the range of port IDs for pseudoports. */ +#define AFE_PORT_ID_PSEUDOPORT_RANGE_SIZE 0x5 + +/* Start of the range of port IDs for the audio interface. */ +#define AFE_PORT_ID_AUDIO_IF_PORT_RANGE_START 0x1000 + +/* End of the range of port IDs for the audio interface. */ +#define AFE_PORT_ID_AUDIO_IF_PORT_RANGE_END \ + (AFE_PORT_ID_AUDIO_IF_PORT_RANGE_START +\ + AFE_PORT_ID_AUDIO_IF_PORT_RANGE_SIZE - 1) + +/* Start of the range of port IDs for real-time proxy ports. */ +#define AFE_PORT_ID_RT_PROXY_PORT_RANGE_START 0x2000 + +/* End of the range of port IDs for real-time proxy ports. */ +#define AFE_PORT_ID_RT_PROXY_PORT_RANGE_END \ + (AFE_PORT_ID_RT_PROXY_PORT_RANGE_START +\ + AFE_PORT_ID_RT_PROXY_PORT_RANGE_SIZE-1) + +/* Start of the range of port IDs for internal BT-FM devices. */ +#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_START 0x3000 + +/* End of the range of port IDs for internal BT-FM devices. */ +#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_END \ + (AFE_PORT_ID_INTERNAL_BT_FM_RANGE_START +\ + AFE_PORT_ID_INTERNAL_BT_FM_RANGE_SIZE-1) + +/* Start of the range of port IDs for SLIMbus devices. */ +#define AFE_PORT_ID_SLIMBUS_RANGE_START 0x4000 + +/* End of the range of port IDs for SLIMbus devices. */ +#define AFE_PORT_ID_SLIMBUS_RANGE_END \ + (AFE_PORT_ID_SLIMBUS_RANGE_START +\ + AFE_PORT_ID_SLIMBUS_RANGE_SIZE-1) + +/* Start of the range of port IDs for pseudoports. */ +#define AFE_PORT_ID_PSEUDOPORT_RANGE_START 0x8001 + +/* End of the range of port IDs for pseudoports. */ +#define AFE_PORT_ID_PSEUDOPORT_RANGE_END \ + (AFE_PORT_ID_PSEUDOPORT_RANGE_START +\ + AFE_PORT_ID_PSEUDOPORT_RANGE_SIZE-1) + +/* Start of the range of port IDs for TDM devices. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 + +/* End of the range of port IDs for TDM devices. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_END \ + (AFE_PORT_ID_TDM_PORT_RANGE_START+0x40-1) + +/* Size of the range of port IDs for TDM ports. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_SIZE \ + (AFE_PORT_ID_TDM_PORT_RANGE_END - \ + AFE_PORT_ID_TDM_PORT_RANGE_START+1) + +#define AFE_PORT_ID_PRIMARY_MI2S_RX 0x1000 +#define AFE_PORT_ID_PRIMARY_MI2S_TX 0x1001 +#define AFE_PORT_ID_SECONDARY_MI2S_RX 0x1002 +#define AFE_PORT_ID_SECONDARY_MI2S_TX 0x1003 +#define AFE_PORT_ID_TERTIARY_MI2S_RX 0x1004 +#define AFE_PORT_ID_TERTIARY_MI2S_TX 0x1005 +#define AFE_PORT_ID_QUATERNARY_MI2S_RX 0x1006 +#define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007 +#define AUDIO_PORT_ID_I2S_RX 0x1008 +#define AFE_PORT_ID_DIGITAL_MIC_TX 0x1009 +#define AFE_PORT_ID_PRIMARY_PCM_RX 0x100A +#define AFE_PORT_ID_PRIMARY_PCM_TX 0x100B +#define AFE_PORT_ID_SECONDARY_PCM_RX 0x100C +#define AFE_PORT_ID_SECONDARY_PCM_TX 0x100D +#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E +#define AFE_PORT_ID_SECONDARY_MI2S_RX_SD1 0x1010 +#define AFE_PORT_ID_TERTIARY_PCM_RX 0x1012 +#define AFE_PORT_ID_TERTIARY_PCM_TX 0x1013 +#define AFE_PORT_ID_QUATERNARY_PCM_RX 0x1014 +#define AFE_PORT_ID_QUATERNARY_PCM_TX 0x1015 +#define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016 +#define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 +/* ID of the senary MI2S Rx port. */ +#define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 +/* ID of the senary MI2S Tx port. */ +#define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 +/* ID of the Internal 0 MI2S Rx port */ +#define AFE_PORT_ID_INT0_MI2S_RX 0x102E +/* ID of the Internal 0 MI2S Tx port */ +#define AFE_PORT_ID_INT0_MI2S_TX 0x102F +/* ID of the Internal 1 MI2S Rx port */ +#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 +/* ID of the Internal 1 MI2S Tx port */ +#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 +/* ID of the Internal 2 MI2S Rx port */ +#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 +/* ID of the Internal 2 MI2S Tx port */ +#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 +/* ID of the Internal 3 MI2S Rx port */ +#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 +/* ID of the Internal 3 MI2S Tx port */ +#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 +/* ID of the Internal 4 MI2S Rx port */ +#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 +/* ID of the Internal 4 MI2S Tx port */ +#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 +/* ID of the Internal 5 MI2S Rx port */ +#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 +/* ID of the Internal 5 MI2S Tx port */ +#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 +/* ID of the Internal 6 MI2S Rx port */ +#define AFE_PORT_ID_INT6_MI2S_RX 0x103A +/* ID of the Internal 6 MI2S Tx port */ +#define AFE_PORT_ID_INT6_MI2S_TX 0x103B +#define AFE_PORT_ID_SPDIF_RX 0x5000 +#define AFE_PORT_ID_RT_PROXY_PORT_001_RX 0x2000 +#define AFE_PORT_ID_RT_PROXY_PORT_001_TX 0x2001 +#define AFE_PORT_ID_INTERNAL_BT_SCO_RX 0x3000 +#define AFE_PORT_ID_INTERNAL_BT_SCO_TX 0x3001 +#define AFE_PORT_ID_INTERNAL_BT_A2DP_RX 0x3002 +#define AFE_PORT_ID_INTERNAL_FM_RX 0x3004 +#define AFE_PORT_ID_INTERNAL_FM_TX 0x3005 +/* SLIMbus Rx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX 0x4000 +/* SLIMbus Tx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX 0x4001 +/* SLIMbus Rx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX 0x4002 +/* SLIMbus Tx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX 0x4003 +/* SLIMbus Rx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX 0x4004 +/* SLIMbus Tx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX 0x4005 +/* SLIMbus Rx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX 0x4006 +/* SLIMbus Tx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX 0x4007 +/* SLIMbus Rx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008 +/* SLIMbus Tx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009 +/* SLIMbus Rx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a +/* SLIMbus Tx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX 0x400b +/* SLIMbus Rx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c +/* SLIMbus Tx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d +/* SLIMbus Rx port on channel 7. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX 0x400e +/* SLIMbus Tx port on channel 7. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX 0x400f +/* SLIMbus Rx port on channel 8. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX 0x4010 +/* SLIMbus Tx port on channel 8. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX 0x4011 +/* AFE Rx port for audio over Display port */ +#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020 +/*USB AFE port */ +#define AFE_PORT_ID_USB_RX 0x7000 +#define AFE_PORT_ID_USB_TX 0x7001 + +/* Generic pseudoport 1. */ +#define AFE_PORT_ID_PSEUDOPORT_01 0x8001 +/* Generic pseudoport 2. */ +#define AFE_PORT_ID_PSEUDOPORT_02 0x8002 + +/* @xreflabel{hdr:AfePortIdPrimaryAuxPcmTx} + * Primary Aux PCM Tx port ID. + */ +#define AFE_PORT_ID_PRIMARY_PCM_TX 0x100B +/* Pseudoport that corresponds to the voice Rx path. + * For recording, the voice Rx path samples are written to this + * port and consumed by the audio path. + */ + +#define AFE_PORT_ID_VOICE_RECORD_RX 0x8003 + +/* Pseudoport that corresponds to the voice Tx path. + * For recording, the voice Tx path samples are written to this + * port and consumed by the audio path. + */ + +#define AFE_PORT_ID_VOICE_RECORD_TX 0x8004 +/* Pseudoport that corresponds to in-call voice delivery samples. + * During in-call audio delivery, the audio path delivers samples + * to this port from where the voice path delivers them on the + * Rx path. + */ +#define AFE_PORT_ID_VOICE2_PLAYBACK_TX 0x8002 +#define AFE_PORT_ID_VOICE_PLAYBACK_TX 0x8005 + +#define AFE_PORT_ID_PRIMARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x00) +#define AFE_PORT_ID_PRIMARY_TDM_RX_1 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x02) +#define AFE_PORT_ID_PRIMARY_TDM_RX_2 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x04) +#define AFE_PORT_ID_PRIMARY_TDM_RX_3 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x06) +#define AFE_PORT_ID_PRIMARY_TDM_RX_4 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x08) +#define AFE_PORT_ID_PRIMARY_TDM_RX_5 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_PRIMARY_TDM_RX_6 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_PRIMARY_TDM_RX_7 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_PRIMARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x01) +#define AFE_PORT_ID_PRIMARY_TDM_TX_1 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x02) +#define AFE_PORT_ID_PRIMARY_TDM_TX_2 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x04) +#define AFE_PORT_ID_PRIMARY_TDM_TX_3 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x06) +#define AFE_PORT_ID_PRIMARY_TDM_TX_4 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x08) +#define AFE_PORT_ID_PRIMARY_TDM_TX_5 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_PRIMARY_TDM_TX_6 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_PRIMARY_TDM_TX_7 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_SECONDARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x10) +#define AFE_PORT_ID_SECONDARY_TDM_RX_1 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x02) +#define AFE_PORT_ID_SECONDARY_TDM_RX_2 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x04) +#define AFE_PORT_ID_SECONDARY_TDM_RX_3 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x06) +#define AFE_PORT_ID_SECONDARY_TDM_RX_4 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x08) +#define AFE_PORT_ID_SECONDARY_TDM_RX_5 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_SECONDARY_TDM_RX_6 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_SECONDARY_TDM_RX_7 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_SECONDARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x11) +#define AFE_PORT_ID_SECONDARY_TDM_TX_1 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x02) +#define AFE_PORT_ID_SECONDARY_TDM_TX_2 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x04) +#define AFE_PORT_ID_SECONDARY_TDM_TX_3 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x06) +#define AFE_PORT_ID_SECONDARY_TDM_TX_4 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x08) +#define AFE_PORT_ID_SECONDARY_TDM_TX_5 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_SECONDARY_TDM_TX_6 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_SECONDARY_TDM_TX_7 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_TERTIARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x20) +#define AFE_PORT_ID_TERTIARY_TDM_RX_1 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x02) +#define AFE_PORT_ID_TERTIARY_TDM_RX_2 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x04) +#define AFE_PORT_ID_TERTIARY_TDM_RX_3 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x06) +#define AFE_PORT_ID_TERTIARY_TDM_RX_4 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x08) +#define AFE_PORT_ID_TERTIARY_TDM_RX_5 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_TERTIARY_TDM_RX_6 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_TERTIARY_TDM_RX_7 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_TERTIARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x21) +#define AFE_PORT_ID_TERTIARY_TDM_TX_1 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x02) +#define AFE_PORT_ID_TERTIARY_TDM_TX_2 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x04) +#define AFE_PORT_ID_TERTIARY_TDM_TX_3 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x06) +#define AFE_PORT_ID_TERTIARY_TDM_TX_4 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x08) +#define AFE_PORT_ID_TERTIARY_TDM_TX_5 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_TERTIARY_TDM_TX_6 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_TERTIARY_TDM_TX_7 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_QUATERNARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x30) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_1 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x02) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_2 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x04) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_3 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x06) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_4 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x08) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_5 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_6 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_7 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_QUATERNARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x31) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_1 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x02) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_2 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x04) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_3 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x06) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_4 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x08) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_5 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_6 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_7 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_INVALID 0xFFFF + +#define AAC_ENC_MODE_AAC_LC 0x02 +#define AAC_ENC_MODE_AAC_P 0x05 +#define AAC_ENC_MODE_EAAC_P 0x1D + +#define AFE_PSEUDOPORT_CMD_START 0x000100cf +struct afe_pseudoport_start_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 timing; /* FTRT = 0 , AVTimer = 1, */ +} __packed; + +#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0 +struct afe_pseudoport_stop_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 reserved; +} __packed; + + +#define AFE_MODULE_SIDETONE_IIR_FILTER 0x00010202 +#define AFE_PARAM_ID_ENABLE 0x00010203 + +/* Payload of the #AFE_PARAM_ID_ENABLE + * parameter, which enables or + * disables any module. + * The fixed size of this structure is four bytes. + */ + +struct afe_mod_enable_param { + u16 enable; + /* Enables (1) or disables (0) the module. */ + + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +/* ID of the configuration parameter used by the + * #AFE_MODULE_SIDETONE_IIR_FILTER module. + */ +#define AFE_PARAM_ID_SIDETONE_IIR_FILTER_CONFIG 0x00010204 + +struct afe_sidetone_iir_filter_config_params { + u16 num_biquad_stages; +/* Number of stages. + * Supported values: Minimum of 5 and maximum of 10 + */ + + u16 pregain; +/* Pregain for the compensating filter response. + * Supported values: Any number in Q13 format + */ +} __packed; + +#define AFE_MODULE_LOOPBACK 0x00010205 +#define AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH 0x00010206 + +/* Payload of the #AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH parameter, + * which gets/sets loopback gain of a port to an Rx port. + * The Tx port ID of the loopback is part of the set_param command. + */ + +/* Payload of the #AFE_PORT_CMD_SET_PARAM_V2 command's + * configuration/calibration settings for the AFE port. + */ +struct afe_port_cmd_set_param_v2 { + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. */ + + u16 payload_size; +/* Actual size of the payload in bytes. + * This is used for parsing the parameter payload. + * Supported values: > 0 + */ + +u32 payload_address_lsw; +/* LSW of 64 bit Payload address. + * Address should be 32-byte, + * 4kbyte aligned and must be contiguous memory. + */ + +u32 payload_address_msw; +/* MSW of 64 bit Payload address. + * In case of 32-bit shared memory address, + * this field must be set to zero. + * In case of 36-bit shared memory address, + * bit-4 to bit-31 must be set to zero. + * Address should be 32-byte, 4kbyte aligned + * and must be contiguous memory. + */ + +u32 mem_map_handle; +/* Memory map handle returned by + * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands. + * Supported Values: + * - NULL -- Message. The parameter data is in-band. + * - Non-NULL -- The parameter data is Out-band.Pointer to + * the physical address + * in shared memory of the payload data. + * An optional field is available if parameter + * data is in-band: + * afe_param_data_v2 param_data[...]. + * For detailed payload content, see the + * afe_port_param_data_v2 structure. + */ +} __packed; + +#define AFE_PORT_CMD_SET_PARAM_V2 0x000100EF + +struct afe_port_param_data_v2 { + u32 module_id; +/* ID of the module to be configured. + * Supported values: Valid module ID + */ + +u32 param_id; +/* ID of the parameter corresponding to the supported parameters + * for the module ID. + * Supported values: Valid parameter ID + */ + +u16 param_size; +/* Actual size of the data for the + * module_id/param_id pair. The size is a + * multiple of four bytes. + * Supported values: > 0 + */ + +u16 reserved; +/* This field must be set to zero. + */ +} __packed; + +struct afe_loopback_gain_per_path_param { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + u16 rx_port_id; +/* Rx port of the loopback. */ + +u16 gain; +/* Loopback gain per path of the port. + * Supported values: Any number in Q13 format + */ +} __packed; + +/* Parameter ID used to configure and enable/disable the + * loopback path. The difference with respect to the existing + * API, AFE_PORT_CMD_LOOPBACK, is that it allows Rx port to be + * configured as source port in loopback path. Port-id in + * AFE_PORT_CMD_SET_PARAM cmd is the source port which can be + * Tx or Rx port. In addition, we can configure the type of + * routing mode to handle different use cases. + */ +#define AFE_PARAM_ID_LOOPBACK_CONFIG 0x0001020B +#define AFE_API_VERSION_LOOPBACK_CONFIG 0x1 + +enum afe_loopback_routing_mode { + LB_MODE_DEFAULT = 1, + /* Regular loopback from source to destination port */ + LB_MODE_SIDETONE, + /* Sidetone feed from Tx source to Rx destination port */ + LB_MODE_EC_REF_VOICE_AUDIO, + /* Echo canceller reference, voice + audio + DTMF */ + LB_MODE_EC_REF_VOICE + /* Echo canceller reference, voice alone */ +} __packed; + +/* Payload of the #AFE_PARAM_ID_LOOPBACK_CONFIG , + * which enables/disables one AFE loopback. + */ +struct afe_loopback_cfg_v1 { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + u32 loopback_cfg_minor_version; +/* Minor version used for tracking the version of the RMC module + * configuration interface. + * Supported values: #AFE_API_VERSION_LOOPBACK_CONFIG + */ + u16 dst_port_id; + /* Destination Port Id. */ + u16 routing_mode; +/* Specifies data path type from src to dest port. + * Supported values: + * #LB_MODE_DEFAULT + * #LB_MODE_SIDETONE + * #LB_MODE_EC_REF_VOICE_AUDIO + * #LB_MODE_EC_REF_VOICE_A + * #LB_MODE_EC_REF_VOICE + */ + + u16 enable; +/* Specifies whether to enable (1) or + * disable (0) an AFE loopback. + */ + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0. + */ + +} __packed; + +#define AFE_MODULE_SPEAKER_PROTECTION 0x00010209 +#define AFE_PARAM_ID_SPKR_PROT_CONFIG 0x0001020a +#define AFE_API_VERSION_SPKR_PROT_CONFIG 0x1 +#define AFE_SPKR_PROT_EXCURSIONF_LEN 512 +struct afe_spkr_prot_cfg_param_v1 { + u32 spkr_prot_minor_version; +/* + * Minor version used for tracking the version of the + * speaker protection module configuration interface. + * Supported values: #AFE_API_VERSION_SPKR_PROT_CONFIG + */ + +int16_t win_size; +/* Analysis and synthesis window size (nWinSize). + * Supported values: 1024, 512, 256 samples + */ + +int16_t margin; +/* Allowable margin for excursion prediction, + * in L16Q15 format. This is a + * control parameter to allow + * for overestimation of peak excursion. + */ + +int16_t spkr_exc_limit; +/* Speaker excursion limit, in L16Q15 format.*/ + +int16_t spkr_resonance_freq; +/* Resonance frequency of the speaker; used + * to define a frequency range + * for signal modification. + * + * Supported values: 0 to 2000 Hz + */ + +int16_t limhresh; +/* Threshold of the hard limiter; used to + * prevent overshooting beyond a + * signal level that was set by the limiter + * prior to speaker protection. + * Supported values: 0 to 32767 + */ + +int16_t hpf_cut_off_freq; +/* High pass filter cutoff frequency. + * Supported values: 100, 200, 300 Hz + */ + +int16_t hpf_enable; +/* Specifies whether the high pass filter + * is enabled (0) or disabled (1). + */ + +int16_t reserved; +/* This field must be set to zero. */ + +int32_t amp_gain; +/* Amplifier gain in L32Q15 format. + * This is the RMS voltage at the + * loudspeaker when a 0dBFS tone + * is played in the digital domain. + */ + +int16_t excursionf[AFE_SPKR_PROT_EXCURSIONF_LEN]; +/* Array of the excursion transfer function. + * The peak excursion of the + * loudspeaker diaphragm is + * measured in millimeters for 1 Vrms Sine + * tone at all FFT bin frequencies. + * Supported values: Q15 format + */ +} __packed; + + +#define AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER 0x000100E0 + +/* Payload of the #AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER + * command, which registers a real-time port driver + * with the AFE service. + */ +struct afe_service_cmd_register_rt_port_driver { + struct apr_hdr hdr; + u16 port_id; +/* Port ID with which the real-time driver exchanges data + * (registers for events). + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +#define AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER 0x000100E1 + +/* Payload of the #AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER + * command, which unregisters a real-time port driver from + * the AFE service. + */ +struct afe_service_cmd_unregister_rt_port_driver { + struct apr_hdr hdr; + u16 port_id; +/* Port ID from which the real-time + * driver unregisters for events. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +#define AFE_EVENT_RT_PROXY_PORT_STATUS 0x00010105 +#define AFE_EVENTYPE_RT_PROXY_PORT_START 0 +#define AFE_EVENTYPE_RT_PROXY_PORT_STOP 1 +#define AFE_EVENTYPE_RT_PROXY_PORT_LOW_WATER_MARK 2 +#define AFE_EVENTYPE_RT_PROXY_PORT_HIGH_WATER_MARK 3 +#define AFE_EVENTYPE_RT_PROXY_PORT_INVALID 0xFFFF + +/* Payload of the #AFE_EVENT_RT_PROXY_PORT_STATUS + * message, which sends an event from the AFE service + * to a registered client. + */ +struct afe_event_rt_proxy_port_status { + u16 port_id; +/* Port ID to which the event is sent. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 eventype; +/* Type of event. + * Supported values: + * - #AFE_EVENTYPE_RT_PROXY_PORT_START + * - #AFE_EVENTYPE_RT_PROXY_PORT_STOP + * - #AFE_EVENTYPE_RT_PROXY_PORT_LOW_WATER_MARK + * - #AFE_EVENTYPE_RT_PROXY_PORT_HIGH_WATER_MARK + */ +} __packed; + +#define AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2 0x000100ED + +struct afe_port_data_cmd_rt_proxy_port_write_v2 { + struct apr_hdr hdr; + u16 port_id; +/* Tx (mic) proxy port ID with which the real-time + * driver exchanges data. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 reserved; + /* This field must be set to zero. */ + + u32 buffer_address_lsw; +/* LSW Address of the buffer containing the + * data from the real-time source + * device on a client. + */ + + u32 buffer_address_msw; +/* MSW Address of the buffer containing the + * data from the real-time source + * device on a client. + */ + + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory + * attributes is returned if + * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS + * command is successful. + * Supported Values: + * - Any 32 bit value + */ + + u32 available_bytes; +/* Number of valid bytes available + * in the buffer (including all + * channels: number of bytes per + * channel = availableBytesumChannels). + * Supported values: > 0 + * + * This field must be equal to the frame + * size specified in the #AFE_PORT_AUDIO_IF_CONFIG + * command that was sent to configure this + * port. + */ +} __packed; + +#define AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2 0x000100EE + +/* Payload of the + * #AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2 command, which + * delivers an empty buffer to the AFE service. On + * acknowledgment, data is filled in the buffer. + */ +struct afe_port_data_cmd_rt_proxy_port_read_v2 { + struct apr_hdr hdr; + u16 port_id; +/* Rx proxy port ID with which the real-time + * driver exchanges data. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + * (This must be an Rx (speaker) port.) + */ + + u16 reserved; + /* This field must be set to zero. */ + + u32 buffer_address_lsw; +/* LSW Address of the buffer containing the data sent from the AFE + * service to a real-time sink device on the client. + */ + + + u32 buffer_address_msw; +/* MSW Address of the buffer containing the data sent from the AFE + * service to a real-time sink device on the client. + */ + + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory attributes is + * returned if AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS command is + * successful. + * Supported Values: + * - Any 32 bit value + */ + + u32 available_bytes; +/* Number of valid bytes available in the buffer (including all + * channels). + * Supported values: > 0 + * This field must be equal to the frame size specified in the + * #AFE_PORT_AUDIO_IF_CONFIG command that was sent to configure + * this port. + */ +} __packed; + +/* This module ID is related to device configuring like I2S,PCM, + * HDMI, SLIMBus etc. This module supports following parameter ids. + * - #AFE_PARAM_ID_I2S_CONFIG + * - #AFE_PARAM_ID_PCM_CONFIG + * - #AFE_PARAM_ID_DIGI_MIC_CONFIG + * - #AFE_PARAM_ID_HDMI_CONFIG + * - #AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG + * - #AFE_PARAM_ID_SLIMBUS_CONFIG + * - #AFE_PARAM_ID_RT_PROXY_CONFIG + */ + +#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C +#define AFE_PORT_SAMPLE_RATE_8K 8000 +#define AFE_PORT_SAMPLE_RATE_16K 16000 +#define AFE_PORT_SAMPLE_RATE_48K 48000 +#define AFE_PORT_SAMPLE_RATE_96K 96000 +#define AFE_PORT_SAMPLE_RATE_192K 192000 +#define AFE_LINEAR_PCM_DATA 0x0 +#define AFE_NON_LINEAR_DATA 0x1 +#define AFE_LINEAR_PCM_DATA_PACKED_60958 0x2 +#define AFE_NON_LINEAR_DATA_PACKED_60958 0x3 + +/* This param id is used to configure I2S interface */ +#define AFE_PARAM_ID_I2S_CONFIG 0x0001020D +#define AFE_API_VERSION_I2S_CONFIG 0x1 +/* Enumeration for setting the I2S configuration + * channel_mode parameter to + * serial data wire number 1-3 (SD3). + */ +#define AFE_PORT_I2S_SD0 0x1 +#define AFE_PORT_I2S_SD1 0x2 +#define AFE_PORT_I2S_SD2 0x3 +#define AFE_PORT_I2S_SD3 0x4 +#define AFE_PORT_I2S_QUAD01 0x5 +#define AFE_PORT_I2S_QUAD23 0x6 +#define AFE_PORT_I2S_6CHS 0x7 +#define AFE_PORT_I2S_8CHS 0x8 +#define AFE_PORT_I2S_MONO 0x0 +#define AFE_PORT_I2S_STEREO 0x1 +#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL 0x0 +#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL 0x1 + +/* Payload of the #AFE_PARAM_ID_I2S_CONFIG + * command's (I2S configuration + * parameter). + */ +struct afe_param_id_i2s_cfg { + u32 i2s_cfg_minor_version; +/* Minor version used for tracking the version of the I2S + * configuration interface. + * Supported values: #AFE_API_VERSION_I2S_CONFIG + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + + u16 channel_mode; +/* I2S lines and multichannel operation. + * Supported values: + * - #AFE_PORT_I2S_SD0 + * - #AFE_PORT_I2S_SD1 + * - #AFE_PORT_I2S_SD2 + * - #AFE_PORT_I2S_SD3 + * - #AFE_PORT_I2S_QUAD01 + * - #AFE_PORT_I2S_QUAD23 + * - #AFE_PORT_I2S_6CHS + * - #AFE_PORT_I2S_8CHS + */ + + u16 mono_stereo; +/* Specifies mono or stereo. This applies only when + * a single I2S line is used. + * Supported values: + * - #AFE_PORT_I2S_MONO + * - #AFE_PORT_I2S_STEREO + */ + + u16 ws_src; +/* Word select source: internal or external. + * Supported values: + * - #AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL + * - #AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K + */ + + u16 data_format; +/* data format + * Supported values: + * - #LINEAR_PCM_DATA + * - #NON_LINEAR_DATA + * - #LINEAR_PCM_DATA_PACKED_IN_60958 + * - #NON_LINEAR_DATA_PACKED_IN_60958 + */ + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +/* + * This param id is used to configure PCM interface + */ + +#define AFE_API_VERSION_SPDIF_CONFIG 0x1 +#define AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG 0x1 +#define AFE_API_VERSION_SPDIF_CLK_CONFIG 0x1 +#define AFE_CH_STATUS_A 1 +#define AFE_CH_STATUS_B 2 + +#define AFE_PARAM_ID_SPDIF_CONFIG 0x00010244 +#define AFE_PARAM_ID_CH_STATUS_CONFIG 0x00010245 +#define AFE_PARAM_ID_SPDIF_CLK_CONFIG 0x00010246 + +#define AFE_PORT_CLK_ROOT_LPAPLL 0x3 +#define AFE_PORT_CLK_ROOT_LPAQ6PLL 0x4 + +struct afe_param_id_spdif_cfg { +/* Minor version used for tracking the version of the SPDIF + * configuration interface. + * Supported values: #AFE_API_VERSION_SPDIF_CONFIG + */ + u32 spdif_cfg_minor_version; + +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_22_05K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_44_1K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_176_4K + * - #AFE_PORT_SAMPLE_RATE_192K + */ + u32 sample_rate; + +/* data format + * Supported values: + * - #AFE_LINEAR_PCM_DATA + * - #AFE_NON_LINEAR_DATA + */ + u16 data_format; +/* Number of channels supported by the port + * - PCM - 1, Compressed Case - 2 + */ + u16 num_channels; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 bit_width; +/* This field must be set to zero. */ + u16 reserved; +} __packed; + +struct afe_param_id_spdif_ch_status_cfg { + u32 ch_status_cfg_minor_version; +/* Minor version used for tracking the version of channel + * status configuration. Current supported version is 1 + */ + + u32 status_type; +/* Indicate if the channel status is for channel A or B + * Supported values: + * - #AFE_CH_STATUS_A + * - #AFE_CH_STATUS_B + */ + + u8 status_bits[24]; +/* Channel status - 192 bits for channel + * Byte ordering as defined by IEC60958-3 + */ + + u8 status_mask[24]; +/* Channel status with mask bits 1 will be applied. + * Byte ordering as defined by IEC60958-3 + */ +} __packed; + +struct afe_param_id_spdif_clk_cfg { + u32 clk_cfg_minor_version; +/* Minor version used for tracking the version of SPDIF + * interface clock configuration. Current supported version + * is 1 + */ + + u32 clk_value; +/* Specifies the clock frequency in Hz to set + * Supported values: + * 0 - Disable the clock + * 2 (byphase) * 32 (60958 subframe size) * sampling rate * 2 + * (channels A and B) + */ + + u32 clk_root; +/* Specifies SPDIF root clk source + * Supported Values: + * - #AFE_PORT_CLK_ROOT_LPAPLL + * - #AFE_PORT_CLK_ROOT_LPAQ6PLL + */ +} __packed; + +struct afe_spdif_clk_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_spdif_clk_cfg clk_cfg; +} __packed; + +struct afe_spdif_chstatus_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_spdif_ch_status_cfg ch_status; +} __packed; + +struct afe_spdif_port_config { + struct afe_param_id_spdif_cfg cfg; + struct afe_param_id_spdif_ch_status_cfg ch_status; +} __packed; + +#define AFE_PARAM_ID_PCM_CONFIG 0x0001020E +#define AFE_API_VERSION_PCM_CONFIG 0x1 +/* Enumeration for the auxiliary PCM synchronization signal + * provided by an external source. + */ + +#define AFE_PORT_PCM_SYNC_SRC_EXTERNAL 0x0 +/* Enumeration for the auxiliary PCM synchronization signal + * provided by an internal source. + */ +#define AFE_PORT_PCM_SYNC_SRC_INTERNAL 0x1 +/* Enumeration for the PCM configuration aux_mode parameter, + * which configures the auxiliary PCM interface to use + * short synchronization. + */ +#define AFE_PORT_PCM_AUX_MODE_PCM 0x0 +/* + * Enumeration for the PCM configuration aux_mode parameter, + * which configures the auxiliary PCM interface to use long + * synchronization. + */ +#define AFE_PORT_PCM_AUX_MODE_AUX 0x1 +/* + * Enumeration for setting the PCM configuration frame to 8. + */ +#define AFE_PORT_PCM_BITS_PER_FRAME_8 0x0 +/* + * Enumeration for setting the PCM configuration frame to 16. + */ +#define AFE_PORT_PCM_BITS_PER_FRAME_16 0x1 + +/* Enumeration for setting the PCM configuration frame to 32.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_32 0x2 + +/* Enumeration for setting the PCM configuration frame to 64.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_64 0x3 + +/* Enumeration for setting the PCM configuration frame to 128.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_128 0x4 + +/* Enumeration for setting the PCM configuration frame to 256.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_256 0x5 + +/* Enumeration for setting the PCM configuration + * quantype parameter to A-law with no padding. + */ +#define AFE_PORT_PCM_ALAW_NOPADDING 0x0 + +/* Enumeration for setting the PCM configuration quantype + * parameter to mu-law with no padding. + */ +#define AFE_PORT_PCM_MULAW_NOPADDING 0x1 +/* Enumeration for setting the PCM configuration quantype + * parameter to linear with no padding. + */ +#define AFE_PORT_PCM_LINEAR_NOPADDING 0x2 +/* Enumeration for setting the PCM configuration quantype + * parameter to A-law with padding. + */ +#define AFE_PORT_PCM_ALAW_PADDING 0x3 +/* Enumeration for setting the PCM configuration quantype + * parameter to mu-law with padding. + */ +#define AFE_PORT_PCM_MULAW_PADDING 0x4 +/* Enumeration for setting the PCM configuration quantype + * parameter to linear with padding. + */ +#define AFE_PORT_PCM_LINEAR_PADDING 0x5 +/* Enumeration for disabling the PCM configuration + * ctrl_data_out_enable parameter. + * The PCM block is the only master. + */ +#define AFE_PORT_PCM_CTRL_DATA_OE_DISABLE 0x0 +/* + * Enumeration for enabling the PCM configuration + * ctrl_data_out_enable parameter. The PCM block shares + * the signal with other masters. + */ +#define AFE_PORT_PCM_CTRL_DATA_OE_ENABLE 0x1 + +/* Payload of the #AFE_PARAM_ID_PCM_CONFIG command's + * (PCM configuration parameter). + */ + +struct afe_param_id_pcm_cfg { + u32 pcm_cfg_minor_version; +/* Minor version used for tracking the version of the AUX PCM + * configuration interface. + * Supported values: #AFE_API_VERSION_PCM_CONFIG + */ + + u16 aux_mode; +/* PCM synchronization setting. + * Supported values: + * - #AFE_PORT_PCM_AUX_MODE_PCM + * - #AFE_PORT_PCM_AUX_MODE_AUX + */ + + u16 sync_src; +/* Synchronization source. + * Supported values: + * - #AFE_PORT_PCM_SYNC_SRC_EXTERNAL + * - #AFE_PORT_PCM_SYNC_SRC_INTERNAL + */ + + u16 frame_setting; +/* Number of bits per frame. + * Supported values: + * - #AFE_PORT_PCM_BITS_PER_FRAME_8 + * - #AFE_PORT_PCM_BITS_PER_FRAME_16 + * - #AFE_PORT_PCM_BITS_PER_FRAME_32 + * - #AFE_PORT_PCM_BITS_PER_FRAME_64 + * - #AFE_PORT_PCM_BITS_PER_FRAME_128 + * - #AFE_PORT_PCM_BITS_PER_FRAME_256 + */ + + u16 quantype; +/* PCM quantization type. + * Supported values: + * - #AFE_PORT_PCM_ALAW_NOPADDING + * - #AFE_PORT_PCM_MULAW_NOPADDING + * - #AFE_PORT_PCM_LINEAR_NOPADDING + * - #AFE_PORT_PCM_ALAW_PADDING + * - #AFE_PORT_PCM_MULAW_PADDING + * - #AFE_PORT_PCM_LINEAR_PADDING + */ + + u16 ctrl_data_out_enable; +/* Specifies whether the PCM block shares the data-out + * signal to the drive with other masters. + * Supported values: + * - #AFE_PORT_PCM_CTRL_DATA_OE_DISABLE + * - #AFE_PORT_PCM_CTRL_DATA_OE_ENABLE + */ + u16 reserved; + /* This field must be set to zero. */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to 4 + */ + + u16 slot_number_mapping[4]; +/* Specifies the slot number for the each channel in + * multi channel scenario. + * Supported values: 1 to 32 + */ +} __packed; + +/* + * This param id is used to configure DIGI MIC interface + */ +#define AFE_PARAM_ID_DIGI_MIC_CONFIG 0x0001020F +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_DIGI_MIC_CONFIG 0x1 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to left 0. + */ + +#define AFE_PORT_DIGI_MIC_MODE_LEFT0 0x1 + +/*Enumeration for setting the digital mic configuration + * channel_mode parameter to right 0. + */ + + +#define AFE_PORT_DIGI_MIC_MODE_RIGHT0 0x2 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to left 1. + */ + +#define AFE_PORT_DIGI_MIC_MODE_LEFT1 0x3 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to right 1. + */ + +#define AFE_PORT_DIGI_MIC_MODE_RIGHT1 0x4 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to stereo 0. + */ +#define AFE_PORT_DIGI_MIC_MODE_STEREO0 0x5 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to stereo 1. + */ + + +#define AFE_PORT_DIGI_MIC_MODE_STEREO1 0x6 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to quad. + */ + +#define AFE_PORT_DIGI_MIC_MODE_QUAD 0x7 + +/* Payload of the #AFE_PARAM_ID_DIGI_MIC_CONFIG command's + * (DIGI MIC configuration + * parameter). + */ +struct afe_param_id_digi_mic_cfg { + u32 digi_mic_cfg_minor_version; +/* Minor version used for tracking the version of the DIGI Mic + * configuration interface. + * Supported values: #AFE_API_VERSION_DIGI_MIC_CONFIG + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u16 channel_mode; +/* Digital mic and multichannel operation. + * Supported values: + * - #AFE_PORT_DIGI_MIC_MODE_LEFT0 + * - #AFE_PORT_DIGI_MIC_MODE_RIGHT0 + * - #AFE_PORT_DIGI_MIC_MODE_LEFT1 + * - #AFE_PORT_DIGI_MIC_MODE_RIGHT1 + * - #AFE_PORT_DIGI_MIC_MODE_STEREO0 + * - #AFE_PORT_DIGI_MIC_MODE_STEREO1 + * - #AFE_PORT_DIGI_MIC_MODE_QUAD + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + */ +} __packed; + +/* This param id is used to configure HDMI interface */ +#define AFE_PARAM_ID_HDMI_CONFIG 0x00010210 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_HDMI_CONFIG 0x1 + +/* Payload of the #AFE_PARAM_ID_HDMI_CONFIG command, + * which configures a multichannel HDMI audio interface. + */ +struct afe_param_id_hdmi_multi_chan_audio_cfg { + u32 hdmi_cfg_minor_version; +/* Minor version used for tracking the version of the HDMI + * configuration interface. + * Supported values: #AFE_API_VERSION_HDMI_CONFIG + */ + +u16 datatype; +/* data type + * Supported values: + * - #LINEAR_PCM_DATA + * - #NON_LINEAR_DATA + * - #LINEAR_PCM_DATA_PACKED_IN_60958 + * - #NON_LINEAR_DATA_PACKED_IN_60958 + */ + +u16 channel_allocation; +/* HDMI channel allocation information for programming an HDMI + * frame. The default is 0 (Stereo). + * + * This information is defined in the HDMI standard, CEA 861-D + * (refer to @xhyperref{S1,[S1]}). The number of channels is also + * inferred from this parameter. + */ + + +u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - 22050, 44100, 176400 for compressed streams + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +/* This param id is used to configure BT or FM(RIVA) interface */ +#define AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG 0x00010211 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_INTERNAL_BT_FM_CONFIG 0x1 + +/* Payload of the #AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG + * command's BT voice/BT audio/FM configuration parameter. + */ +struct afe_param_id_internal_bt_fm_cfg { + u32 bt_fm_cfg_minor_version; +/* Minor version used for tracking the version of the BT and FM + * configuration interface. + * Supported values: #AFE_API_VERSION_INTERNAL_BT_FM_CONFIG + */ + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to 2 + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K (only for BTSCO) + * - #AFE_PORT_SAMPLE_RATE_16K (only for BTSCO) + * - #AFE_PORT_SAMPLE_RATE_48K (FM and A2DP) + */ +} __packed; + +/* This param id is used to configure SLIMBUS interface using + * shared channel approach. + */ + + +#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1 + +/* Enumeration for setting SLIMbus device ID 1. */ +#define AFE_SLIMBUS_DEVICE_1 0x0 + +/* Enumeration for setting SLIMbus device ID 2. */ +#define AFE_SLIMBUS_DEVICE_2 0x1 + +/* Enumeration for setting the SLIMbus data formats. */ +#define AFE_SB_DATA_FORMAT_NOT_INDICATED 0x0 + +/* Enumeration for setting the maximum number of streams per + * device. + */ + +#define AFE_PORT_MAX_AUDIO_CHAN_CNT 0x8 + +/* Payload of the #AFE_PORT_CMD_SLIMBUS_CONFIG command's SLIMbus + * port configuration parameter. + */ + +struct afe_param_id_slimbus_cfg { + u32 sb_cfg_minor_version; +/* Minor version used for tracking the version of the SLIMBUS + * configuration interface. + * Supported values: #AFE_API_VERSION_SLIMBUS_CONFIG + */ + + u16 slimbus_dev_id; +/* SLIMbus hardware device ID, which is required to handle + * multiple SLIMbus hardware blocks. + * Supported values: - #AFE_SLIMBUS_DEVICE_1 - #AFE_SLIMBUS_DEVICE_2 + */ + + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + + u16 data_format; +/* Data format supported by the SLIMbus hardware. The default is + * 0 (#AFE_SB_DATA_FORMAT_NOT_INDICATED), which indicates the + * hardware does not perform any format conversions before the data + * transfer. + */ + + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + + u8 shared_ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +/* Mapping of shared channel IDs (128 to 255) to which the + * master port is to be connected. + * Shared_channel_mapping[i] represents the shared channel assigned + * for audio channel i in multichannel audio data. + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K + */ +} __packed; + + +/* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS to configure + * USB audio device parameter. It should be used with + * AFE_MODULE_AUDIO_DEV_INTERFACE + */ +#define AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS 0x000102A5 + +/* Minor version used for tracking USB audio configuration */ +#define AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG 0x1 + +/* Payload of the AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_usb_audio_dev_params { +/* Minor version used for tracking USB audio device parameter. + * Supported values: AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Token of actual end USB aduio device */ + u32 dev_token; +} __packed; + +/* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_CONFIG to configure + * USB audio interface. It should be used with AFE_MODULE_AUDIO_DEV_INTERFACE + */ +#define AFE_PARAM_ID_USB_AUDIO_CONFIG 0x000102A4 + +/* Payload of the AFE_PARAM_ID_USB_AUDIO_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_usb_audio_cfg { +/* Minor version used for tracking USB audio device configuration. + * Supported values: AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Sampling rate of the port. + * Supported values: + * - AFE_PORT_SAMPLE_RATE_8K + * - AFE_PORT_SAMPLE_RATE_11025 + * - AFE_PORT_SAMPLE_RATE_12K + * - AFE_PORT_SAMPLE_RATE_16K + * - AFE_PORT_SAMPLE_RATE_22050 + * - AFE_PORT_SAMPLE_RATE_24K + * - AFE_PORT_SAMPLE_RATE_32K + * - AFE_PORT_SAMPLE_RATE_44P1K + * - AFE_PORT_SAMPLE_RATE_48K + * - AFE_PORT_SAMPLE_RATE_96K + * - AFE_PORT_SAMPLE_RATE_192K + */ + u32 sample_rate; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 bit_width; +/* Number of channels. + * Supported values: 1 and 2 + */ + u16 num_channels; +/* Data format supported by the USB. The supported value is + * 0 (#AFE_USB_AUDIO_DATA_FORMAT_LINEAR_PCM). + */ + u16 data_format; +/* this field must be 0 */ + u16 reserved; +/* device token of actual end USB aduio device */ + u32 dev_token; +} __packed; + +struct afe_usb_audio_dev_param_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_usb_audio_dev_params usb_dev; +} __packed; + +/* This param id is used to configure Real Time Proxy interface. */ +#define AFE_PARAM_ID_RT_PROXY_CONFIG 0x00010213 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_RT_PROXY_CONFIG 0x1 + +/* Payload of the #AFE_PARAM_ID_RT_PROXY_CONFIG + * command (real-time proxy port configuration parameter). + */ +struct afe_param_id_rt_proxy_port_cfg { + u32 rt_proxy_cfg_minor_version; +/* Minor version used for tracking the version of rt-proxy + * config interface. + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u16 interleaved; +/* Specifies whether the data exchanged between the AFE + * interface and real-time port is interleaved. + * Supported values: - 0 -- Non-interleaved (samples from each + * channel are contiguous in the buffer) - 1 -- Interleaved + * (corresponding samples from each input channel are interleaved + * within the buffer) + */ + + + u16 frame_size; +/* Size of the frames that are used for PCM exchanges with this + * port. + * Supported values: > 0, in bytes + * For example, 5 ms buffers of 16 bits and 16 kHz stereo samples + * is 5 ms * 16 samples/ms * 2 bytes/sample * 2 channels = 320 + * bytes. + */ + u16 jitter_allowance; +/* Configures the amount of jitter that the port will allow. + * Supported values: > 0 + * For example, if +/-10 ms of jitter is anticipated in the timing + * of sending frames to the port, and the configuration is 16 kHz + * mono with 16-bit samples, this field is 10 ms * 16 samples/ms * 2 + * bytes/sample = 320. + */ + + u16 low_water_mark; +/* Low watermark in bytes (including all channels). + * Supported values: + * - 0 -- Do not send any low watermark events + * - > 0 -- Low watermark for triggering an event + * If the number of bytes in an internal circular buffer is lower + * than this low_water_mark parameter, a LOW_WATER_MARK event is + * sent to applications (via the #AFE_EVENT_RT_PROXY_PORT_STATUS + * event). + * Use of watermark events is optional for debugging purposes. + */ + + u16 high_water_mark; +/* High watermark in bytes (including all channels). + * Supported values: + * - 0 -- Do not send any high watermark events + * - > 0 -- High watermark for triggering an event + * If the number of bytes in an internal circular buffer exceeds + * TOTAL_CIRC_BUF_SIZE minus high_water_mark, a high watermark event + * is sent to applications (via the #AFE_EVENT_RT_PROXY_PORT_STATUS + * event). + * The use of watermark events is optional and for debugging + * purposes. + */ + + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + */ + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + + u16 reserved; + /* For 32 bit alignment. */ +} __packed; + + +/* This param id is used to configure the Pseudoport interface */ + +#define AFE_PARAM_ID_PSEUDO_PORT_CONFIG 0x00010219 + +/* Version information used to handle future additions to the configuration + * interface (for backward compatibility). + */ +#define AFE_API_VERSION_PSEUDO_PORT_CONFIG 0x1 + +/* Enumeration for setting the timing_mode parameter to faster than real + * time. + */ +#define AFE_PSEUDOPORT_TIMING_MODE_FTRT 0x0 + +/* Enumeration for setting the timing_mode parameter to real time using + * timers. + */ +#define AFE_PSEUDOPORT_TIMING_MODE_TIMER 0x1 + +/* Payload of the AFE_PARAM_ID_PSEUDO_PORT_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_pseudo_port_cfg { + u32 pseud_port_cfg_minor_version; + /* + * Minor version used for tracking the version of the pseudoport + * configuration interface. + */ + + u16 bit_width; + /* Bit width of the sample at values 16, 24 */ + + u16 num_channels; + /* Number of channels at values 1 to 8 */ + + u16 data_format; + /* Non-linear data format supported by the pseudoport (for future use). + * At values #AFE_LINEAR_PCM_DATA + */ + + u16 timing_mode; + /* Indicates whether the pseudoport synchronizes to the clock or + * operates faster than real time. + * at values + * - #AFE_PSEUDOPORT_TIMING_MODE_FTRT + * - #AFE_PSEUDOPORT_TIMING_MODE_TIMER @tablebulletend + */ + + u32 sample_rate; + /* Sample rate at which the pseudoport will run. + * at values + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K @tablebulletend + */ +} __packed; + +#define AFE_PARAM_ID_TDM_CONFIG 0x0001029D + +#define AFE_API_VERSION_TDM_CONFIG 1 + +#define AFE_PORT_TDM_SHORT_SYNC_BIT_MODE 0 +#define AFE_PORT_TDM_LONG_SYNC_MODE 1 +#define AFE_PORT_TDM_SHORT_SYNC_SLOT_MODE 2 + +#define AFE_PORT_TDM_SYNC_SRC_EXTERNAL 0 +#define AFE_PORT_TDM_SYNC_SRC_INTERNAL 1 + +#define AFE_PORT_TDM_CTRL_DATA_OE_DISABLE 0 +#define AFE_PORT_TDM_CTRL_DATA_OE_ENABLE 1 + +#define AFE_PORT_TDM_SYNC_NORMAL 0 +#define AFE_PORT_TDM_SYNC_INVERT 1 + +#define AFE_PORT_TDM_DATA_DELAY_0_BCLK_CYCLE 0 +#define AFE_PORT_TDM_DATA_DELAY_1_BCLK_CYCLE 1 +#define AFE_PORT_TDM_DATA_DELAY_2_BCLK_CYCLE 2 + +/* Payload of the AFE_PARAM_ID_TDM_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_tdm_cfg { + u32 tdm_cfg_minor_version; + /* < Minor version used to track TDM configuration. + * @values #AFE_API_VERSION_TDM_CONFIG + */ + + u32 num_channels; + /* < Number of enabled slots for TDM frame. + * @values 1 to 8 + */ + + u32 sample_rate; + /* < Sampling rate of the port. + * @values + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_24K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_48K @tablebulletend + */ + + u32 bit_width; + /* < Bit width of the sample. + * @values 16, 24 + */ + + u16 data_format; + /* < Data format: linear and compressed + * @values + * - #AFE_LINEAR_PCM_DATA + * - #AFE_NON_LINEAR_DATA @tablebulletend + */ + + u16 sync_mode; + /* < TDM synchronization setting. + * @values (short, long, slot) sync mode + * - #AFE_PORT_TDM_SHORT_SYNC_BIT_MODE + * - #AFE_PORT_TDM_LONG_SYNC_MODE + * - #AFE_PORT_TDM_SHORT_SYNC_SLOT_MODE @tablebulletend + */ + + u16 sync_src; + /* < Synchronization source. + * @values + * - #AFE_PORT_TDM_SYNC_SRC_EXTERNAL + * - #AFE_PORT_TDM_SYNC_SRC_INTERNAL @tablebulletend + */ + + u16 nslots_per_frame; + /* < Number of slots per frame. Typical : 1, 2, 4, 8, 16, 32. + * @values 1 - 32 + */ + + u16 ctrl_data_out_enable; + /* < Specifies whether the TDM block shares the data-out signal to the + * drive with other masters. + * @values + * - #AFE_PORT_TDM_CTRL_DATA_OE_DISABLE + * - #AFE_PORT_TDM_CTRL_DATA_OE_ENABLE @tablebulletend + */ + + u16 ctrl_invert_sync_pulse; + /* < Specifies whether to invert the sync or not. + * @values + * - #AFE_PORT_TDM_SYNC_NORMAL + * - #AFE_PORT_TDM_SYNC_INVERT @tablebulletend + */ + + u16 ctrl_sync_data_delay; + /* < Specifies the number of bit clock to delay data with respect to + * sync edge. + * @values + * - #AFE_PORT_TDM_DATA_DELAY_0_BCLK_CYCLE + * - #AFE_PORT_TDM_DATA_DELAY_1_BCLK_CYCLE + * - #AFE_PORT_TDM_DATA_DELAY_2_BCLK_CYCLE @tablebulletend + */ + + u16 slot_width; + /* < Slot width of the slot in a TDM frame. (slot_width >= bit_width) + * have to be satisfied. + * @values 16, 24, 32 + */ + + u32 slot_mask; + /* < Position of active slots. When that bit is set, + * that paricular slot is active. + * Number of active slots can be inferred by number of + * bits set in the mask. Only 8 individual bits can be enabled. + * Bits 0..31 corresponding to slot 0..31 + * @values 1 to 2^32 - 1 + */ +} __packed; + +/* ID of Time Divsion Multiplexing (TDM) module, + * which is used for configuring the AFE TDM. + * + * This module supports following parameter IDs: + * - #AFE_PORT_TDM_SLOT_CONFIG + * + * To configure the TDM interface, the client must use the + * #AFE_PORT_CMD_SET_PARAM command, and fill the module ID with the + * respective parameter IDs as listed above. + */ + +#define AFE_MODULE_TDM 0x0001028A + +/* ID of the parameter used by #AFE_MODULE_TDM to configure + * the TDM slot mapping. #AFE_PORT_CMD_SET_PARAM can use this parameter ID. + */ +#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 + +/* Version information used to handle future additions to slot mapping + * configuration (for backward compatibility). + */ +#define AFE_API_VERSION_SLOT_MAPPING_CONFIG 0x1 + +/* Data align type */ +#define AFE_SLOT_MAPPING_DATA_ALIGN_MSB 0 +#define AFE_SLOT_MAPPING_DATA_ALIGN_LSB 1 + +#define AFE_SLOT_MAPPING_OFFSET_INVALID 0xFFFF + +/* Payload of the AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG + * command's TDM configuration parameter. + */ +struct afe_param_id_slot_mapping_cfg { + u32 minor_version; + /* < Minor version used for tracking TDM slot configuration. + * @values #AFE_API_VERSION_TDM_SLOT_CONFIG + */ + + u16 num_channel; + /* < number of channel of the audio sample. + * @values 1, 2, 4, 6, 8 @tablebulletend + */ + + u16 bitwidth; + /* < Slot bit width for each channel + * @values 16, 24, 32 + */ + + u32 data_align_type; + /* < indicate how data packed from slot_offset for 32 slot bit width + * in case of sample bit width is 24. + * @values + * #AFE_SLOT_MAPPING_DATA_ALIGN_MSB + * #AFE_SLOT_MAPPING_DATA_ALIGN_LSB + */ + + u16 offset[AFE_PORT_MAX_AUDIO_CHAN_CNT]; + /* < Array of the slot mapping start offset in bytes for this frame. + * The bytes is counted from 0. The 0 is mapped to the 1st byte + * in or out of the digital serial data line this sub-frame belong to. + * slot_offset[] setting is per-channel based. + * The max num of channel supported is 8. + * The valid offset value must always be continuly placed in from + * index 0. + * Set offset as AFE_SLOT_MAPPING_OFFSET_INVALID for not used arrays. + * If "slot_bitwidth_per_channel" is 32 and "sample_bitwidth" is 24, + * "data_align_type" is used to indicate how 24 bit sample data in + * aligning with 32 bit slot width per-channel. + * @values, in byte + */ +} __packed; + +/* ID of the parameter used by #AFE_MODULE_TDM to configure + * the customer TDM header. #AFE_PORT_CMD_SET_PARAM can use this parameter ID. + */ +#define AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG 0x00010298 + +/* Version information used to handle future additions to custom TDM header + * configuration (for backward compatibility). + */ +#define AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG 0x1 + +#define AFE_CUSTOM_TDM_HEADER_TYPE_INVALID 0x0 +#define AFE_CUSTOM_TDM_HEADER_TYPE_DEFAULT 0x1 +#define AFE_CUSTOM_TDM_HEADER_TYPE_ENTERTAINMENT_MOST 0x2 + +#define AFE_CUSTOM_TDM_HEADER_MAX_CNT 0x8 + +/* Payload of the AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG parameter ID */ +struct afe_param_id_custom_tdm_header_cfg { + u32 minor_version; + /* < Minor version used for tracking custom TDM header configuration. + * @values #AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG + */ + + u16 start_offset; + /* < the slot mapping start offset in bytes from this sub-frame + * The bytes is counted from 0. The 0 is mapped to the 1st byte in or + * out of the digital serial data line this sub-frame belong to. + * @values, in byte, + * supported values are 0, 4, 8 + */ + + u16 header_width; + /* < the header width per-frame followed. + * 2 bytes for MOST/TDM case + * @values, in byte + * supported value is 2 + */ + + u16 header_type; + /* < Indicate what kind of custom TDM header it is. + * @values #AFE_CUSTOM_TDM_HEADER_TYPE_INVALID = 0 + * #AFE_CUSTOM_TDM_HEADER_TYPE_DEFAULT = 1 (for AAN channel per MOST) + * #AFE_CUSTOM_TDM_HEADER_TYPE_ENTERTAINMENT_MOST = 2 + * (for entertainment channel, which will overwrite + * AFE_API_VERSION_TDM_SAD_HEADER_TYPE_DEFAULT per MOST) + */ + + u16 num_frame_repeat; + /* < num of header followed. + * @values, supported value is 8 + */ + u16 header[AFE_CUSTOM_TDM_HEADER_MAX_CNT]; + /* < SAD header for MOST/TDM case is followed as payload as below. + * The size of followed SAD header in bytes is num_of_frame_repeat * + * header_width_per_frame, which is 2 * 8 = 16 bytes here. + * the supported payload format is in uint16_t as below + * uint16_t header0; SyncHi 0x3C Info[4] - CodecType -> 0x3C00 + * uint16_t header1; SyncLo 0xB2 Info[5] - SampleWidth -> 0xB218 + * uint16_t header2; DTCP Info Info[6] - unused -> 0x0 + * uint16_t header3; Extension Info[7] - ASAD-Value -> 0xC0 + * uint16_t header4; Reserved Info[0] - Num of bytes following -> 0x7 + * uint16_t header5; Reserved Info[1] - Media Type -> 0x0 + * uint16_t header6; Reserved Info[2] - Bitrate[kbps] - High Byte -> 0x0 + * uint16_t header7; Reserved Info[3] - Bitrate[kbps] - Low Byte -> 0x0 + */ +} __packed; + +struct afe_slot_mapping_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_slot_mapping_cfg slot_mapping; +} __packed; + +struct afe_custom_tdm_header_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_custom_tdm_header_cfg custom_tdm_header; +} __packed; + +struct afe_tdm_port_config { + struct afe_param_id_tdm_cfg tdm; + struct afe_param_id_slot_mapping_cfg slot_mapping; + struct afe_param_id_custom_tdm_header_cfg custom_tdm_header; +} __packed; + +#define AFE_PARAM_ID_DEVICE_HW_DELAY 0x00010243 +#define AFE_API_VERSION_DEVICE_HW_DELAY 0x1 + +struct afe_param_id_device_hw_delay_cfg { + uint32_t device_hw_delay_minor_version; + uint32_t delay_in_us; +} __packed; + +#define AFE_PARAM_ID_SET_TOPOLOGY 0x0001025A +#define AFE_API_VERSION_TOPOLOGY_V1 0x1 + +struct afe_param_id_set_topology_cfg { + /* + * Minor version used for tracking afe topology id configuration. + * @values #AFE_API_VERSION_TOPOLOGY_V1 + */ + u32 minor_version; + /* + * Id of the topology for the afe session. + * @values Any valid AFE topology ID + */ + u32 topology_id; +} __packed; + + +/* + * Generic encoder module ID. + * This module supports the following parameter IDs: + * #AVS_ENCODER_PARAM_ID_ENC_FMT_ID (cannot be set run time) + * #AVS_ENCODER_PARAM_ID_ENC_CFG_BLK (may be set run time) + * #AVS_ENCODER_PARAM_ID_ENC_BITRATE (may be set run time) + * #AVS_ENCODER_PARAM_ID_PACKETIZER_ID (cannot be set run time) + * Opcode - AVS_MODULE_ID_ENCODER + * AFE Command AFE_PORT_CMD_SET_PARAM_V2 supports this module ID. + */ +#define AFE_MODULE_ID_ENCODER 0x00013229 + +/* Macro for defining the packetizer ID: COP. */ +#define AFE_MODULE_ID_PACKETIZER_COP 0x0001322A + +/* + * Packetizer type parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter cannot be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_PACKETIZER_ID 0x0001322E + +/* + * Encoder config block parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter may be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_ENC_CFG_BLK 0x0001322C + +/* + * Encoder format ID parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter cannot be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_ENC_FMT_ID 0x0001322B + +/* + * Data format to send compressed data + * is transmitted/received over Slimbus lines. + */ +#define AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED 0x3 + +/* + * ID for AFE port module. This will be used to define port properties. + * This module supports following parameter IDs: + * #AFE_PARAM_ID_PORT_MEDIA_TYPE + * To configure the port property, the client must use the + * #AFE_PORT_CMD_SET_PARAM_V2 command, + * and fill the module ID with the respective parameter IDs as listed above. + * @apr_hdr_fields + * Opcode -- AFE_MODULE_PORT + */ +#define AFE_MODULE_PORT 0x000102a6 + +/* + * ID of the parameter used by #AFE_MODULE_PORT to set the port media type. + * parameter ID is currently supported using#AFE_PORT_CMD_SET_PARAM_V2 command. + */ +#define AFE_PARAM_ID_PORT_MEDIA_TYPE 0x000102a7 + +/* + * Macros for defining the "data_format" field in the + * #AFE_PARAM_ID_PORT_MEDIA_TYPE + */ +#define AFE_PORT_DATA_FORMAT_PCM 0x0 +#define AFE_PORT_DATA_FORMAT_GENERIC_COMPRESSED 0x1 + +/* + * Macro for defining the "minor_version" field in the + * #AFE_PARAM_ID_PORT_MEDIA_TYPE + */ +#define AFE_API_VERSION_PORT_MEDIA_TYPE 0x1 + +#define ASM_MEDIA_FMT_NONE 0x0 + +/* + * Media format ID for SBC encode configuration. + * @par SBC encode configuration (asm_sbc_enc_cfg_t) + * @table{weak__asm__sbc__enc__cfg__t} + */ +#define ASM_MEDIA_FMT_SBC 0x00010BF2 + +/* SBC channel Mono mode.*/ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_MONO 1 + +/* SBC channel Stereo mode. */ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_STEREO 2 + +/* SBC channel Dual Mono mode. */ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO 8 + +/* SBC channel Joint Stereo mode. */ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO 9 + +/* SBC bit allocation method = loudness. */ +#define ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS 0 + +/* SBC bit allocation method = SNR. */ +#define ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR 1 + + +/* + * Payload of the SBC encoder configuration parameters in the + * #ASM_MEDIA_FMT_SBC media format. + */ +struct asm_sbc_enc_cfg_t { + /* + * Number of subbands. + * @values 4, 8 + */ + uint32_t num_subbands; + + /* + * Size of the encoded block in samples. + * @values 4, 8, 12, 16 + */ + uint32_t blk_len; + + /* + * Mode used to allocate bits between channels. + * @values + * 0 (Native mode) + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_MONO + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_STEREO + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + * If postprocessing outputs one-channel data, Mono mode is used. If + * postprocessing outputs two-channel data, Stereo mode is used. + * The number of channels must not change during encoding. + */ + uint32_t channel_mode; + + /* + * Encoder bit allocation method. + * @values + * #ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS + * #ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR @tablebulletend + */ + uint32_t alloc_method; + + /* + * Number of encoded bits per second. + * @values + * Mono channel -- Maximum of 320 kbps + * Stereo channel -- Maximum of 512 kbps @tablebulletend + */ + uint32_t bit_rate; + + /* + * Number of samples per second. + * @values 0 (Native mode), 16000, 32000, 44100, 48000 Hz + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + uint32_t sample_rate; +}; + +#define ASM_MEDIA_FMT_AAC_AOT_LC 2 +#define ASM_MEDIA_FMT_AAC_AOT_SBR 5 +#define ASM_MEDIA_FMT_AAC_AOT_PS 29 +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS 0 +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW 3 + +struct asm_aac_enc_cfg_v2_t { + + /* Encoding rate in bits per second.*/ + uint32_t bit_rate; + + /* + * Encoding mode. + * Supported values: + * #ASM_MEDIA_FMT_AAC_AOT_LC + * #ASM_MEDIA_FMT_AAC_AOT_SBR + * #ASM_MEDIA_FMT_AAC_AOT_PS + */ + uint32_t enc_mode; + + /* + * AAC format flag. + * Supported values: + * #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + */ + uint16_t aac_fmt_flag; + + /* + * Number of channels to encode. + * Supported values: + * 0 - Native mode + * 1 - Mono + * 2 - Stereo + * Other values are not supported. + * @note1hang The eAAC+ encoder mode supports only stereo. + * Native mode indicates that encoding must be performed with the + * number of channels at the input. + * The number of channels must not change during encoding. + */ + uint16_t channel_cfg; + + /* + * Number of samples per second. + * Supported values: - 0 -- Native mode - For other values, + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + uint32_t sample_rate; +} __packed; + +/* FMT ID for apt-X Classic */ +#define ASM_MEDIA_FMT_APTX 0x000131ff + +/* FMT ID for apt-X HD */ +#define ASM_MEDIA_FMT_APTX_HD 0x00013200 + +#define PCM_CHANNEL_L 1 +#define PCM_CHANNEL_R 2 +#define PCM_CHANNEL_C 3 + +struct asm_custom_enc_cfg_aptx_t { + uint32_t sample_rate; + /* Mono or stereo */ + uint16_t num_channels; + uint16_t reserved; + /* num_ch == 1, then PCM_CHANNEL_C, + * num_ch == 2, then {PCM_CHANNEL_L, PCM_CHANNEL_R} + */ + uint8_t channel_mapping[8]; + uint32_t custom_size; +} __packed; + +struct afe_enc_fmt_id_param_t { + /* + * Supported values: + * #ASM_MEDIA_FMT_SBC + * #ASM_MEDIA_FMT_AAC_V2 + * Any OpenDSP supported values + */ + uint32_t fmt_id; +} __packed; + +struct afe_port_media_type_t { + /* + * Minor version + * @values #AFE_API_VERSION_PORT_MEDIA_TYPE. + */ + uint32_t minor_version; + + /* + * Sampling rate of the port. + * @values + * #AFE_PORT_SAMPLE_RATE_8K + * #AFE_PORT_SAMPLE_RATE_11_025K + * #AFE_PORT_SAMPLE_RATE_12K + * #AFE_PORT_SAMPLE_RATE_16K + * #AFE_PORT_SAMPLE_RATE_22_05K + * #AFE_PORT_SAMPLE_RATE_24K + * #AFE_PORT_SAMPLE_RATE_32K + * #AFE_PORT_SAMPLE_RATE_44_1K + * #AFE_PORT_SAMPLE_RATE_48K + * #AFE_PORT_SAMPLE_RATE_88_2K + * #AFE_PORT_SAMPLE_RATE_96K + * #AFE_PORT_SAMPLE_RATE_176_4K + * #AFE_PORT_SAMPLE_RATE_192K + * #AFE_PORT_SAMPLE_RATE_352_8K + * #AFE_PORT_SAMPLE_RATE_384K + */ + uint32_t sample_rate; + + /* + * Bit width of the sample. + * @values 16, 24 + */ + uint16_t bit_width; + + /* + * Number of channels. + * @values 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + uint16_t num_channels; + + /* + * Data format supported by this port. + * If the port media type and device media type are different, + * it signifies a encoding/decoding use case + * @values + * #AFE_PORT_DATA_FORMAT_PCM + * #AFE_PORT_DATA_FORMAT_GENERIC_COMPRESSED + */ + uint16_t data_format; + + /*This field must be set to zero.*/ + uint16_t reserved; +} __packed; + +union afe_enc_config_data { + struct asm_sbc_enc_cfg_t sbc_config; + struct asm_aac_enc_cfg_v2_t aac_config; + struct asm_custom_enc_cfg_aptx_t aptx_config; +}; + +struct afe_enc_config { + u32 format; + union afe_enc_config_data data; +}; + +struct afe_enc_cfg_blk_param_t { + uint32_t enc_cfg_blk_size; + /* + *Size of the encoder configuration block that follows this member + */ + union afe_enc_config_data enc_blk_config; +}; + +/* + * Payload of the AVS_ENCODER_PARAM_ID_PACKETIZER_ID parameter. + */ +struct avs_enc_packetizer_id_param_t { + /* + * Supported values: + * #AVS_MODULE_ID_PACKETIZER_COP + * Any OpenDSP supported values + */ + uint32_t enc_packetizer_id; +}; + +union afe_port_config { + struct afe_param_id_pcm_cfg pcm; + struct afe_param_id_i2s_cfg i2s; + struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; + struct afe_param_id_slimbus_cfg slim_sch; + struct afe_param_id_rt_proxy_port_cfg rtproxy; + struct afe_param_id_internal_bt_fm_cfg int_bt_fm; + struct afe_param_id_pseudo_port_cfg pseudo_port; + struct afe_param_id_device_hw_delay_cfg hw_delay; + struct afe_param_id_spdif_cfg spdif; + struct afe_param_id_set_topology_cfg topology; + struct afe_param_id_tdm_cfg tdm; + struct afe_param_id_usb_audio_cfg usb_audio; + struct afe_enc_fmt_id_param_t enc_fmt; + struct afe_port_media_type_t media_type; + struct afe_enc_cfg_blk_param_t enc_blk_param; + struct avs_enc_packetizer_id_param_t enc_pkt_id_param; +} __packed; + +struct afe_audioif_config_command_no_payload { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; +} __packed; + +struct afe_audioif_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + union afe_port_config port; +} __packed; + +#define AFE_PORT_CMD_DEVICE_START 0x000100E5 + +/* Payload of the #AFE_PORT_CMD_DEVICE_START.*/ +struct afe_port_cmd_device_start { + struct apr_hdr hdr; + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. An even + * number represents the Rx direction, and an odd number represents + * the Tx direction. + */ + + + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0.*/ + +} __packed; + +#define AFE_PORT_CMD_DEVICE_STOP 0x000100E6 + +/* Payload of the #AFE_PORT_CMD_DEVICE_STOP. */ +struct afe_port_cmd_device_stop { + struct apr_hdr hdr; + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. An even + * number represents the Rx direction, and an odd number represents + * the Tx direction. + */ + + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0.*/ +} __packed; + +#define AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS 0x000100EA + +/* Memory map regions command payload used by the + * #AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS . + * This structure allows clients to map multiple shared memory + * regions in a single command. Following this structure are + * num_regions of afe_service_shared_map_region_payload. + */ +struct afe_service_cmd_shared_mem_map_regions { + struct apr_hdr hdr; +u16 mem_pool_id; +/* Type of memory on which this memory region is mapped. + * Supported values: + * - #ADSP_MEMORY_MAP_EBI_POOL + * - #ADSP_MEMORY_MAP_SMI_POOL + * - #ADSP_MEMORY_MAP_SHMEM8_4K_POOL + * - Other values are reserved + * + * The memory pool ID implicitly defines the characteristics of the + * memory. Characteristics may include alignment type, permissions, + * etc. + * + * ADSP_MEMORY_MAP_EBI_POOL is External Buffer Interface type memory + * ADSP_MEMORY_MAP_SMI_POOL is Shared Memory Interface type memory + * ADSP_MEMORY_MAP_SHMEM8_4K_POOL is shared memory, byte + * addressable, and 4 KB aligned. + */ + + + u16 num_regions; +/* Number of regions to map. + * Supported values: + * - Any value greater than zero + */ + + u32 property_flag; +/* Configures one common property for all the regions in the + * payload. + * + * Supported values: - 0x00000000 to 0x00000001 + * + * b0 - bit 0 indicates physical or virtual mapping 0 Shared memory + * address provided in afe_service_shared_map_region_payloadis a + * physical address. The shared memory needs to be mapped( hardware + * TLB entry) and a software entry needs to be added for internal + * book keeping. + * + * 1 Shared memory address provided in + * afe_service_shared_map_region_payloadis a virtual address. The + * shared memory must not be mapped (since hardware TLB entry is + * already available) but a software entry needs to be added for + * internal book keeping. This can be useful if two services with in + * ADSP is communicating via APR. They can now directly communicate + * via the Virtual address instead of Physical address. The virtual + * regions must be contiguous. num_regions must be 1 in this case. + * + * b31-b1 - reserved bits. must be set to zero + */ + + +} __packed; +/* Map region payload used by the + * afe_service_shared_map_region_payloadstructure. + */ +struct afe_service_shared_map_region_payload { + u32 shm_addr_lsw; +/* least significant word of starting address in the memory + * region to map. It must be contiguous memory, and it must be 4 KB + * aligned. + * Supported values: - Any 32 bit value + */ + + + u32 shm_addr_msw; +/* most significant word of startng address in the memory region + * to map. For 32 bit shared memory address, this field must be set + * to zero. For 36 bit shared memory address, bit31 to bit 4 must be + * set to zero + * + * Supported values: - For 32 bit shared memory address, this field + * must be set to zero. - For 36 bit shared memory address, bit31 to + * bit 4 must be set to zero - For 64 bit shared memory address, any + * 32 bit value + */ + + + u32 mem_size_bytes; +/* Number of bytes in the region. The aDSP will always map the + * regions as virtual contiguous memory, but the memory size must be + * in multiples of 4 KB to avoid gaps in the virtually contiguous + * mapped memory. + * + * Supported values: - multiples of 4KB + */ + +} __packed; + +#define AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS 0x000100EB +struct afe_service_cmdrsp_shared_mem_map_regions { + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory attributes is + * returned iff AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS command is + * successful. In the case of failure , a generic APR error response + * is returned to the client. + * + * Supported Values: - Any 32 bit value + */ + +} __packed; +#define AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS 0x000100EC +/* Memory unmap regions command payload used by the + * #AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS + * + * This structure allows clients to unmap multiple shared memory + * regions in a single command. + */ + + +struct afe_service_cmd_shared_mem_unmap_regions { + struct apr_hdr hdr; +u32 mem_map_handle; +/* memory map handle returned by + * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands + * + * Supported Values: + * - Any 32 bit value + */ +} __packed; + +#define AFE_PORT_CMD_GET_PARAM_V2 0x000100F0 + +/* Payload of the #AFE_PORT_CMD_GET_PARAM_V2 command, + * which queries for one post/preprocessing parameter of a + * stream. + */ +struct afe_port_cmd_get_param_v2 { + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. */ + + u16 payload_size; +/* Maximum data size of the parameter ID/module ID combination. + * This is a multiple of four bytes + * Supported values: > 0 + */ + + u32 payload_address_lsw; +/* LSW of 64 bit Payload address. Address should be 32-byte, + * 4kbyte aligned and must be contig memory. + */ + + + u32 payload_address_msw; +/* MSW of 64 bit Payload address. In case of 32-bit shared + * memory address, this field must be set to zero. In case of 36-bit + * shared memory address, bit-4 to bit-31 must be set to zero. + * Address should be 32-byte, 4kbyte aligned and must be contiguous + * memory. + */ + + u32 mem_map_handle; +/* Memory map handle returned by + * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands. + * Supported Values: - NULL -- Message. The parameter data is + * in-band. - Non-NULL -- The parameter data is Out-band.Pointer to + * - the physical address in shared memory of the payload data. + * For detailed payload content, see the afe_port_param_data_v2 + * structure + */ + + + u32 module_id; +/* ID of the module to be queried. + * Supported values: Valid module ID + */ + + u32 param_id; +/* ID of the parameter to be queried. + * Supported values: Valid parameter ID + */ +} __packed; + +#define AFE_PORT_CMDRSP_GET_PARAM_V2 0x00010106 + +/* Payload of the #AFE_PORT_CMDRSP_GET_PARAM_V2 message, which + * responds to an #AFE_PORT_CMD_GET_PARAM_V2 command. + * + * Immediately following this structure is the parameters structure + * (afe_port_param_data) containing the response(acknowledgment) + * parameter payload. This payload is included for an in-band + * scenario. For an address/shared memory-based set parameter, this + * payload is not needed. + */ + + +struct afe_port_cmdrsp_get_param_v2 { + u32 status; +} __packed; + +#define AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG 0x0001028C +#define AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG 0x1 + +/* Payload of the AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_lpass_core_shared_clk_cfg { + u32 lpass_core_shared_clk_cfg_minor_version; +/* + * Minor version used for lpass core shared clock configuration + * Supported value: AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG + */ + u32 enable; +/* + * Specifies whether the lpass core shared clock is + * enabled (1) or disabled (0). + */ +} __packed; + +struct afe_lpass_core_shared_clk_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_lpass_core_shared_clk_cfg clk_cfg; +} __packed; + +/* adsp_afe_service_commands.h */ + +#define ADSP_MEMORY_MAP_EBI_POOL 0 + +#define ADSP_MEMORY_MAP_SMI_POOL 1 +#define ADSP_MEMORY_MAP_IMEM_POOL 2 +#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 + +/* Definition of virtual memory flag */ +#define ADSP_MEMORY_MAP_VIRTUAL_MEMORY 1 + +/* Definition of physical memory flag */ +#define ADSP_MEMORY_MAP_PHYSICAL_MEMORY 0 + +#define NULL_POPP_TOPOLOGY 0x00010C68 +#define NULL_COPP_TOPOLOGY 0x00010312 +#define DEFAULT_COPP_TOPOLOGY 0x00010314 +#define DEFAULT_POPP_TOPOLOGY 0x00010BE4 +#define COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY 0x0001076B +#define COMPRESS_PASSTHROUGH_NONE_TOPOLOGY 0x00010774 +#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71 +#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72 +#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75 +#define VPM_TX_DM_RFECNS_COPP_TOPOLOGY 0x00010F86 +#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX 0x10015002 +#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE 0x10028000 + +/* Memory map regions command payload used by the + * #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS + * commands. + * + * This structure allows clients to map multiple shared memory + * regions in a single command. Following this structure are + * num_regions of avs_shared_map_region_payload. + */ + + +struct avs_cmd_shared_mem_map_regions { + struct apr_hdr hdr; + u16 mem_pool_id; +/* Type of memory on which this memory region is mapped. + * + * Supported values: - #ADSP_MEMORY_MAP_EBI_POOL - + * #ADSP_MEMORY_MAP_SMI_POOL - #ADSP_MEMORY_MAP_IMEM_POOL + * (unsupported) - #ADSP_MEMORY_MAP_SHMEM8_4K_POOL - Other values + * are reserved + * + * The memory ID implicitly defines the characteristics of the + * memory. Characteristics may include alignment type, permissions, + * etc. + * + * SHMEM8_4K is shared memory, byte addressable, and 4 KB aligned. + */ + + + u16 num_regions; + /* Number of regions to map.*/ + + u32 property_flag; +/* Configures one common property for all the regions in the + * payload. No two regions in the same memory map regions cmd can + * have differnt property. Supported values: - 0x00000000 to + * 0x00000001 + * + * b0 - bit 0 indicates physical or virtual mapping 0 shared memory + * address provided in avs_shared_map_regions_payload is physical + * address. The shared memory needs to be mapped( hardware TLB + * entry) + * + * and a software entry needs to be added for internal book keeping. + * + * 1 Shared memory address provided in MayPayload[usRegions] is + * virtual address. The shared memory must not be mapped (since + * hardware TLB entry is already available) but a software entry + * needs to be added for internal book keeping. This can be useful + * if two services with in ADSP is communicating via APR. They can + * now directly communicate via the Virtual address instead of + * Physical address. The virtual regions must be contiguous. + * + * b31-b1 - reserved bits. must be set to zero + */ + +} __packed; + +struct avs_shared_map_region_payload { + u32 shm_addr_lsw; +/* least significant word of shared memory address of the memory + * region to map. It must be contiguous memory, and it must be 4 KB + * aligned. + */ + + u32 shm_addr_msw; +/* most significant word of shared memory address of the memory + * region to map. For 32 bit shared memory address, this field must + * tbe set to zero. For 36 bit shared memory address, bit31 to bit 4 + * must be set to zero + */ + + u32 mem_size_bytes; +/* Number of bytes in the region. + * + * The aDSP will always map the regions as virtual contiguous + * memory, but the memory size must be in multiples of 4 KB to avoid + * gaps in the virtually contiguous mapped memory. + */ + +} __packed; + +struct avs_cmd_shared_mem_unmap_regions { + struct apr_hdr hdr; + u32 mem_map_handle; +/* memory map handle returned by ASM_CMD_SHARED_MEM_MAP_REGIONS + * , ADM_CMD_SHARED_MEM_MAP_REGIONS, commands + */ + +} __packed; + +/* Memory map command response payload used by the + * #ASM_CMDRSP_SHARED_MEM_MAP_REGIONS + * ,#ADM_CMDRSP_SHARED_MEM_MAP_REGIONS + */ + + +struct avs_cmdrsp_shared_mem_map_regions { + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory attributes is + * returned + */ + +} __packed; + +/*adsp_audio_memmap_api.h*/ + +/* ASM related data structures */ +struct asm_wma_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +} __packed; + +struct asm_wmapro_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +} __packed; + +struct asm_aac_cfg { + u16 format; + u16 aot; + u16 ep_config; + u16 section_data_resilience; + u16 scalefactor_data_resilience; + u16 spectral_data_resilience; + u16 ch_cfg; + u16 reserved; + u32 sample_rate; +} __packed; + +struct asm_amrwbplus_cfg { + u32 size_bytes; + u32 version; + u32 num_channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_fmt; + u32 amr_lsf_idx; +} __packed; + +struct asm_flac_cfg { + u32 sample_rate; + u32 ext_sample_rate; + u32 min_frame_size; + u32 max_frame_size; + u16 stream_info_present; + u16 min_blk_size; + u16 max_blk_size; + u16 ch_cfg; + u16 sample_size; + u16 md5_sum; +}; + +struct asm_alac_cfg { + u32 frame_length; + u8 compatible_version; + u8 bit_depth; + u8 pb; + u8 mb; + u8 kb; + u8 num_channels; + u16 max_run; + u32 max_frame_bytes; + u32 avg_bit_rate; + u32 sample_rate; + u32 channel_layout_tag; +}; + +struct asm_g711_dec_cfg { + u32 sample_rate; +}; + +struct asm_vorbis_cfg { + u32 bit_stream_fmt; +}; + +struct asm_ape_cfg { + u16 compatible_version; + u16 compression_level; + u32 format_flags; + u32 blocks_per_frame; + u32 final_frame_blocks; + u32 total_frames; + u16 bits_per_sample; + u16 num_channels; + u32 sample_rate; + u32 seek_table_present; +}; + +struct asm_dsd_cfg { + u16 num_version; + u16 is_bitwise_big_endian; + u16 dsd_channel_block_size; + u16 num_channels; + u8 channel_mapping[8]; + u32 dsd_data_rate; +}; + +struct asm_softpause_params { + u32 enable; + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +struct asm_softvolume_params { + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +#define ASM_END_POINT_DEVICE_MATRIX 0 + +#define PCM_CHANNEL_NULL 0 + +/* Front left channel. */ +#define PCM_CHANNEL_FL 1 + +/* Front right channel. */ +#define PCM_CHANNEL_FR 2 + +/* Front center channel. */ +#define PCM_CHANNEL_FC 3 + +/* Left surround channel.*/ +#define PCM_CHANNEL_LS 4 + +/* Right surround channel.*/ +#define PCM_CHANNEL_RS 5 + +/* Low frequency effect channel. */ +#define PCM_CHANNEL_LFE 6 + +/* Center surround channel; Rear center channel. */ +#define PCM_CHANNEL_CS 7 + +/* Left back channel; Rear left channel. */ +#define PCM_CHANNEL_LB 8 + +/* Right back channel; Rear right channel. */ +#define PCM_CHANNEL_RB 9 + +/* Top surround channel. */ +#define PCM_CHANNELS 10 + +/* Center vertical height channel.*/ +#define PCM_CHANNEL_CVH 11 + +/* Mono surround channel.*/ +#define PCM_CHANNEL_MS 12 + +/* Front left of center. */ +#define PCM_CHANNEL_FLC 13 + +/* Front right of center. */ +#define PCM_CHANNEL_FRC 14 + +/* Rear left of center. */ +#define PCM_CHANNEL_RLC 15 + +/* Rear right of center. */ +#define PCM_CHANNEL_RRC 16 + +#define PCM_FORMAT_MAX_NUM_CHANNEL 8 + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 0x00010DDC + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 0x0001320C + +#define ASM_MEDIA_FMT_EVRCB_FS 0x00010BEF + +#define ASM_MEDIA_FMT_EVRCWB_FS 0x00010BF0 + +#define ASM_MAX_EQ_BANDS 12 + +#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98 + +struct asm_data_cmd_media_fmt_update_v2 { +u32 fmt_blk_size; + /* Media format block size in bytes.*/ +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + + u16 num_channels; + /* Number of channels. Supported values: 1 to 8 */ + u16 bits_per_sample; +/* Number of bits per sample per channel. * Supported values: + * 16, 24 * When used for playback, the client must send 24-bit + * samples packed in 32-bit words. The 24-bit samples must be placed + * in the most significant 24 bits of the 32-bit word. When used for + * recording, the aDSP sends 24-bit samples packed in 32-bit words. + * The 24-bit samples are placed in the most significant 24 bits of + * the 32-bit word. + */ + + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * Supported values: 2000 to 48000 + */ + + u16 is_signed; + /* Flag that indicates the samples are signed (1). */ + + u16 reserved; + /* reserved field for 32 bit alignment. must be set to zero. */ + + u8 channel_mapping[8]; +/* Channel array of size 8. + * Supported values: + * - #PCM_CHANNEL_L + * - #PCM_CHANNEL_R + * - #PCM_CHANNEL_C + * - #PCM_CHANNEL_LS + * - #PCM_CHANNEL_RS + * - #PCM_CHANNEL_LFE + * - #PCM_CHANNEL_CS + * - #PCM_CHANNEL_LB + * - #PCM_CHANNEL_RB + * - #PCM_CHANNELS + * - #PCM_CHANNEL_CVH + * - #PCM_CHANNEL_MS + * - #PCM_CHANNEL_FLC + * - #PCM_CHANNEL_FRC + * - #PCM_CHANNEL_RLC + * - #PCM_CHANNEL_RRC + * + * Channel[i] mapping describes channel I. Each element i of the + * array describes channel I inside the buffer where 0 @le I < + * num_channels. An unused channel is set to zero. + */ +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v3 { + uint16_t num_channels; +/* + * Number of channels + * Supported values: 1 to 8 + */ + + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel + * Supported values: 16, 24 + */ + + uint32_t sample_rate; +/* + * Number of samples per second + * Supported values: 2000 to 48000, 96000,192000 Hz + */ + + uint16_t is_signed; +/* Flag that indicates that PCM samples are signed (1) */ + + uint16_t sample_word_size; +/* + * Size in bits of the word that holds a sample of a channel. + * Supported values: 12,24,32 + */ + + uint8_t channel_mapping[8]; +/* + * Each element, i, in the array describes channel i inside the buffer where + * 0 <= i < num_channels. Unused channels are set to 0. + */ +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v4 { + uint16_t num_channels; +/* + * Number of channels + * Supported values: 1 to 8 + */ + + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel + * Supported values: 16, 24, 32 + */ + + uint32_t sample_rate; +/* + * Number of samples per second + * Supported values: 2000 to 48000, 96000,192000 Hz + */ + + uint16_t is_signed; +/* Flag that indicates that PCM samples are signed (1) */ + + uint16_t sample_word_size; +/* + * Size in bits of the word that holds a sample of a channel. + * Supported values: 12,24,32 + */ + + uint8_t channel_mapping[8]; +/* + * Each element, i, in the array describes channel i inside the buffer where + * 0 <= i < num_channels. Unused channels are set to 0. + */ + uint16_t endianness; +/* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; +/* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ +} __packed; + +/* + * Payload of the multichannel PCM configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format. + */ +struct asm_multi_channel_pcm_fmt_blk_param_v3 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + struct asm_multi_channel_pcm_fmt_blk_v3 param; +} __packed; + +/* + * Payload of the multichannel PCM configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format. + */ +struct asm_multi_channel_pcm_fmt_blk_param_v4 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + struct asm_multi_channel_pcm_fmt_blk_v4 param; +} __packed; + +struct asm_stream_cmd_set_encdec_param { + u32 param_id; + /* ID of the parameter. */ + + u32 param_size; +/* Data size of this parameter, in bytes. The size is a multiple + * of 4 bytes. + */ + +} __packed; + +struct asm_enc_cfg_blk_param_v2 { + u32 frames_per_buf; +/* Number of encoded frames to pack into each buffer. + * + * @note1hang This is only guidance information for the aDSP. The + * number of encoded frames put into each buffer (specified by the + * client) is less than or equal to this number. + */ + + u32 enc_cfg_blk_size; +/* Size in bytes of the encoder configuration block that follows + * this member. + */ + +} __packed; + +/* @brief Dolby Digital Plus end point configuration structure + */ +struct asm_dec_ddp_endp_param_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + int endp_param_value; +} __packed; + +/* + * Payload of the multichannel PCM encoder configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format. + */ + +struct asm_multi_channel_pcm_enc_cfg_v4 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; + /* + * Number of PCM channels. + * @values + * - 0 -- Native mode + * - 1 -- 8 channels + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + */ + uint16_t bits_per_sample; + /* + * Number of bits per sample per channel. + * @values 16, 24 + */ + uint32_t sample_rate; + /* + * Number of samples per second. + * @values 0, 8000 to 48000 Hz + * A value of 0 indicates the native sampling rate. Encoding is + * performed at the input sampling rate. + */ + uint16_t is_signed; + /* + * Flag that indicates the PCM samples are signed (1). Currently, only + * signed PCM samples are supported. + */ + uint16_t sample_word_size; + /* + * The size in bits of the word that holds a sample of a channel. + * @values 16, 24, 32 + * 16-bit samples are always placed in 16-bit words: + * sample_word_size = 1. + * 24-bit samples can be placed in 32-bit words or in consecutive + * 24-bit words. + * - If sample_word_size = 32, 24-bit samples are placed in the + * most significant 24 bits of a 32-bit word. + * - If sample_word_size = 24, 24-bit samples are placed in + * 24-bit words. @tablebulletend + */ + uint8_t channel_mapping[8]; + /* + * Channel mapping array expected at the encoder output. + * Channel[i] mapping describes channel i inside the buffer, where + * 0 @le i < num_channels. All valid used channels must be present at + * the beginning of the array. + * If Native mode is set for the channels, this field is ignored. + * @values See Section @xref{dox:PcmChannelDefs} + */ + uint16_t endianness; + /* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; + /* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ +} __packed; + +/* + * Payload of the multichannel PCM encoder configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format. + */ + +struct asm_multi_channel_pcm_enc_cfg_v3 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; + /* + * Number of PCM channels. + * @values + * - 0 -- Native mode + * - 1 -- 8 channels + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + */ + uint16_t bits_per_sample; + /* + * Number of bits per sample per channel. + * @values 16, 24 + */ + uint32_t sample_rate; + /* + * Number of samples per second. + * @values 0, 8000 to 48000 Hz + * A value of 0 indicates the native sampling rate. Encoding is + * performed at the input sampling rate. + */ + uint16_t is_signed; + /* + * Flag that indicates the PCM samples are signed (1). Currently, only + * signed PCM samples are supported. + */ + uint16_t sample_word_size; + /* + * The size in bits of the word that holds a sample of a channel. + * @values 16, 24, 32 + * 16-bit samples are always placed in 16-bit words: + * sample_word_size = 1. + * 24-bit samples can be placed in 32-bit words or in consecutive + * 24-bit words. + * - If sample_word_size = 32, 24-bit samples are placed in the + * most significant 24 bits of a 32-bit word. + * - If sample_word_size = 24, 24-bit samples are placed in + * 24-bit words. @tablebulletend + */ + uint8_t channel_mapping[8]; + /* + * Channel mapping array expected at the encoder output. + * Channel[i] mapping describes channel i inside the buffer, where + * 0 @le i < num_channels. All valid used channels must be present at + * the beginning of the array. + * If Native mode is set for the channels, this field is ignored. + * @values See Section @xref{dox:PcmChannelDefs} + */ +}; + +/* @brief Multichannel PCM encoder configuration structure used + * in the #ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 command. + */ + +struct asm_multi_channel_pcm_enc_cfg_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; +/*< Number of PCM channels. + * + * Supported values: - 0 -- Native mode - 1 -- 8 Native mode + * indicates that encoding must be performed with the number of + * channels at the input. + */ + + uint16_t bits_per_sample; +/*< Number of bits per sample per channel. + * Supported values: 16, 24 + */ + + uint32_t sample_rate; +/*< Number of samples per second (in Hertz). + * + * Supported values: 0, 8000 to 48000 A value of 0 indicates the + * native sampling rate. Encoding is performed at the input sampling + * rate. + */ + + uint16_t is_signed; +/*< Specifies whether the samples are signed (1). Currently, + * only signed samples are supported. + */ + + uint16_t reserved; +/*< reserved field for 32 bit alignment. must be set to zero.*/ + + + uint8_t channel_mapping[8]; +} __packed; + +#define ASM_MEDIA_FMT_MP3 0x00010BE9 +#define ASM_MEDIA_FMT_AAC_V2 0x00010DA6 + +/* @xreflabel + * {hdr:AsmMediaFmtDolbyAac} Media format ID for the + * Dolby AAC decoder. This format ID is be used if the client wants + * to use the Dolby AAC decoder to decode MPEG2 and MPEG4 AAC + * contents. + */ + +#define ASM_MEDIA_FMT_DOLBY_AAC 0x00010D86 + +/* Enumeration for the audio data transport stream AAC format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS 0 + +/* Enumeration for low overhead audio stream AAC format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS 1 + +/* Enumeration for the audio data interchange format + * AAC format. + */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF 2 + +/* Enumeration for the raw AAC format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW 3 + +#define ASM_MEDIA_FMT_AAC_AOT_LC 2 +#define ASM_MEDIA_FMT_AAC_AOT_SBR 5 +#define ASM_MEDIA_FMT_AAC_AOT_PS 29 +#define ASM_MEDIA_FMT_AAC_AOT_BSAC 22 + +struct asm_aac_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + + u16 aac_fmt_flag; +/* Bitstream format option. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + */ + + u16 audio_objype; +/* Audio Object Type (AOT) present in the AAC stream. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_AOT_LC + * - #ASM_MEDIA_FMT_AAC_AOT_SBR + * - #ASM_MEDIA_FMT_AAC_AOT_BSAC + * - #ASM_MEDIA_FMT_AAC_AOT_PS + * - Otherwise -- Not supported + */ + + u16 channel_config; +/* Number of channels present in the AAC stream. + * Supported values: + * - 1 -- Mono + * - 2 -- Stereo + * - 6 -- 5.1 content + */ + + u16 total_size_of_PCE_bits; +/* greater or equal to zero. * -In case of RAW formats and + * channel config = 0 (PCE), client can send * the bit stream + * containing PCE immediately following this structure * (in-band). + * -This number does not include bits included for 32 bit alignment. + * -If zero, then the PCE info is assumed to be available in the + * audio -bit stream & not in-band. + */ + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * + * Supported values: 8000, 11025, 12000, 16000, 22050, 24000, 32000, + * 44100, 48000 + * + * This field must be equal to the sample rate of the AAC-LC + * decoder's output. - For MP4 or 3GP containers, this is indicated + * by the samplingFrequencyIndex field in the AudioSpecificConfig + * element. - For ADTS format, this is indicated by the + * samplingFrequencyIndex in the ADTS fixed header. - For ADIF + * format, this is indicated by the samplingFrequencyIndex in the + * program_config_element present in the ADIF header. + */ + +} __packed; + +struct asm_aac_enc_cfg_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u32 bit_rate; + /* Encoding rate in bits per second. */ + u32 enc_mode; +/* Encoding mode. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_AOT_LC + * - #ASM_MEDIA_FMT_AAC_AOT_SBR + * - #ASM_MEDIA_FMT_AAC_AOT_PS + */ + u16 aac_fmt_flag; +/* AAC format flag. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + */ + u16 channel_cfg; +/* Number of channels to encode. + * Supported values: + * - 0 -- Native mode + * - 1 -- Mono + * - 2 -- Stereo + * - Other values are not supported. + * @note1hang The eAAC+ encoder mode supports only stereo. + * Native mode indicates that encoding must be performed with the + * number of channels at the input. + * The number of channels must not change during encoding. + */ + + u32 sample_rate; +/* Number of samples per second. + * Supported values: - 0 -- Native mode - For other values, + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + +} __packed; + +#define ASM_MEDIA_FMT_G711_ALAW_FS 0x00010BF7 +#define ASM_MEDIA_FMT_G711_MLAW_FS 0x00010C2E + +struct asm_g711_enc_cfg_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u32 sample_rate; +/* + * Number of samples per second. + * Supported values: 8000, 16000 Hz + */ + +} __packed; + +struct asm_vorbis_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 bit_stream_fmt; +/* Bit stream format. + * Supported values: + * - 0 -- Raw bitstream + * - 1 -- Transcoded bitstream + * + * Transcoded bitstream containing the size of the frame as the first + * word in each frame. + */ + +} __packed; + +struct asm_flac_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 is_stream_info_present; +/* Specifies whether stream information is present in the FLAC format + * block. + * + * Supported values: + * - 0 -- Stream information is not present in this message + * - 1 -- Stream information is present in this message + * + * When set to 1, the FLAC bitstream was successfully parsed by the + * client, and other fields in the FLAC format block can be read by the + * decoder to get metadata stream information. + */ + + u16 num_channels; +/* Number of channels for decoding. + * Supported values: 1 to 2 + */ + + u16 min_blk_size; +/* Minimum block size (in samples) used in the stream. It must be less + * than or equal to max_blk_size. + */ + + u16 max_blk_size; +/* Maximum block size (in samples) used in the stream. If the + * minimum block size equals the maximum block size, a fixed block + * size stream is implied. + */ + + u16 md5_sum[8]; +/* MD5 signature array of the unencoded audio data. This allows the + * decoder to determine if an error exists in the audio data, even when + * the error does not result in an invalid bitstream. + */ + + u32 sample_rate; +/* Number of samples per second. + * Supported values: 8000 to 48000 Hz + */ + + u32 min_frame_size; +/* Minimum frame size used in the stream. + * Supported values: + * - > 0 bytes + * - 0 -- The value is unknown + */ + + u32 max_frame_size; +/* Maximum frame size used in the stream. + * Supported values: + * -- > 0 bytes + * -- 0 . The value is unknown + */ + + u16 sample_size; +/* Bits per sample.Supported values: 8, 16 */ + + u16 reserved; +/* Clients must set this field to zero + */ + +} __packed; + +struct asm_alac_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u32 frame_length; + u8 compatible_version; + u8 bit_depth; + u8 pb; + u8 mb; + u8 kb; + u8 num_channels; + u16 max_run; + u32 max_frame_bytes; + u32 avg_bit_rate; + u32 sample_rate; + u32 channel_layout_tag; + +} __packed; + +struct asm_g711_dec_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 sample_rate; +} __packed; + +struct asm_ape_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 compatible_version; + u16 compression_level; + u32 format_flags; + u32 blocks_per_frame; + u32 final_frame_blocks; + u32 total_frames; + u16 bits_per_sample; + u16 num_channels; + u32 sample_rate; + u32 seek_table_present; + +} __packed; + +struct asm_dsd_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 num_version; + u16 is_bitwise_big_endian; + u16 dsd_channel_block_size; + u16 num_channels; + u8 channel_mapping[8]; + u32 dsd_data_rate; + +} __packed; + +#define ASM_MEDIA_FMT_AMRNB_FS 0x00010BEB + +/* Enumeration for 4.75 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MR475 0 + +/* Enumeration for 5.15 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MR515 1 + +/* Enumeration for 5.90 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR59 2 + +/* Enumeration for 6.70 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR67 3 + +/* Enumeration for 7.40 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR74 4 + +/* Enumeration for 7.95 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR795 5 + +/* Enumeration for 10.20 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR102 6 + +/* Enumeration for 12.20 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR122 7 + +/* Enumeration for AMR-NB Discontinuous Transmission mode off. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF 0 + +/* Enumeration for AMR-NB DTX mode VAD1. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1 1 + +/* Enumeration for AMR-NB DTX mode VAD2. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD2 2 + +/* Enumeration for AMR-NB DTX mode auto. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_AUTO 3 + +struct asm_amrnb_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u16 enc_mode; +/* AMR-NB encoding rate. + * Supported values: + * Use the ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_* + * macros + */ + + u16 dtx_mode; +/* Specifies whether DTX mode is disabled or enabled. + * Supported values: + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1 + */ +} __packed; + +#define ASM_MEDIA_FMT_AMRWB_FS 0x00010BEC + +/* Enumeration for 6.6 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR66 0 + +/* Enumeration for 8.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR885 1 + +/* Enumeration for 12.65 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1265 2 + +/* Enumeration for 14.25 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1425 3 + +/* Enumeration for 15.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1585 4 + +/* Enumeration for 18.25 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1825 5 + +/* Enumeration for 19.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1985 6 + +/* Enumeration for 23.05 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR2305 7 + +/* Enumeration for 23.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR2385 8 + +struct asm_amrwb_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u16 enc_mode; +/* AMR-WB encoding rate. + * Suupported values: + * Use the ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_* + * macros + */ + + u16 dtx_mode; +/* Specifies whether DTX mode is disabled or enabled. + * Supported values: + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1 + */ +} __packed; + +#define ASM_MEDIA_FMT_V13K_FS 0x00010BED + +/* Enumeration for 14.4 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1440 0 + +/* Enumeration for 12.2 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1220 1 + +/* Enumeration for 11.2 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1120 2 + +/* Enumeration for 9.0 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR90 3 + +/* Enumeration for 7.2 kbps V13K eEncoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR720 4 + +/* Enumeration for 1/8 vocoder rate.*/ +#define ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE 1 + +/* Enumeration for 1/4 vocoder rate. */ +#define ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE 2 + +/* Enumeration for 1/2 vocoder rate. */ +#define ASM_MEDIA_FMT_VOC_HALF_RATE 3 + +/* Enumeration for full vocoder rate. */ +#define ASM_MEDIA_FMT_VOC_FULL_RATE 4 + +struct asm_v13k_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u16 max_rate; +/* Maximum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 min_rate; +/* Minimum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 reduced_rate_cmd; +/* Reduced rate command, used to change + * the average bitrate of the V13K + * vocoder. + * Supported values: + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1440 (Default) + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1220 + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1120 + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR90 + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR720 + */ + + u16 rate_mod_cmd; +/* Rate modulation command. Default = 0. + *- If bit 0=1, rate control is enabled. + *- If bit 1=1, the maximum number of consecutive full rate + * frames is limited with numbers supplied in + * bits 2 to 10. + *- If bit 1=0, the minimum number of non-full rate frames + * in between two full rate frames is forced to + * the number supplied in bits 2 to 10. In both cases, if necessary, + * half rate is used to substitute full rate. - Bits 15 to 10 are + * reserved and must all be set to zero. + */ + +} __packed; + +#define ASM_MEDIA_FMT_EVRC_FS 0x00010BEE + +/* EVRC encoder configuration structure used in the + * #ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 command. + */ +struct asm_evrc_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u16 max_rate; +/* Maximum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 min_rate; +/* Minimum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 rate_mod_cmd; +/* Rate modulation command. Default: 0. + * - If bit 0=1, rate control is enabled. + * - If bit 1=1, the maximum number of consecutive full rate frames + * is limited with numbers supplied in bits 2 to 10. + * + * - If bit 1=0, the minimum number of non-full rate frames in + * between two full rate frames is forced to the number supplied in + * bits 2 to 10. In both cases, if necessary, half rate is used to + * substitute full rate. + * + * - Bits 15 to 10 are reserved and must all be set to zero. + */ + + u16 reserved; + /* Reserved. Clients must set this field to zero. */ +} __packed; + +#define ASM_MEDIA_FMT_WMA_V10PRO_V2 0x00010DA7 + +struct asm_wmaprov10_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 fmtag; +/* WMA format type. + * Supported values: + * - 0x162 -- WMA 9 Pro + * - 0x163 -- WMA 9 Pro Lossless + * - 0x166 -- WMA 10 Pro + * - 0x167 -- WMA 10 Pro Lossless + */ + + u16 num_channels; +/* Number of channels encoded in the input stream. + * Supported values: 1 to 8 + */ + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * Supported values: 11025, 16000, 22050, 32000, 44100, 48000, + * 88200, 96000 + */ + + u32 avg_bytes_per_sec; +/* Bitrate expressed as the average bytes per second. + * Supported values: 2000 to 96000 + */ + + u16 blk_align; +/* Size of the bitstream packet size in bytes. WMA Pro files + * have a payload of one block per bitstream packet. + * Supported values: @le 13376 + */ + + u16 bits_per_sample; +/* Number of bits per sample in the encoded WMA stream. + * Supported values: 16, 24 + */ + + u32 channel_mask; +/* Bit-packed double word (32-bits) that indicates the + * recommended speaker positions for each source channel. + */ + + u16 enc_options; +/* Bit-packed word with values that indicate whether certain + * features of the bitstream are used. + * Supported values: - 0x0001 -- ENCOPT3_PURE_LOSSLESS - 0x0006 -- + * ENCOPT3_FRM_SIZE_MOD - 0x0038 -- ENCOPT3_SUBFRM_DIV - 0x0040 -- + * ENCOPT3_WRITE_FRAMESIZE_IN_HDR - 0x0080 -- + * ENCOPT3_GENERATE_DRC_PARAMS - 0x0100 -- ENCOPT3_RTMBITS + */ + + + u16 usAdvancedEncodeOpt; + /* Advanced encoding option. */ + + u32 advanced_enc_options2; + /* Advanced encoding option 2. */ + +} __packed; + +#define ASM_MEDIA_FMT_WMA_V9_V2 0x00010DA8 +struct asm_wmastdv9_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u16 fmtag; +/* WMA format tag. + * Supported values: 0x161 (WMA 9 standard) + */ + + u16 num_channels; +/* Number of channels in the stream. + * Supported values: 1, 2 + */ + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * Supported values: 48000 + */ + + u32 avg_bytes_per_sec; + /* Bitrate expressed as the average bytes per second. */ + + u16 blk_align; +/* Block align. All WMA files with a maximum packet size of + * 13376 are supported. + */ + + + u16 bits_per_sample; +/* Number of bits per sample in the output. + * Supported values: 16 + */ + + u32 channel_mask; +/* Channel mask. + * Supported values: + * - 3 -- Stereo (front left/front right) + * - 4 -- Mono (center) + */ + + u16 enc_options; + /* Options used during encoding. */ + + u16 reserved; + +} __packed; + +#define ASM_MEDIA_FMT_WMA_V8 0x00010D91 + +struct asm_wmastdv8_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u32 bit_rate; + /* Encoding rate in bits per second. */ + + u32 sample_rate; +/* Number of samples per second. + * + * Supported values: + * - 0 -- Native mode + * - Other Supported values are 22050, 32000, 44100, and 48000. + * + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + + u16 channel_cfg; +/* Number of channels to encode. + * Supported values: + * - 0 -- Native mode + * - 1 -- Mono + * - 2 -- Stereo + * - Other values are not supported. + * + * Native mode indicates that encoding must be performed with the + * number of channels at the input. + * The number of channels must not change during encoding. + */ + + u16 reserved; + /* Reserved. Clients must set this field to zero.*/ + } __packed; + +#define ASM_MEDIA_FMT_AMR_WB_PLUS_V2 0x00010DA9 + +struct asm_amrwbplus_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 amr_frame_fmt; +/* AMR frame format. + * Supported values: + * - 6 -- Transport Interface Format (TIF) + * - Any other value -- File storage format (FSF) + * + * TIF stream contains 2-byte header for each frame within the + * superframe. FSF stream contains one 2-byte header per superframe. + */ + +} __packed; + +#define ASM_MEDIA_FMT_AC3 0x00010DEE +#define ASM_MEDIA_FMT_EAC3 0x00010DEF +#define ASM_MEDIA_FMT_DTS 0x00010D88 +#define ASM_MEDIA_FMT_MP2 0x00010DE9 +#define ASM_MEDIA_FMT_FLAC 0x00010C16 +#define ASM_MEDIA_FMT_ALAC 0x00012F31 +#define ASM_MEDIA_FMT_VORBIS 0x00010C15 +#define ASM_MEDIA_FMT_APE 0x00012F32 +#define ASM_MEDIA_FMT_DSD 0x00012F3E + + +/* Media format ID for adaptive transform acoustic coding. This + * ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED command + * only. + */ + +#define ASM_MEDIA_FMT_ATRAC 0x00010D89 + +/* Media format ID for metadata-enhanced audio transmission. + * This ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED + * command only. + */ + +#define ASM_MEDIA_FMT_MAT 0x00010D8A + +/* adsp_media_fmt.h */ + +#define ASM_DATA_CMD_WRITE_V2 0x00010DAB + +struct asm_data_cmd_write_v2 { + struct apr_hdr hdr; + u32 buf_addr_lsw; +/* The 64 bit address msw-lsw should be a valid, mapped address. + * 64 bit address should be a multiple of 32 bytes + */ + + u32 buf_addr_msw; +/* The 64 bit address msw-lsw should be a valid, mapped address. + * 64 bit address should be a multiple of 32 bytes. + * -Address of the buffer containing the data to be decoded. + * The buffer should be aligned to a 32 byte boundary. + * -In the case of 32 bit Shared memory address, msw field must + * -be set to zero. + * -In the case of 36 bit shared memory address, bit 31 to bit 4 + * -of msw must be set to zero. + */ + u32 mem_map_handle; +/* memory map handle returned by DSP through + * ASM_CMD_SHARED_MEM_MAP_REGIONS command + */ + u32 buf_size; +/* Number of valid bytes available in the buffer for decoding. The + * first byte starts at buf_addr. + */ + + u32 seq_id; + /* Optional buffer sequence ID. */ + + u32 timestamp_lsw; +/* Lower 32 bits of the 64-bit session time in microseconds of the + * first buffer sample. + */ + + u32 timestamp_msw; +/* Upper 32 bits of the 64-bit session time in microseconds of the + * first buffer sample. + */ + + u32 flags; +/* Bitfield of flags. + * Supported values for bit 31: + * - 1 -- Valid timestamp. + * - 0 -- Invalid timestamp. + * - Use #ASM_BIT_MASKIMESTAMP_VALID_FLAG as the bitmask and + * #ASM_SHIFTIMESTAMP_VALID_FLAG as the shift value to set this bit. + * Supported values for bit 30: + * - 1 -- Last buffer. + * - 0 -- Not the last buffer. + * + * Supported values for bit 29: + * - 1 -- Continue the timestamp from the previous buffer. + * - 0 -- Timestamp of the current buffer is not related + * to the timestamp of the previous buffer. + * - Use #ASM_BIT_MASKS_CONTINUE_FLAG and #ASM_SHIFTS_CONTINUE_FLAG + * to set this bit. + * + * Supported values for bit 4: + * - 1 -- End of the frame. + * - 0 -- Not the end of frame, or this information is not known. + * - Use #ASM_BIT_MASK_EOF_FLAG as the bitmask and #ASM_SHIFT_EOF_FLAG + * as the shift value to set this bit. + * + * All other bits are reserved and must be set to 0. + * + * If bit 31=0 and bit 29=1: The timestamp of the first sample in + * this buffer continues from the timestamp of the last sample in + * the previous buffer. If there is no previous buffer (i.e., this + * is the first buffer sent after opening the stream or after a + * flush operation), or if the previous buffer does not have a valid + * timestamp, the samples in the current buffer also do not have a + * valid timestamp. They are played out as soon as possible. + * + * + * If bit 31=0 and bit 29=0: No timestamp is associated with the + * first sample in this buffer. The samples are played out as soon + * as possible. + * + * + * If bit 31=1 and bit 29 is ignored: The timestamp specified in + * this payload is honored. + * + * + * If bit 30=0: Not the last buffer in the stream. This is useful + * in removing trailing samples. + * + * + * For bit 4: The client can set this flag for every buffer sent in + * which the last byte is the end of a frame. If this flag is set, + * the buffer can contain data from multiple frames, but it should + * always end at a frame boundary. Restrictions allow the aDSP to + * detect an end of frame without requiring additional processing. + */ + +} __packed; + +#define ASM_DATA_CMD_READ_V2 0x00010DAC + +struct asm_data_cmd_read_v2 { + struct apr_hdr hdr; + u32 buf_addr_lsw; +/* the 64 bit address msw-lsw should be a valid mapped address + * and should be a multiple of 32 bytes + */ + + + u32 buf_addr_msw; +/* the 64 bit address msw-lsw should be a valid mapped address + * and should be a multiple of 32 bytes. + * - Address of the buffer where the DSP puts the encoded data, + * potentially, at an offset specified by the uOffset field in + * ASM_DATA_EVENT_READ_DONE structure. The buffer should be aligned + * to a 32 byte boundary. + * - In the case of 32 bit Shared memory address, msw field must + * - be set to zero. + * - In the case of 36 bit shared memory address, bit 31 to bit + * - 4 of msw must be set to zero. + */ + u32 mem_map_handle; +/* memory map handle returned by DSP through + * ASM_CMD_SHARED_MEM_MAP_REGIONS command. + */ + + u32 buf_size; +/* Number of bytes available for the aDSP to write. The aDSP + * starts writing from buf_addr. + */ + + u32 seq_id; + /* Optional buffer sequence ID. */ +} __packed; + +#define ASM_DATA_CMD_EOS 0x00010BDB +#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C +#define ASM_DATA_EVENT_EOS 0x00010BDD + +#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99 +struct asm_data_event_write_done_v2 { + u32 buf_addr_lsw; + /* lsw of the 64 bit address */ + u32 buf_addr_msw; + /* msw of the 64 bit address. address given by the client in + * ASM_DATA_CMD_WRITE_V2 command. + */ + u32 mem_map_handle; + /* memory map handle in the ASM_DATA_CMD_WRITE_V2 */ + + u32 status; +/* Status message (error code) that indicates whether the + * referenced buffer has been successfully consumed. + * Supported values: Refer to @xhyperref{Q3,[Q3]} + */ +} __packed; + +#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A + +/* Definition of the frame metadata flag bitmask.*/ +#define ASM_BIT_MASK_FRAME_METADATA_FLAG (0x40000000UL) + +/* Definition of the frame metadata flag shift value. */ +#define ASM_SHIFT_FRAME_METADATA_FLAG 30 + +struct asm_data_event_read_done_v2 { + u32 status; +/* Status message (error code). + * Supported values: Refer to @xhyperref{Q3,[Q3]} + */ + +u32 buf_addr_lsw; +/* 64 bit address msw-lsw is a valid, mapped address. 64 bit + * address is a multiple of 32 bytes. + */ + +u32 buf_addr_msw; +/* 64 bit address msw-lsw is a valid, mapped address. 64 bit + * address is a multiple of 32 bytes. + * + * -Same address provided by the client in ASM_DATA_CMD_READ_V2 + * -In the case of 32 bit Shared memory address, msw field is set to + * zero. + * -In the case of 36 bit shared memory address, bit 31 to bit 4 + * -of msw is set to zero. + */ + +u32 mem_map_handle; +/* memory map handle in the ASM_DATA_CMD_READ_V2 */ + +u32 enc_framesotal_size; +/* Total size of the encoded frames in bytes. + * Supported values: >0 + */ + +u32 offset; +/* Offset (from buf_addr) to the first byte of the first encoded + * frame. All encoded frames are consecutive, starting from this + * offset. + * Supported values: > 0 + */ + +u32 timestamp_lsw; +/* Lower 32 bits of the 64-bit session time in microseconds of + * the first sample in the buffer. If Bit 5 of mode_flags flag of + * ASM_STREAM_CMD_OPEN_READ_V2 is 1 then the 64 bit timestamp is + * absolute capture time otherwise it is relative session time. The + * absolute timestamp doesn't reset unless the system is reset. + */ + + +u32 timestamp_msw; +/* Upper 32 bits of the 64-bit session time in microseconds of + * the first sample in the buffer. + */ + + +u32 flags; +/* Bitfield of flags. Bit 30 indicates whether frame metadata is + * present. If frame metadata is present, num_frames consecutive + * instances of @xhyperref{hdr:FrameMetaData,Frame metadata} start + * at the buffer address. + * Supported values for bit 31: + * - 1 -- Timestamp is valid. + * - 0 -- Timestamp is invalid. + * - Use #ASM_BIT_MASKIMESTAMP_VALID_FLAG and + * #ASM_SHIFTIMESTAMP_VALID_FLAG to set this bit. + * + * Supported values for bit 30: + * - 1 -- Frame metadata is present. + * - 0 -- Frame metadata is absent. + * - Use #ASM_BIT_MASK_FRAME_METADATA_FLAG and + * #ASM_SHIFT_FRAME_METADATA_FLAG to set this bit. + * + * All other bits are reserved; the aDSP sets them to 0. + */ + +u32 num_frames; +/* Number of encoded frames in the buffer. */ + +u32 seq_id; +/* Optional buffer sequence ID. */ +} __packed; + +struct asm_data_read_buf_metadata_v2 { + u32 offset; +/* Offset from buf_addr in #ASM_DATA_EVENT_READ_DONE_PAYLOAD to + * the frame associated with this metadata. + * Supported values: > 0 + */ + +u32 frm_size; +/* Size of the encoded frame in bytes. + * Supported values: > 0 + */ + +u32 num_encoded_pcm_samples; +/* Number of encoded PCM samples (per channel) in the frame + * associated with this metadata. + * Supported values: > 0 + */ + +u32 timestamp_lsw; +/* Lower 32 bits of the 64-bit session time in microseconds of the + * first sample for this frame. + * If Bit 5 of mode_flags flag of ASM_STREAM_CMD_OPEN_READ_V2 is 1 + * then the 64 bit timestamp is absolute capture time otherwise it + * is relative session time. The absolute timestamp doesn't reset + * unless the system is reset. + */ + + +u32 timestamp_msw; +/* Lower 32 bits of the 64-bit session time in microseconds of the + * first sample for this frame. + */ + +u32 flags; +/* Frame flags. + * Supported values for bit 31: + * - 1 -- Time stamp is valid + * - 0 -- Time stamp is not valid + * - All other bits are reserved; the aDSP sets them to 0. + */ +} __packed; + +/* Notifies the client of a change in the data sampling rate or + * Channel mode. This event is raised by the decoder service. The + * event is enabled through the mode flags of + * #ASM_STREAM_CMD_OPEN_WRITE_V2 or + * #ASM_STREAM_CMD_OPEN_READWRITE_V2. - The decoder detects a change + * in the output sampling frequency or the number/positioning of + * output channels, or if it is the first frame decoded.The new + * sampling frequency or the new channel configuration is + * communicated back to the client asynchronously. + */ + +#define ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY 0x00010C65 + +/* Payload of the #ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY event. + * This event is raised when the following conditions are both true: + * - The event is enabled through the mode_flags of + * #ASM_STREAM_CMD_OPEN_WRITE_V2 or + * #ASM_STREAM_CMD_OPEN_READWRITE_V2. - The decoder detects a change + * in either the output sampling frequency or the number/positioning + * of output channels, or if it is the first frame decoded. + * This event is not raised (even if enabled) if the decoder is + * MIDI, because + */ + + +struct asm_data_event_sr_cm_change_notify { + u32 sample_rate; +/* New sampling rate (in Hertz) after detecting a change in the + * bitstream. + * Supported values: 2000 to 48000 + */ + + u16 num_channels; +/* New number of channels after detecting a change in the + * bitstream. + * Supported values: 1 to 8 + */ + + + u16 reserved; + /* Reserved for future use. This field must be set to 0.*/ + + u8 channel_mapping[8]; + +} __packed; + +/* Notifies the client of a data sampling rate or channel mode + * change. This event is raised by the encoder service. + * This event is raised when : + * - Native mode encoding was requested in the encoder + * configuration (i.e., the channel number was 0), the sample rate + * was 0, or both were 0. + * + * - The input data frame at the encoder is the first one, or the + * sampling rate/channel mode is different from the previous input + * data frame. + * + */ +#define ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY 0x00010BDE + +struct asm_data_event_enc_sr_cm_change_notify { + u32 sample_rate; +/* New sampling rate (in Hertz) after detecting a change in the + * input data. + * Supported values: 2000 to 48000 + */ + + + u16 num_channels; +/* New number of channels after detecting a change in the input + * data. Supported values: 1 to 8 + */ + + + u16 bits_per_sample; +/* New bits per sample after detecting a change in the input + * data. + * Supported values: 16, 24 + */ + + + u8 channel_mapping[8]; + +} __packed; +#define ASM_DATA_CMD_IEC_60958_FRAME_RATE 0x00010D87 + + +/* Payload of the #ASM_DATA_CMD_IEC_60958_FRAME_RATE command, + * which is used to indicate the IEC 60958 frame rate of a given + * packetized audio stream. + */ + +struct asm_data_cmd_iec_60958_frame_rate { + u32 frame_rate; +/* IEC 60958 frame rate of the incoming IEC 61937 packetized stream. + * Supported values: Any valid frame rate + */ +} __packed; + +/* adsp_asm_data_commands.h*/ +/* Definition of the stream ID bitmask.*/ +#define ASM_BIT_MASK_STREAM_ID (0x000000FFUL) + +/* Definition of the stream ID shift value.*/ +#define ASM_SHIFT_STREAM_ID 0 + +/* Definition of the session ID bitmask.*/ +#define ASM_BIT_MASK_SESSION_ID (0x0000FF00UL) + +/* Definition of the session ID shift value.*/ +#define ASM_SHIFT_SESSION_ID 8 + +/* Definition of the service ID bitmask.*/ +#define ASM_BIT_MASK_SERVICE_ID (0x00FF0000UL) + +/* Definition of the service ID shift value.*/ +#define ASM_SHIFT_SERVICE_ID 16 + +/* Definition of the domain ID bitmask.*/ +#define ASM_BIT_MASK_DOMAIN_ID (0xFF000000UL) + +/* Definition of the domain ID shift value.*/ +#define ASM_SHIFT_DOMAIN_ID 24 + +#define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92 +#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93 +#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94 + +/* adsp_asm_service_commands.h */ + +#define ASM_MAX_SESSION_ID (15) + +/* Maximum number of sessions.*/ +#define ASM_MAX_NUM_SESSIONS ASM_MAX_SESSION_ID + +/* Maximum number of streams per session.*/ +#define ASM_MAX_STREAMS_PER_SESSION (8) +#define ASM_SESSION_CMD_RUN_V2 0x00010DAA +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_IMMEDIATE 0 +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_ABSOLUTEIME 1 +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_RELATIVEIME 2 +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY 3 + +#define ASM_BIT_MASK_RUN_STARTIME (0x00000003UL) + +/* Bit shift value used to specify the start time for the + * ASM_SESSION_CMD_RUN_V2 command. + */ +#define ASM_SHIFT_RUN_STARTIME 0 +struct asm_session_cmd_run_v2 { + struct apr_hdr hdr; + u32 flags; +/* Specifies whether to run immediately or at a specific + * rendering time or with a specified delay. Run with delay is + * useful for delaying in case of ASM loopback opened through + * ASM_STREAM_CMD_OPEN_LOOPBACK_V2. Use #ASM_BIT_MASK_RUN_STARTIME + * and #ASM_SHIFT_RUN_STARTIME to set this 2-bit flag. + * + * + *Bits 0 and 1 can take one of four possible values: + * + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_IMMEDIATE + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_ABSOLUTEIME + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_RELATIVEIME + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY + * + *All other bits are reserved; clients must set them to zero. + */ + + u32 time_lsw; +/* Lower 32 bits of the time in microseconds used to align the + * session origin time. When bits 0-1 of flags is + * ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, time lsw is the lsw of + * the delay in us. For ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, + * maximum value of the 64 bit delay is 150 ms. + */ + + u32 time_msw; +/* Upper 32 bits of the time in microseconds used to align the + * session origin time. When bits 0-1 of flags is + * ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, time msw is the msw of + * the delay in us. For ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, + * maximum value of the 64 bit delay is 150 ms. + */ + +} __packed; + +#define ASM_SESSION_CMD_PAUSE 0x00010BD3 +#define ASM_SESSION_CMD_SUSPEND 0x00010DEC +#define ASM_SESSION_CMD_GET_SESSIONTIME_V3 0x00010D9D +#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5 + +struct asm_session_cmd_rgstr_rx_underflow { + struct apr_hdr hdr; + u16 enable_flag; +/* Specifies whether a client is to receive events when an Rx + * session underflows. + * Supported values: + * - 0 -- Do not send underflow events + * - 1 -- Send underflow events + */ + u16 reserved; + /* Reserved. This field must be set to zero.*/ +} __packed; + +#define ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS 0x00010BD6 + +struct asm_session_cmd_regx_overflow { + struct apr_hdr hdr; + u16 enable_flag; +/* Specifies whether a client is to receive events when a Tx + * session overflows. + * Supported values: + * - 0 -- Do not send overflow events + * - 1 -- Send overflow events + */ + + u16 reserved; + /* Reserved. This field must be set to zero.*/ +} __packed; + +#define ASM_SESSION_EVENT_RX_UNDERFLOW 0x00010C17 +#define ASM_SESSION_EVENTX_OVERFLOW 0x00010C18 +#define ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3 0x00010D9E + +struct asm_session_cmdrsp_get_sessiontime_v3 { + u32 status; + /* Status message (error code). + * Supported values: Refer to @xhyperref{Q3,[Q3]} + */ + + u32 sessiontime_lsw; + /* Lower 32 bits of the current session time in microseconds.*/ + + u32 sessiontime_msw; + /* Upper 32 bits of the current session time in microseconds.*/ + + u32 absolutetime_lsw; +/* Lower 32 bits in micro seconds of the absolute time at which + * the * sample corresponding to the above session time gets + * rendered * to hardware. This absolute time may be slightly in the + * future or past. + */ + + + u32 absolutetime_msw; +/* Upper 32 bits in micro seconds of the absolute time at which + * the * sample corresponding to the above session time gets + * rendered to * hardware. This absolute time may be slightly in the + * future or past. + */ + +} __packed; + +#define ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2 0x00010D9F + +struct asm_session_cmd_adjust_session_clock_v2 { + struct apr_hdr hdr; +u32 adjustime_lsw; +/* Lower 32 bits of the signed 64-bit quantity that specifies the + * adjustment time in microseconds to the session clock. + * + * Positive values indicate advancement of the session clock. + * Negative values indicate delay of the session clock. + */ + + + u32 adjustime_msw; +/* Upper 32 bits of the signed 64-bit quantity that specifies + * the adjustment time in microseconds to the session clock. + * Positive values indicate advancement of the session clock. + * Negative values indicate delay of the session clock. + */ + +} __packed; + +#define ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 0x00010DA0 + +struct asm_session_cmdrsp_adjust_session_clock_v2 { + u32 status; +/* Status message (error code). + * Supported values: Refer to @xhyperref{Q3,[Q3]} + * An error means the session clock is not adjusted. In this case, + * the next two fields are irrelevant. + */ + + + u32 actual_adjustime_lsw; +/* Lower 32 bits of the signed 64-bit quantity that specifies + * the actual adjustment in microseconds performed by the aDSP. + * A positive value indicates advancement of the session clock. A + * negative value indicates delay of the session clock. + */ + + + u32 actual_adjustime_msw; +/* Upper 32 bits of the signed 64-bit quantity that specifies + * the actual adjustment in microseconds performed by the aDSP. + * A positive value indicates advancement of the session clock. A + * negative value indicates delay of the session clock. + */ + + + u32 cmd_latency_lsw; +/* Lower 32 bits of the unsigned 64-bit quantity that specifies + * the amount of time in microseconds taken to perform the session + * clock adjustment. + */ + + + u32 cmd_latency_msw; +/* Upper 32 bits of the unsigned 64-bit quantity that specifies + * the amount of time in microseconds taken to perform the session + * clock adjustment. + */ + +} __packed; + +#define ASM_SESSION_CMD_GET_PATH_DELAY_V2 0x00010DAF +#define ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 0x00010DB0 + +struct asm_session_cmdrsp_get_path_delay_v2 { + u32 status; +/* Status message (error code). Whether this get delay operation + * is successful or not. Delay value is valid only if status is + * success. + * Supported values: Refer to @xhyperref{Q5,[Q5]} + */ + + u32 audio_delay_lsw; + /* Upper 32 bits of the aDSP delay in microseconds. */ + + u32 audio_delay_msw; + /* Lower 32 bits of the aDSP delay in microseconds. */ + +} __packed; + +/* adsp_asm_session_command.h*/ +#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3 + +#define ASM_LOW_LATENCY_STREAM_SESSION 0x10000000 + +#define ASM_ULTRA_LOW_LATENCY_STREAM_SESSION 0x20000000 + +#define ASM_ULL_POST_PROCESSING_STREAM_SESSION 0x40000000 + +#define ASM_LEGACY_STREAM_SESSION 0 + + +struct asm_stream_cmd_open_write_v3 { + struct apr_hdr hdr; + uint32_t mode_flags; +/* Mode flags that configure the stream to notify the client + * whenever it detects an SR/CM change at the input to its POPP. + * Supported values for bits 0 to 1: + * - Reserved; clients must set them to zero. + * Supported values for bit 2: + * - 0 -- SR/CM change notification event is disabled. + * - 1 -- SR/CM change notification event is enabled. + * - Use #ASM_BIT_MASK_SR_CM_CHANGE_NOTIFY_FLAG and + * #ASM_SHIFT_SR_CM_CHANGE_NOTIFY_FLAG to set or get this bit. + * + * Supported values for bit 31: + * - 0 -- Stream to be opened in on-Gapless mode. + * - 1 -- Stream to be opened in Gapless mode. In Gapless mode, + * successive streams must be opened with same session ID but + * different stream IDs. + * + * - Use #ASM_BIT_MASK_GAPLESS_MODE_FLAG and + * #ASM_SHIFT_GAPLESS_MODE_FLAG to set or get this bit. + * + * + * @note1hang MIDI and DTMF streams cannot be opened in Gapless mode. + */ + + uint16_t sink_endpointype; +/*< Sink point type. + * Supported values: + * - 0 -- Device matrix + * - Other values are reserved. + * + * The device matrix is the gateway to the hardware ports. + */ + + uint16_t bits_per_sample; +/*< Number of bits per sample processed by ASM modules. + * Supported values: 16 and 24 bits per sample + */ + + uint32_t postprocopo_id; +/*< Specifies the topology (order of processing) of + * postprocessing algorithms. None means no postprocessing. + * Supported values: + * - #ASM_STREAM_POSTPROCOPO_ID_DEFAULT + * - #ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL + * - #ASM_STREAM_POSTPROCOPO_ID_NONE + * + * This field can also be enabled through SetParams flags. + */ + + uint32_t dec_fmt_id; +/*< Configuration ID of the decoder media format. + * + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_ADPCM + * - #ASM_MEDIA_FMT_MP3 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_DOLBY_AAC + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_AMR_WB_PLUS_V2 + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_WMA_V10PRO_V2 + * - #ASM_MEDIA_FMT_WMA_V9_V2 + * - #ASM_MEDIA_FMT_AC3 + * - #ASM_MEDIA_FMT_EAC3 + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_FR_FS + * - #ASM_MEDIA_FMT_VORBIS + * - #ASM_MEDIA_FMT_FLAC + * - #ASM_MEDIA_FMT_ALAC + * - #ASM_MEDIA_FMT_APE + * - #ASM_MEDIA_FMT_EXAMPLE + */ +} __packed; + +#define ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE 0x00010DD9 + +/* Bitmask for the stream_perf_mode subfield. */ +#define ASM_BIT_MASK_STREAM_PERF_FLAG_PULL_MODE_WRITE 0xE0000000UL + +/* Bitmask for the stream_perf_mode subfield. */ +#define ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE 29 + +#define ASM_STREAM_CMD_OPEN_PUSH_MODE_READ 0x00010DDA + +#define ASM_BIT_MASK_STREAM_PERF_FLAG_PUSH_MODE_READ 0xE0000000UL + +#define ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ 29 + +#define ASM_DATA_EVENT_WATERMARK 0x00010DDB + +struct asm_shared_position_buffer { + volatile uint32_t frame_counter; +/* Counter used to handle interprocessor synchronization issues. + * When frame_counter is 0: read_index, wall_clock_us_lsw, and + * wall_clock_us_msw are invalid. + * Supported values: >= 0. + */ + + volatile uint32_t index; +/* Index in bytes from where the aDSP is reading/writing. + * Supported values: 0 to circular buffer size - 1 + */ + + volatile uint32_t wall_clock_us_lsw; +/* Lower 32 bits of the 64-bit wall clock time in microseconds when the + * read index was updated. + * Supported values: >= 0 + */ + + volatile uint32_t wall_clock_us_msw; +/* Upper 32 bits of the 64 bit wall clock time in microseconds when the + * read index was updated + * Supported values: >= 0 + */ +} __packed; + +struct asm_shared_watermark_level { + uint32_t watermark_level_bytes; +} __packed; + +struct asm_stream_cmd_open_shared_io { + struct apr_hdr hdr; + uint32_t mode_flags; + uint16_t endpoint_type; + uint16_t topo_bits_per_sample; + uint32_t topo_id; + uint32_t fmt_id; + uint32_t shared_pos_buf_phy_addr_lsw; + uint32_t shared_pos_buf_phy_addr_msw; + uint16_t shared_pos_buf_mem_pool_id; + uint16_t shared_pos_buf_num_regions; + uint32_t shared_pos_buf_property_flag; + uint32_t shared_circ_buf_start_phy_addr_lsw; + uint32_t shared_circ_buf_start_phy_addr_msw; + uint32_t shared_circ_buf_size; + uint16_t shared_circ_buf_mem_pool_id; + uint16_t shared_circ_buf_num_regions; + uint32_t shared_circ_buf_property_flag; + uint32_t num_watermark_levels; + struct asm_multi_channel_pcm_fmt_blk_v3 fmt; + struct avs_shared_map_region_payload map_region_pos_buf; + struct avs_shared_map_region_payload map_region_circ_buf; + struct asm_shared_watermark_level watermark[0]; +} __packed; + +#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4 + +/* Definition of the timestamp type flag bitmask */ +#define ASM_BIT_MASKIMESTAMPYPE_FLAG (0x00000020UL) + +/* Definition of the timestamp type flag shift value. */ +#define ASM_SHIFTIMESTAMPYPE_FLAG 5 + +/* Relative timestamp is identified by this value.*/ +#define ASM_RELATIVEIMESTAMP 0 + +/* Absolute timestamp is identified by this value.*/ +#define ASM_ABSOLUTEIMESTAMP 1 + +/* Bit value for Low Latency Tx stream subfield */ +#define ASM_LOW_LATENCY_TX_STREAM_SESSION 1 + +/* Bit shift for the stream_perf_mode subfield. */ +#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ 29 + +struct asm_stream_cmd_open_read_v3 { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags that indicate whether meta information per encoded + * frame is to be provided. + * Supported values for bit 4: + * + * - 0 -- Return data buffer contains all encoded frames only; it + * does not contain frame metadata. + * + * - 1 -- Return data buffer contains an array of metadata and + * encoded frames. + * + * - Use #ASM_BIT_MASK_META_INFO_FLAG as the bitmask and + * #ASM_SHIFT_META_INFO_FLAG as the shift value for this bit. + * + * + * Supported values for bit 5: + * + * - ASM_RELATIVEIMESTAMP -- ASM_DATA_EVENT_READ_DONE_V2 will have + * - relative time-stamp. + * - ASM_ABSOLUTEIMESTAMP -- ASM_DATA_EVENT_READ_DONE_V2 will + * - have absolute time-stamp. + * + * - Use #ASM_BIT_MASKIMESTAMPYPE_FLAG as the bitmask and + * #ASM_SHIFTIMESTAMPYPE_FLAG as the shift value for this bit. + * + * All other bits are reserved; clients must set them to zero. + */ + + u32 src_endpointype; +/* Specifies the endpoint providing the input samples. + * Supported values: + * - 0 -- Device matrix + * - All other values are reserved; clients must set them to zero. + * Otherwise, an error is returned. + * The device matrix is the gateway from the tunneled Tx ports. + */ + + u32 preprocopo_id; +/* Specifies the topology (order of processing) of preprocessing + * algorithms. None means no preprocessing. + * Supported values: + * - #ASM_STREAM_PREPROCOPO_ID_DEFAULT + * - #ASM_STREAM_PREPROCOPO_ID_NONE + * + * This field can also be enabled through SetParams flags. + */ + + u32 enc_cfg_id; +/* Media configuration ID for encoded output. + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_EXAMPLE + * - #ASM_MEDIA_FMT_WMA_V8 + */ + + u16 bits_per_sample; +/* Number of bits per sample processed by ASM modules. + * Supported values: 16 and 24 bits per sample + */ + + u16 reserved; +/* Reserved for future use. This field must be set to zero.*/ +} __packed; + +#define ASM_POPP_OUTPUT_SR_NATIVE_RATE 0 + +/* Enumeration for the maximum sampling rate at the POPP output.*/ +#define ASM_POPP_OUTPUT_SR_MAX_RATE 48000 + +#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D +#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D + +struct asm_stream_cmd_open_readwrite_v2 { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags. + * Supported values for bit 2: + * - 0 -- SR/CM change notification event is disabled. + * - 1 -- SR/CM change notification event is enabled. Use + * #ASM_BIT_MASK_SR_CM_CHANGE_NOTIFY_FLAG and + * #ASM_SHIFT_SR_CM_CHANGE_NOTIFY_FLAG to set or + * getting this flag. + * + * Supported values for bit 4: + * - 0 -- Return read data buffer contains all encoded frames only; it + * does not contain frame metadata. + * - 1 -- Return read data buffer contains an array of metadata and + * encoded frames. + * + * All other bits are reserved; clients must set them to zero. + */ + + u32 postprocopo_id; +/* Specifies the topology (order of processing) of postprocessing + * algorithms. None means no postprocessing. + * + * Supported values: + * - #ASM_STREAM_POSTPROCOPO_ID_DEFAULT + * - #ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL + * - #ASM_STREAM_POSTPROCOPO_ID_NONE + */ + + u32 dec_fmt_id; +/* Specifies the media type of the input data. PCM indicates that + * no decoding must be performed, e.g., this is an NT encoder + * session. + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_ADPCM + * - #ASM_MEDIA_FMT_MP3 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_DOLBY_AAC + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_WMA_V10PRO_V2 + * - #ASM_MEDIA_FMT_WMA_V9_V2 + * - #ASM_MEDIA_FMT_AMR_WB_PLUS_V2 + * - #ASM_MEDIA_FMT_AC3 + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_EXAMPLE + */ + + u32 enc_cfg_id; +/* Specifies the media type for the output of the stream. PCM + * indicates that no encoding must be performed, e.g., this is an NT + * decoder session. + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_EXAMPLE + * - #ASM_MEDIA_FMT_WMA_V8 + */ + + u16 bits_per_sample; +/* Number of bits per sample processed by ASM modules. + * Supported values: 16 and 24 bits per sample + */ + + u16 reserved; +/* Reserved for future use. This field must be set to zero.*/ + +} __packed; + +#define ASM_STREAM_CMD_OPEN_LOOPBACK_V2 0x00010D8E +struct asm_stream_cmd_open_loopback_v2 { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags. + * Bit 0-31: reserved; client should set these bits to 0 + */ + u16 src_endpointype; + /* Endpoint type. 0 = Tx Matrix */ + u16 sink_endpointype; + /* Endpoint type. 0 = Rx Matrix */ + u32 postprocopo_id; +/* Postprocessor topology ID. Specifies the topology of + * postprocessing algorithms. + */ + + u16 bits_per_sample; +/* The number of bits per sample processed by ASM modules + * Supported values: 16 and 24 bits per sample + */ + u16 reserved; +/* Reserved for future use. This field must be set to zero. */ +} __packed; + +#define ASM_STREAM_CMD_CLOSE 0x00010BCD +#define ASM_STREAM_CMD_FLUSH 0x00010BCE + + +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 +#define ASM_STREAM_CMD_SET_PP_PARAMS_V2 0x00010DA1 + +struct asm_stream_cmd_set_pp_params_v2 { + u32 data_payload_addr_lsw; +/* LSW of parameter data payload address. Supported values: any. */ + u32 data_payload_addr_msw; +/* MSW of Parameter data payload address. Supported values: any. + * - Must be set to zero for in-band data. + * - In the case of 32 bit Shared memory address, msw field must be + * - set to zero. + * - In the case of 36 bit shared memory address, bit 31 to bit 4 of + * msw + * + * - must be set to zero. + */ + u32 mem_map_handle; +/* Supported Values: Any. + * memory map handle returned by DSP through + * ASM_CMD_SHARED_MEM_MAP_REGIONS + * command. + * if mmhandle is NULL, the ParamData payloads are within the + * message payload (in-band). + * If mmhandle is non-NULL, the ParamData payloads begin at the + * address specified in the address msw and lsw (out-of-band). + */ + + u32 data_payload_size; +/* Size in bytes of the variable payload accompanying the + * message, or in shared memory. This field is used for parsing the + * parameter payload. + */ +} __packed; + + +struct asm_stream_param_data_v2 { + u32 module_id; + /* Unique module ID. */ + + u32 param_id; + /* Unique parameter ID. */ + + u16 param_size; +/* Data size of the param_id/module_id combination. This is + * a multiple of 4 bytes. + */ + + u16 reserved; +/* Reserved for future enhancements. This field must be set to + * zero. + */ + +} __packed; + +#define ASM_STREAM_CMD_GET_PP_PARAMS_V2 0x00010DA2 + +struct asm_stream_cmd_get_pp_params_v2 { + u32 data_payload_addr_lsw; + /* LSW of the parameter data payload address. */ + u32 data_payload_addr_msw; +/* MSW of the parameter data payload address. + * - Size of the shared memory, if specified, shall be large enough + * to contain the whole ParamData payload, including Module ID, + * Param ID, Param Size, and Param Values + * - Must be set to zero for in-band data + * - In the case of 32 bit Shared memory address, msw field must be + * set to zero. + * - In the case of 36 bit shared memory address, bit 31 to bit 4 of + * msw must be set to zero. + */ + + u32 mem_map_handle; +/* Supported Values: Any. + * memory map handle returned by DSP through ASM_CMD_SHARED_MEM_MAP_REGIONS + * command. + * if mmhandle is NULL, the ParamData payloads in the ACK are within the + * message payload (in-band). + * If mmhandle is non-NULL, the ParamData payloads in the ACK begin at the + * address specified in the address msw and lsw. + * (out-of-band). + */ + + u32 module_id; +/* Unique module ID. */ + + u32 param_id; +/* Unique parameter ID. */ + + u16 param_max_size; +/* Maximum data size of the module_id/param_id combination. This + * is a multiple of 4 bytes. + */ + + + u16 reserved; +/* Reserved for backward compatibility. Clients must set this + * field to zero. + */ +} __packed; + +#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10 + +#define ASM_PARAM_ID_ENCDEC_BITRATE 0x00010C13 + +struct asm_bitrate_param { + u32 bitrate; +/* Maximum supported bitrate. Only the AAC encoder is supported.*/ + +} __packed; + +#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3 +#define ASM_PARAM_ID_AAC_SBR_PS_FLAG 0x00010C63 + +/* Flag to turn off both SBR and PS processing, if they are + * present in the bitstream. + */ + +#define ASM_AAC_SBR_OFF_PS_OFF (2) + +/* Flag to turn on SBR but turn off PS processing,if they are + * present in the bitstream. + */ + +#define ASM_AAC_SBR_ON_PS_OFF (1) + +/* Flag to turn on both SBR and PS processing, if they are + * present in the bitstream (default behavior). + */ + + +#define ASM_AAC_SBR_ON_PS_ON (0) + +/* Structure for an AAC SBR PS processing flag. */ + +/* Payload of the #ASM_PARAM_ID_AAC_SBR_PS_FLAG parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +struct asm_aac_sbr_ps_flag_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u32 sbr_ps_flag; +/* Control parameter to enable or disable SBR/PS processing in + * the AAC bitstream. Use the following macros to set this field: + * - #ASM_AAC_SBR_OFF_PS_OFF -- Turn off both SBR and PS + * processing, if they are present in the bitstream. + * - #ASM_AAC_SBR_ON_PS_OFF -- Turn on SBR processing, but not PS + * processing, if they are present in the bitstream. + * - #ASM_AAC_SBR_ON_PS_ON -- Turn on both SBR and PS processing, + * if they are present in the bitstream (default behavior). + * - All other values are invalid. + * Changes are applied to the next decoded frame. + */ +} __packed; + +#define ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING 0x00010C64 + +/* First single channel element in a dual mono bitstream.*/ +#define ASM_AAC_DUAL_MONO_MAP_SCE_1 (1) + +/* Second single channel element in a dual mono bitstream.*/ +#define ASM_AAC_DUAL_MONO_MAP_SCE_2 (2) + +/* Structure for AAC decoder dual mono channel mapping. */ + + +struct asm_aac_dual_mono_mapping_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + u16 left_channel_sce; + u16 right_channel_sce; + +} __packed; + +#define ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 0x00010DA4 + +struct asm_stream_cmdrsp_get_pp_params_v2 { + u32 status; +} __packed; + +#define ASM_PARAM_ID_AC3_KARAOKE_MODE 0x00010D73 + +/* Enumeration for both vocals in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_NO_VOCAL (0) + +/* Enumeration for only the left vocal in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_LEFT_VOCAL (1) + +/* Enumeration for only the right vocal in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_RIGHT_VOCAL (2) + +/* Enumeration for both vocal channels in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_BOTH_VOCAL (3) +#define ASM_PARAM_ID_AC3_DRC_MODE 0x00010D74 +/* Enumeration for the Custom Analog mode.*/ +#define AC3_DRC_MODE_CUSTOM_ANALOG (0) + +/* Enumeration for the Custom Digital mode.*/ +#define AC3_DRC_MODE_CUSTOM_DIGITAL (1) +/* Enumeration for the Line Out mode (light compression).*/ +#define AC3_DRC_MODE_LINE_OUT (2) + +/* Enumeration for the RF remodulation mode (heavy compression).*/ +#define AC3_DRC_MODE_RF_REMOD (3) +#define ASM_PARAM_ID_AC3_DUAL_MONO_MODE 0x00010D75 + +/* Enumeration for playing dual mono in stereo mode.*/ +#define AC3_DUAL_MONO_MODE_STEREO (0) + +/* Enumeration for playing left mono.*/ +#define AC3_DUAL_MONO_MODE_LEFT_MONO (1) + +/* Enumeration for playing right mono.*/ +#define AC3_DUAL_MONO_MODE_RIGHT_MONO (2) + +/* Enumeration for mixing both dual mono channels and playing them.*/ +#define AC3_DUAL_MONO_MODE_MIXED_MONO (3) +#define ASM_PARAM_ID_AC3_STEREO_DOWNMIX_MODE 0x00010D76 + +/* Enumeration for using the Downmix mode indicated in the bitstream. */ + +#define AC3_STEREO_DOWNMIX_MODE_AUTO_DETECT (0) + +/* Enumeration for Surround Compatible mode (preserves the + * surround information). + */ + +#define AC3_STEREO_DOWNMIX_MODE_LT_RT (1) +/* Enumeration for Mono Compatible mode (if the output is to be + * further downmixed to mono). + */ + +#define AC3_STEREO_DOWNMIX_MODE_LO_RO (2) + +/* ID of the AC3 PCM scale factor parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +#define ASM_PARAM_ID_AC3_PCM_SCALEFACTOR 0x00010D78 + +/* ID of the AC3 DRC boost scale factor parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +#define ASM_PARAM_ID_AC3_DRC_BOOST_SCALEFACTOR 0x00010D79 + +/* ID of the AC3 DRC cut scale factor parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +#define ASM_PARAM_ID_AC3_DRC_CUT_SCALEFACTOR 0x00010D7A + +/* Structure for AC3 Generic Parameter. */ + +/* Payload of the AC3 parameters in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +struct asm_ac3_generic_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u32 generic_parameter; +/* AC3 generic parameter. Select from one of the following + * possible values. + * + * For #ASM_PARAM_ID_AC3_KARAOKE_MODE, supported values are: + * - AC3_KARAOKE_MODE_NO_VOCAL + * - AC3_KARAOKE_MODE_LEFT_VOCAL + * - AC3_KARAOKE_MODE_RIGHT_VOCAL + * - AC3_KARAOKE_MODE_BOTH_VOCAL + * + * For #ASM_PARAM_ID_AC3_DRC_MODE, supported values are: + * - AC3_DRC_MODE_CUSTOM_ANALOG + * - AC3_DRC_MODE_CUSTOM_DIGITAL + * - AC3_DRC_MODE_LINE_OUT + * - AC3_DRC_MODE_RF_REMOD + * + * For #ASM_PARAM_ID_AC3_DUAL_MONO_MODE, supported values are: + * - AC3_DUAL_MONO_MODE_STEREO + * - AC3_DUAL_MONO_MODE_LEFT_MONO + * - AC3_DUAL_MONO_MODE_RIGHT_MONO + * - AC3_DUAL_MONO_MODE_MIXED_MONO + * + * For #ASM_PARAM_ID_AC3_STEREO_DOWNMIX_MODE, supported values are: + * - AC3_STEREO_DOWNMIX_MODE_AUTO_DETECT + * - AC3_STEREO_DOWNMIX_MODE_LT_RT + * - AC3_STEREO_DOWNMIX_MODE_LO_RO + * + * For #ASM_PARAM_ID_AC3_PCM_SCALEFACTOR, supported values are + * 0 to 1 in Q31 format. + * + * For #ASM_PARAM_ID_AC3_DRC_BOOST_SCALEFACTOR, supported values are + * 0 to 1 in Q31 format. + * + * For #ASM_PARAM_ID_AC3_DRC_CUT_SCALEFACTOR, supported values are + * 0 to 1 in Q31 format. + */ +} __packed; + +/* Enumeration for Raw mode (no downmixing), which specifies + * that all channels in the bitstream are to be played out as is + * without any downmixing. (Default) + */ + +#define WMAPRO_CHANNEL_MASK_RAW (-1) + +/* Enumeration for setting the channel mask to 0. The 7.1 mode + * (Home Theater) is assigned. + */ + + +#define WMAPRO_CHANNEL_MASK_ZERO 0x0000 + +/* Speaker layout mask for one channel (Home Theater, mono). + * - Speaker front center + */ +#define WMAPRO_CHANNEL_MASK_1_C 0x0004 + +/* Speaker layout mask for two channels (Home Theater, stereo). + * - Speaker front left + * - Speaker front right + */ +#define WMAPRO_CHANNEL_MASK_2_L_R 0x0003 + +/* Speaker layout mask for three channels (Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + */ +#define WMAPRO_CHANNEL_MASK_3_L_C_R 0x0007 + +/* Speaker layout mask for two channels (stereo). + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_2_Bl_Br 0x0030 + +/* Speaker layout mask for four channels. + * - Speaker front left + * - Speaker front right + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_4_L_R_Bl_Br 0x0033 + +/* Speaker layout mask for four channels (Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back center + */ +#define WMAPRO_CHANNEL_MASK_4_L_R_C_Bc_HT 0x0107 +/* Speaker layout mask for five channels. + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_5_L_C_R_Bl_Br 0x0037 + +/* Speaker layout mask for five channels (5 mode, Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_5_L_C_R_Sl_Sr_HT 0x0607 +/* Speaker layout mask for six channels (5.1 mode). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Bl_Br_SLF 0x003F +/* Speaker layout mask for six channels (5.1 mode, Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Sl_Sr_SLF_HT 0x060F +/* Speaker layout mask for six channels (5.1 mode, no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + * - Speaker back center + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Bl_Br_Bc 0x0137 +/* Speaker layout mask for six channels (5.1 mode, Home Theater, + * no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back center + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Sl_Sr_Bc_HT 0x0707 + +/* Speaker layout mask for seven channels (6.1 mode). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker back left + * - Speaker back right + * - Speaker back center + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Bl_Br_Bc_SLF 0x013F + +/* Speaker layout mask for seven channels (6.1 mode, Home + * Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker back center + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Sl_Sr_Bc_SLF_HT 0x070F + +/* Speaker layout mask for seven channels (6.1 mode, no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + * - Speaker front left of center + * - Speaker front right of center + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Bl_Br_SFLOC_SFROC 0x00F7 + +/* Speaker layout mask for seven channels (6.1 mode, Home + * Theater, no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker side left + * - Speaker side right + * - Speaker front left of center + * - Speaker front right of center + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Sl_Sr_SFLOC_SFROC_HT 0x0637 + +/* Speaker layout mask for eight channels (7.1 mode). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + * - Speaker low frequency + * - Speaker front left of center + * - Speaker front right of center + */ +#define WMAPRO_CHANNEL_MASK_7DOT1_L_C_R_Bl_Br_SLF_SFLOC_SFROC \ + 0x00FF + +/* Speaker layout mask for eight channels (7.1 mode, Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker side left + * - Speaker side right + * - Speaker low frequency + * - Speaker front left of center + * - Speaker front right of center + * + */ +#define WMAPRO_CHANNEL_MASK_7DOT1_L_C_R_Sl_Sr_SLF_SFLOC_SFROC_HT \ + 0x063F + +#define ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP 0x00010D82 + +/* Maximum number of decoder output channels. */ +#define MAX_CHAN_MAP_CHANNELS 16 + +/* Structure for decoder output channel mapping. */ + +/* Payload of the #ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +struct asm_dec_out_chan_map_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + u32 num_channels; +/* Number of decoder output channels. + * Supported values: 0 to #MAX_CHAN_MAP_CHANNELS + * + * A value of 0 indicates native channel mapping, which is valid + * only for NT mode. This means the output of the decoder is to be + * preserved as is. + */ + u8 channel_mapping[MAX_CHAN_MAP_CHANNELS]; +} __packed; + +#define ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED 0x00010D84 + +/* Bitmask for the IEC 61937 enable flag.*/ +#define ASM_BIT_MASK_IEC_61937_STREAM_FLAG (0x00000001UL) + +/* Shift value for the IEC 61937 enable flag.*/ +#define ASM_SHIFT_IEC_61937_STREAM_FLAG 0 + +/* Bitmask for the IEC 60958 enable flag.*/ +#define ASM_BIT_MASK_IEC_60958_STREAM_FLAG (0x00000002UL) + +/* Shift value for the IEC 60958 enable flag.*/ +#define ASM_SHIFT_IEC_60958_STREAM_FLAG 1 + +/* Payload format for open write compressed command */ + +/* Payload format for the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED + * command, which opens a stream for a given session ID and stream ID + * to be rendered in the compressed format. + */ + +struct asm_stream_cmd_open_write_compressed { + struct apr_hdr hdr; + u32 flags; +/* Mode flags that configure the stream for a specific format. + * Supported values: + * - Bit 0 -- IEC 61937 compatibility + * - 0 -- Stream is not in IEC 61937 format + * - 1 -- Stream is in IEC 61937 format + * - Bit 1 -- IEC 60958 compatibility + * - 0 -- Stream is not in IEC 60958 format + * - 1 -- Stream is in IEC 60958 format + * - Bits 2 to 31 -- 0 (Reserved) + * + * For the same stream, bit 0 cannot be set to 0 and bit 1 cannot + * be set to 1. A compressed stream connot have IEC 60958 + * packetization applied without IEC 61937 packetization. + * @note1hang Currently, IEC 60958 packetized input streams are not + * supported. + */ + + + u32 fmt_id; +/* Specifies the media type of the HDMI stream to be opened. + * Supported values: + * - #ASM_MEDIA_FMT_AC3 + * - #ASM_MEDIA_FMT_EAC3 + * - #ASM_MEDIA_FMT_DTS + * - #ASM_MEDIA_FMT_ATRAC + * - #ASM_MEDIA_FMT_MAT + * + * @note1hang This field must be set to a valid media type even if + * IEC 61937 packetization is not performed by the aDSP. + */ + +} __packed; + + +/* Indicates the number of samples per channel to be removed from the + * beginning of the stream. + */ +#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67 + +/* Indicates the number of samples per channel to be removed from + * the end of the stream. + */ +#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68 + +struct asm_data_cmd_remove_silence { + struct apr_hdr hdr; + u32 num_samples_to_remove; + /* < Number of samples per channel to be removed. + * @values 0 to (2@sscr{32}-1) + */ +} __packed; + +#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED 0x00010D95 + +struct asm_stream_cmd_open_read_compressed { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags that indicate whether meta information per encoded + * frame is to be provided. + * Supported values for bit 4: + * - 0 -- Return data buffer contains all encoded frames only; it does + * not contain frame metadata. + * - 1 -- Return data buffer contains an array of metadata and encoded + * frames. + * - Use #ASM_BIT_MASK_META_INFO_FLAG to set the bitmask and + * #ASM_SHIFT_META_INFO_FLAG to set the shift value for this bit. + * All other bits are reserved; clients must set them to zero. + */ + + u32 frames_per_buf; +/* Indicates the number of frames that need to be returned per + * read buffer + * Supported values: should be greater than 0 + */ + +} __packed; + +/* adsp_asm_stream_commands.h*/ + + +/* adsp_asm_api.h (no changes)*/ +#define ASM_STREAM_POSTPROCOPO_ID_DEFAULT \ + 0x00010BE4 +#define ASM_STREAM_POSTPROCOPO_ID_PEAKMETER \ + 0x00010D83 +#define ASM_STREAM_POSTPROCOPO_ID_NONE \ + 0x00010C68 +#define ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL \ + 0x00010D8B +#define ASM_STREAM_PREPROCOPO_ID_DEFAULT \ + ASM_STREAM_POSTPROCOPO_ID_DEFAULT +#define ASM_STREAM_PREPROCOPO_ID_NONE \ + ASM_STREAM_POSTPROCOPO_ID_NONE +#define ADM_CMD_COPP_OPENOPOLOGY_ID_NONE_AUDIO_COPP \ + 0x00010312 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MONO_AUDIO_COPP \ + 0x00010313 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP \ + 0x00010314 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_IIR_AUDIO_COPP\ + 0x00010704 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MONO_AUDIO_COPP_MBDRCV2\ + 0x0001070D +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_MBDRCV2\ + 0x0001070E +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_IIR_AUDIO_COPP_MBDRCV2\ + 0x0001070F +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_MBDRC_V3 \ + 0x11000000 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MCH_PEAK_VOL \ + 0x0001031B +#define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_MONO_AUDIO_COPP 0x00010315 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_STEREO_AUDIO_COPP 0x00010316 +#define AUDPROC_COPPOPOLOGY_ID_MCHAN_IIR_AUDIO 0x00010715 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_DEFAULT_AUDIO_COPP 0x00010BE3 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_PEAKMETER_AUDIO_COPP 0x00010317 +#define AUDPROC_MODULE_ID_AIG 0x00010716 +#define AUDPROC_PARAM_ID_AIG_ENABLE 0x00010717 +#define AUDPROC_PARAM_ID_AIG_CONFIG 0x00010718 + +struct Audio_AigParam { + uint16_t mode; +/*< Mode word for enabling AIG/SIG mode . + * Byte offset: 0 + */ + int16_t staticGainL16Q12; +/*< Static input gain when aigMode is set to 1. + * Byte offset: 2 + */ + int16_t initialGainDBL16Q7; +/*module id + * data variable payload containing the pre/postprocessing module id + * values. For an in-band scenario, the variable payload depends on the size + * of the parameter. + */ +struct adm_cmd_rsp_get_pp_topo_module_list_t { + /* Status message (error code). */ + uint32_t status; +} __packed; + +struct audproc_topology_module_id_info_t { + uint32_t num_modules; +} __packed; + +/* end_addtogroup audio_pp_module_ids */ + +/* @ingroup audio_pp_module_ids + * ID of the Volume Control module pre/postprocessing block. + * This module supports the following parameter IDs: + * - #ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN + * - #ASM_PARAM_ID_MULTICHANNEL_GAIN + * - #ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG + * - #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS + * - #ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS + * - #ASM_PARAM_ID_MULTICHANNEL_GAIN + * - #ASM_PARAM_ID_MULTICHANNEL_MUTE + */ +#define ASM_MODULE_ID_VOL_CTRL 0x00010BFE +#define ASM_MODULE_ID_VOL_CTRL2 0x00010910 +#define AUDPROC_MODULE_ID_VOL_CTRL ASM_MODULE_ID_VOL_CTRL + +/* @addtogroup audio_pp_param_ids */ +/* ID of the master gain parameter used by the #ASM_MODULE_ID_VOL_CTRL + * module. + * @messagepayload + * @structure{asm_volume_ctrl_master_gain} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN.tex} + */ +#define ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN 0x00010BFF +#define AUDPROC_PARAM_ID_VOL_CTRL_MASTER_GAIN ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN + +/* ID of the left/right channel gain parameter used by the + * #ASM_MODULE_ID_VOL_CTRL module. + * @messagepayload + * @structure{asm_volume_ctrl_lr_chan_gain} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_MULTICHANNEL_GAIN.tex} + */ +#define ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN 0x00010C00 + +/* ID of the mute configuration parameter used by the + * #ASM_MODULE_ID_VOL_CTRL module. + * @messagepayload + * @structure{asm_volume_ctrl_mute_config} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG.tex} + */ +#define ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG 0x00010C01 + +/* ID of the soft stepping volume parameters used by the + * #ASM_MODULE_ID_VOL_CTRL module. + * @messagepayload + * @structure{asm_soft_step_volume_params} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMET + * ERS.tex} + */ +#define ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS 0x00010C29 +#define AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS\ + ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS + +/* ID of the soft pause parameters used by the #ASM_MODULE_ID_VOL_CTRL + * module. + */ +#define ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS 0x00010D6A + +/* ID of the multiple-channel volume control parameters used by the + * #ASM_MODULE_ID_VOL_CTRL module. + */ +#define ASM_PARAM_ID_MULTICHANNEL_GAIN 0x00010713 + +/* ID of the multiple-channel mute configuration parameters used by the + * #ASM_MODULE_ID_VOL_CTRL module. + */ + +#define ASM_PARAM_ID_MULTICHANNEL_MUTE 0x00010714 + +/* Structure for the master gain parameter for a volume control + * module. + */ + + +/* @brief Payload of the #ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN + * parameter used by the Volume Control module. + */ + + + +struct asm_volume_ctrl_master_gain { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint16_t master_gain; + /* Linear gain in Q13 format. */ + + uint16_t reserved; + /* Clients must set this field to zero. */ +} __packed; + + +struct asm_volume_ctrl_lr_chan_gain { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + + uint16_t l_chan_gain; + /*< Linear gain in Q13 format for the left channel. */ + + uint16_t r_chan_gain; + /*< Linear gain in Q13 format for the right channel.*/ +} __packed; + + +/* Structure for the mute configuration parameter for a + * volume control module. + */ + + +/* @brief Payload of the #ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG + * parameter used by the Volume Control module. + */ + + +struct asm_volume_ctrl_mute_config { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint32_t mute_flag; +/*< Specifies whether mute is disabled (0) or enabled (nonzero).*/ + +} __packed; + +/* + * Supported parameters for a soft stepping linear ramping curve. + */ +#define ASM_PARAM_SVC_RAMPINGCURVE_LINEAR 0 + +/* + * Exponential ramping curve. + */ +#define ASM_PARAM_SVC_RAMPINGCURVE_EXP 1 + +/* + * Logarithmic ramping curve. + */ +#define ASM_PARAM_SVC_RAMPINGCURVE_LOG 2 + +/* Structure for holding soft stepping volume parameters. */ + + +/* Payload of the #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS + * parameters used by the Volume Control module. + */ +struct asm_soft_step_volume_params { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint32_t period; +/*< Period in milliseconds. + * Supported values: 0 to 15000 + */ + + uint32_t step; +/*< Step in microseconds. + * Supported values: 0 to 15000000 + */ + + uint32_t ramping_curve; +/*< Ramping curve type. + * Supported values: + * - #ASM_PARAM_SVC_RAMPINGCURVE_LINEAR + * - #ASM_PARAM_SVC_RAMPINGCURVE_EXP + * - #ASM_PARAM_SVC_RAMPINGCURVE_LOG + */ +} __packed; + + +/* Structure for holding soft pause parameters. */ + + +/* Payload of the #ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS + * parameters used by the Volume Control module. + */ + + +struct asm_soft_pause_params { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint32_t enable_flag; +/*< Specifies whether soft pause is disabled (0) or enabled + * (nonzero). + */ + + + + uint32_t period; +/*< Period in milliseconds. + * Supported values: 0 to 15000 + */ + + uint32_t step; +/*< Step in microseconds. + * Supported values: 0 to 15000000 + */ + + uint32_t ramping_curve; +/*< Ramping curve. + * Supported values: + * - #ASM_PARAM_SVC_RAMPINGCURVE_LINEAR + * - #ASM_PARAM_SVC_RAMPINGCURVE_EXP + * - #ASM_PARAM_SVC_RAMPINGCURVE_LOG + */ +} __packed; + + +/* Maximum number of channels.*/ +#define VOLUME_CONTROL_MAX_CHANNELS 8 + +/* Structure for holding one channel type - gain pair. */ + + +/* Payload of the #ASM_PARAM_ID_MULTICHANNEL_GAIN channel + * type/gain pairs used by the Volume Control module. \n \n This + * structure immediately follows the + * asm_volume_ctrl_multichannel_gain structure. + */ + + +struct asm_volume_ctrl_channeltype_gain_pair { + uint8_t channeltype; + /* + * Channel type for which the gain setting is to be applied. + * Supported values: + * - #PCM_CHANNEL_L + * - #PCM_CHANNEL_R + * - #PCM_CHANNEL_C + * - #PCM_CHANNEL_LS + * - #PCM_CHANNEL_RS + * - #PCM_CHANNEL_LFE + * - #PCM_CHANNEL_CS + * - #PCM_CHANNEL_LB + * - #PCM_CHANNEL_RB + * - #PCM_CHANNELS + * - #PCM_CHANNEL_CVH + * - #PCM_CHANNEL_MS + * - #PCM_CHANNEL_FLC + * - #PCM_CHANNEL_FRC + * - #PCM_CHANNEL_RLC + * - #PCM_CHANNEL_RRC + */ + + uint8_t reserved1; + /* Clients must set this field to zero. */ + + uint8_t reserved2; + /* Clients must set this field to zero. */ + + uint8_t reserved3; + /* Clients must set this field to zero. */ + + uint32_t gain; + /* + * Gain value for this channel in Q28 format. + * Supported values: Any + */ +} __packed; + + +/* Structure for the multichannel gain command */ + + +/* Payload of the #ASM_PARAM_ID_MULTICHANNEL_GAIN + * parameters used by the Volume Control module. + */ + + +struct asm_volume_ctrl_multichannel_gain { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint32_t num_channels; + /* + * Number of channels for which gain values are provided. Any + * channels present in the data for which gain is not provided are + * set to unity gain. + * Supported values: 1 to 8 + */ + + struct asm_volume_ctrl_channeltype_gain_pair + gain_data[VOLUME_CONTROL_MAX_CHANNELS]; + /* Array of channel type/gain pairs.*/ +} __packed; + + +/* Structure for holding one channel type - mute pair. */ + + +/* Payload of the #ASM_PARAM_ID_MULTICHANNEL_MUTE channel + * type/mute setting pairs used by the Volume Control module. \n \n + * This structure immediately follows the + * asm_volume_ctrl_multichannel_mute structure. + */ + + +struct asm_volume_ctrl_channelype_mute_pair { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint8_t channelype; +/*< Channel type for which the mute setting is to be applied. + * Supported values: + * - #PCM_CHANNEL_L + * - #PCM_CHANNEL_R + * - #PCM_CHANNEL_C + * - #PCM_CHANNEL_LS + * - #PCM_CHANNEL_RS + * - #PCM_CHANNEL_LFE + * - #PCM_CHANNEL_CS + * - #PCM_CHANNEL_LB + * - #PCM_CHANNEL_RB + * - #PCM_CHANNELS + * - #PCM_CHANNEL_CVH + * - #PCM_CHANNEL_MS + * - #PCM_CHANNEL_FLC + * - #PCM_CHANNEL_FRC + * - #PCM_CHANNEL_RLC + * - #PCM_CHANNEL_RRC + */ + + uint8_t reserved1; + /*< Clients must set this field to zero. */ + + uint8_t reserved2; + /*< Clients must set this field to zero. */ + + uint8_t reserved3; + /*< Clients must set this field to zero. */ + + uint32_t mute; +/*< Mute setting for this channel. + * Supported values: + * - 0 = Unmute + * - Nonzero = Mute + */ +} __packed; + + +/* Structure for the multichannel mute command */ + + +/* @brief Payload of the #ASM_PARAM_ID_MULTICHANNEL_MUTE + * parameters used by the Volume Control module. + */ + + +struct asm_volume_ctrl_multichannel_mute { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint32_t num_channels; +/*< Number of channels for which mute configuration is + * provided. Any channels present in the data for which mute + * configuration is not provided are set to unmute. + * Supported values: 1 to 8 + */ + +struct asm_volume_ctrl_channelype_mute_pair + mute_data[VOLUME_CONTROL_MAX_CHANNELS]; + /*< Array of channel type/mute setting pairs.*/ +} __packed; +/* end_addtogroup audio_pp_param_ids */ + +/* audio_pp_module_ids + * ID of the IIR Tuning Filter module. + * This module supports the following parameter IDs: + * - #ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG + * - #ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN + * - #ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS + */ +#define ASM_MODULE_ID_IIRUNING_FILTER 0x00010C02 + +/* @addtogroup audio_pp_param_ids */ +/* ID of the IIR tuning filter enable parameter used by the + * #ASM_MODULE_ID_IIRUNING_FILTER module. + * @messagepayload + * @structure{asm_iiruning_filter_enable} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CO + * NFIG.tex} + */ +#define ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG 0x00010C03 + +/* ID of the IIR tuning filter pregain parameter used by the + * #ASM_MODULE_ID_IIRUNING_FILTER module. + */ +#define ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN 0x00010C04 + +/* ID of the IIR tuning filter configuration parameters used by the + * #ASM_MODULE_ID_IIRUNING_FILTER module. + */ +#define ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS 0x00010C05 + +/* Structure for an enable configuration parameter for an + * IIR tuning filter module. + */ + + +/* @brief Payload of the #ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG + * parameter used by the IIR Tuning Filter module. + */ +struct asm_iiruning_filter_enable { + uint32_t enable_flag; +/*< Specifies whether the IIR tuning filter is disabled (0) or + * enabled (1). + */ +} __packed; + +/* Structure for the pregain parameter for an IIR tuning filter module. */ + + +/* Payload of the #ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN + * parameters used by the IIR Tuning Filter module. + */ +struct asm_iiruning_filter_pregain { + uint16_t pregain; + /*< Linear gain in Q13 format. */ + + uint16_t reserved; + /*< Clients must set this field to zero.*/ +} __packed; + +/* Structure for the configuration parameter for an IIR tuning filter + * module. + */ + + +/* @brief Payload of the #ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS + * parameters used by the IIR Tuning Filter module. \n + * \n + * This structure is followed by the IIR filter coefficients: \n + * - Sequence of int32_t FilterCoeffs \n + * Five coefficients for each band. Each coefficient is in int32_t format, in + * the order of b0, b1, b2, a1, a2. + * - Sequence of int16_t NumShiftFactor \n + * One int16_t per band. The numerator shift factor is related to the Q + * factor of the filter coefficients. + * - Sequence of uint16_t PanSetting \n + * One uint16_t per band, indicating if the filter is applied to left (0), + * right (1), or both (2) channels. + */ +struct asm_iir_filter_config_params { + uint16_t num_biquad_stages; +/*< Number of bands. + * Supported values: 0 to 20 + */ + + uint16_t reserved; + /*< Clients must set this field to zero.*/ +} __packed; + +/* audio_pp_module_ids + * ID of the Multiband Dynamic Range Control (MBDRC) module on the Tx/Rx + * paths. + * This module supports the following parameter IDs: + * - #ASM_PARAM_ID_MBDRC_ENABLE + * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS + */ +#define ASM_MODULE_ID_MBDRC 0x00010C06 + +/* audio_pp_param_ids */ +/* ID of the MBDRC enable parameter used by the #ASM_MODULE_ID_MBDRC module. + * @messagepayload + * @structure{asm_mbdrc_enable} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_MBDRC_ENABLE.tex} + */ +#define ASM_PARAM_ID_MBDRC_ENABLE 0x00010C07 + +/* ID of the MBDRC configuration parameters used by the + * #ASM_MODULE_ID_MBDRC module. + * @messagepayload + * @structure{asm_mbdrc_config_params} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_MBDRC_CONFIG_PARAMS.tex} + * + * @parspace Sub-band DRC configuration parameters + * @structure{asm_subband_drc_config_params} + * @tablespace + * @inputtable{Audio_Postproc_ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_subband_DRC.tex} + * + * @keep{6} + * To obtain legacy ADRC from MBDRC, use the calibration tool to: + * + * - Enable MBDRC (EnableFlag = TRUE) + * - Set number of bands to 1 (uiNumBands = 1) + * - Enable the first MBDRC band (DrcMode[0] = DRC_ENABLED = 1) + * - Clear the first band mute flag (MuteFlag[0] = 0) + * - Set the first band makeup gain to unity (compMakeUpGain[0] = 0x2000) + * - Use the legacy ADRC parameters to calibrate the rest of the MBDRC + * parameters. + */ +#define ASM_PARAM_ID_MBDRC_CONFIG_PARAMS 0x00010C08 + +/* end_addtogroup audio_pp_param_ids */ + +/* audio_pp_module_ids + * ID of the MMBDRC module version 2 pre/postprocessing block. + * This module differs from the original MBDRC (#ASM_MODULE_ID_MBDRC) in + * the length of the filters used in each sub-band. + * This module supports the following parameter ID: + * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_IMPROVED_FILTBANK_V2 + */ +#define ASM_MODULE_ID_MBDRCV2 0x0001070B + +/* @addtogroup audio_pp_param_ids */ +/* ID of the configuration parameters used by the + * #ASM_MODULE_ID_MBDRCV2 module for the improved filter structure + * of the MBDRC v2 pre/postprocessing block. + * The update to this configuration structure from the original + * MBDRC is the number of filter coefficients in the filter + * structure. The sequence for is as follows: + * - 1 band = 0 FIR coefficient + 1 mute flag + uint16_t padding + * - 2 bands = 141 FIR coefficients + 2 mute flags + uint16_t padding + * - 3 bands = 141+81 FIR coefficients + 3 mute flags + uint16_t padding + * - 4 bands = 141+81+61 FIR coefficients + 4 mute flags + uint16_t + * padding + * - 5 bands = 141+81+61+61 FIR coefficients + 5 mute flags + + * uint16_t padding + * This block uses the same parameter structure as + * #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS. + */ +#define ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_IMPROVED_FILTBANK_V2 \ + 0x0001070C + +#define ASM_MODULE_ID_MBDRCV3 0x0001090B +/* + * ID of the MMBDRC module version 3 pre/postprocessing block. + * This module differs from MBDRCv2 (#ASM_MODULE_ID_MBDRCV2) in + * that it supports both 16- and 24-bit data. + * This module supports the following parameter ID: + * - #ASM_PARAM_ID_MBDRC_ENABLE + * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS + * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_V3 + * - #ASM_PARAM_ID_MBDRC_FILTER_XOVER_FREQS + */ + +/* Structure for the enable parameter for an MBDRC module. */ + + +/* Payload of the #ASM_PARAM_ID_MBDRC_ENABLE parameter used by the + * MBDRC module. + */ +struct asm_mbdrc_enable { + uint32_t enable_flag; +/*< Specifies whether MBDRC is disabled (0) or enabled (nonzero).*/ +} __packed; + +/* Structure for the configuration parameters for an MBDRC module. */ + + +/* Payload of the #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS + * parameters used by the MBDRC module. \n \n Following this + * structure is the payload for sub-band DRC configuration + * parameters (asm_subband_drc_config_params). This sub-band + * structure must be repeated for each band. + */ + + +struct asm_mbdrc_config_params { + uint16_t num_bands; +/*< Number of bands. + * Supported values: 1 to 5 + */ + + int16_t limiterhreshold; +/*< Threshold in decibels for the limiter output. + * Supported values: -72 to 18 \n + * Recommended value: 3994 (-0.22 db in Q3.12 format) + */ + + int16_t limiter_makeup_gain; +/*< Makeup gain in decibels for the limiter output. + * Supported values: -42 to 42 \n + * Recommended value: 256 (0 dB in Q7.8 format) + */ + + int16_t limiter_gc; +/*< Limiter gain recovery coefficient. + * Supported values: 0.5 to 0.99 \n + * Recommended value: 32440 (0.99 in Q15 format) + */ + + int16_t limiter_delay; +/*< Limiter delay in samples. + * Supported values: 0 to 10 \n + * Recommended value: 262 (0.008 samples in Q15 format) + */ + + int16_t limiter_max_wait; +/*< Maximum limiter waiting time in samples. + * Supported values: 0 to 10 \n + * Recommended value: 262 (0.008 samples in Q15 format) + */ +} __packed; + +/* DRC configuration structure for each sub-band of an MBDRC module. */ + + +/* Payload of the #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS DRC + * configuration parameters for each sub-band in the MBDRC module. + * After this DRC structure is configured for valid bands, the next + * MBDRC setparams expects the sequence of sub-band MBDRC filter + * coefficients (the length depends on the number of bands) plus the + * mute flag for that band plus uint16_t padding. + * + * @keep{10} + * The filter coefficient and mute flag are of type int16_t: + * - FIR coefficient = int16_t firFilter + * - Mute flag = int16_t fMuteFlag + * + * The sequence is as follows: + * - 1 band = 0 FIR coefficient + 1 mute flag + uint16_t padding + * - 2 bands = 97 FIR coefficients + 2 mute flags + uint16_t padding + * - 3 bands = 97+33 FIR coefficients + 3 mute flags + uint16_t padding + * - 4 bands = 97+33+33 FIR coefficients + 4 mute flags + uint16_t padding + * - 5 bands = 97+33+33+33 FIR coefficients + 5 mute flags + uint16_t padding + * + * For improved filterbank, the sequence is as follows: + * - 1 band = 0 FIR coefficient + 1 mute flag + uint16_t padding + * - 2 bands = 141 FIR coefficients + 2 mute flags + uint16_t padding + * - 3 bands = 141+81 FIR coefficients + 3 mute flags + uint16_t padding + * - 4 bands = 141+81+61 FIR coefficients + 4 mute flags + uint16_t padding + * - 5 bands = 141+81+61+61 FIR coefficients + 5 mute flags + uint16_t padding + */ +struct asm_subband_drc_config_params { + int16_t drc_stereo_linked_flag; +/*< Specifies whether all stereo channels have the same applied + * dynamics (1) or if they process their dynamics independently (0). + * Supported values: + * - 0 -- Not linked + * - 1 -- Linked + */ + + int16_t drc_mode; +/*< Specifies whether DRC mode is bypassed for sub-bands. + * Supported values: + * - 0 -- Disabled + * - 1 -- Enabled + */ + + int16_t drc_down_sample_level; +/*< DRC down sample level. + * Supported values: @ge 1 + */ + + int16_t drc_delay; +/*< DRC delay in samples. + * Supported values: 0 to 1200 + */ + + uint16_t drc_rmsime_avg_const; +/*< RMS signal energy time-averaging constant. + * Supported values: 0 to 2^16-1 + */ + + uint16_t drc_makeup_gain; +/*< DRC makeup gain in decibels. + * Supported values: 258 to 64917 + */ + /* Down expander settings */ + int16_t down_expdrhreshold; +/*< Down expander threshold. + * Supported Q7 format values: 1320 to up_cmpsrhreshold + */ + + int16_t down_expdr_slope; +/*< Down expander slope. + * Supported Q8 format values: -32768 to 0. + */ + + uint32_t down_expdr_attack; +/*< Down expander attack constant. + * Supported Q31 format values: 196844 to 2^31. + */ + + uint32_t down_expdr_release; +/*< Down expander release constant. + * Supported Q31 format values: 19685 to 2^31 + */ + + uint16_t down_expdr_hysteresis; +/*< Down expander hysteresis constant. + * Supported Q14 format values: 1 to 32690 + */ + + uint16_t reserved; + /*< Clients must set this field to zero. */ + + int32_t down_expdr_min_gain_db; +/*< Down expander minimum gain. + * Supported Q23 format values: -805306368 to 0. + */ + + /* Up compressor settings */ + + int16_t up_cmpsrhreshold; +/*< Up compressor threshold. + * Supported Q7 format values: down_expdrhreshold to + * down_cmpsrhreshold. + */ + + uint16_t up_cmpsr_slope; +/*< Up compressor slope. + * Supported Q16 format values: 0 to 64881. + */ + + uint32_t up_cmpsr_attack; +/*< Up compressor attack constant. + * Supported Q31 format values: 196844 to 2^31. + */ + + uint32_t up_cmpsr_release; +/*< Up compressor release constant. + * Supported Q31 format values: 19685 to 2^31. + */ + + uint16_t up_cmpsr_hysteresis; +/*< Up compressor hysteresis constant. + * Supported Q14 format values: 1 to 32690. + */ + + /* Down compressor settings */ + + int16_t down_cmpsrhreshold; +/*< Down compressor threshold. + * Supported Q7 format values: up_cmpsrhreshold to 11560. + */ + + uint16_t down_cmpsr_slope; +/*< Down compressor slope. + * Supported Q16 format values: 0 to 64881. + */ + + uint16_t reserved1; +/*< Clients must set this field to zero. */ + + uint32_t down_cmpsr_attack; +/*< Down compressor attack constant. + * Supported Q31 format values: 196844 to 2^31. + */ + + uint32_t down_cmpsr_release; +/*< Down compressor release constant. + * Supported Q31 format values: 19685 to 2^31. + */ + + uint16_t down_cmpsr_hysteresis; +/*< Down compressor hysteresis constant. + * Supported Q14 values: 1 to 32690. + */ + + uint16_t reserved2; +/*< Clients must set this field to zero.*/ +} __packed; + +#define ASM_MODULE_ID_EQUALIZER 0x00010C27 +#define ASM_PARAM_ID_EQUALIZER_PARAMETERS 0x00010C28 + +#define ASM_MAX_EQ_BANDS 12 + +struct asm_eq_per_band_params { + uint32_t band_idx; +/*< Band index. + * Supported values: 0 to 11 + */ + + uint32_t filterype; +/*< Type of filter. + * Supported values: + * - #ASM_PARAM_EQYPE_NONE + * - #ASM_PARAM_EQ_BASS_BOOST + * - #ASM_PARAM_EQ_BASS_CUT + * - #ASM_PARAM_EQREBLE_BOOST + * - #ASM_PARAM_EQREBLE_CUT + * - #ASM_PARAM_EQ_BAND_BOOST + * - #ASM_PARAM_EQ_BAND_CUT + */ + + uint32_t center_freq_hz; + /*< Filter band center frequency in Hertz. */ + + int32_t filter_gain; +/*< Filter band initial gain. + * Supported values: +12 to -12 dB in 1 dB increments + */ + + int32_t q_factor; +/*< Filter band quality factor expressed as a Q8 number, i.e., a + * fixed-point number with q factor of 8. For example, 3000/(2^8). + */ +} __packed; + +struct asm_eq_params { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; + uint32_t enable_flag; +/*< Specifies whether the equalizer module is disabled (0) or enabled + * (nonzero). + */ + + uint32_t num_bands; +/*< Number of bands. + * Supported values: 1 to 12 + */ + struct asm_eq_per_band_params eq_bands[ASM_MAX_EQ_BANDS]; + +} __packed; + +/* No equalizer effect.*/ +#define ASM_PARAM_EQYPE_NONE 0 + +/* Bass boost equalizer effect.*/ +#define ASM_PARAM_EQ_BASS_BOOST 1 + +/*Bass cut equalizer effect.*/ +#define ASM_PARAM_EQ_BASS_CUT 2 + +/* Treble boost equalizer effect */ +#define ASM_PARAM_EQREBLE_BOOST 3 + +/* Treble cut equalizer effect.*/ +#define ASM_PARAM_EQREBLE_CUT 4 + +/* Band boost equalizer effect.*/ +#define ASM_PARAM_EQ_BAND_BOOST 5 + +/* Band cut equalizer effect.*/ +#define ASM_PARAM_EQ_BAND_CUT 6 + +/* Voice get & set params */ +#define VOICE_CMD_SET_PARAM 0x0001133D +#define VOICE_CMD_GET_PARAM 0x0001133E +#define VOICE_EVT_GET_PARAM_ACK 0x00011008 + + +/* ID of the Bass Boost module. + * This module supports the following parameter IDs: + * - #AUDPROC_PARAM_ID_BASS_BOOST_ENABLE + * - #AUDPROC_PARAM_ID_BASS_BOOST_MODE + * - #AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH + */ +#define AUDPROC_MODULE_ID_BASS_BOOST 0x000108A1 +/* ID of the Bass Boost enable parameter used by + * AUDPROC_MODULE_ID_BASS_BOOST. + */ +#define AUDPROC_PARAM_ID_BASS_BOOST_ENABLE 0x000108A2 +/* ID of the Bass Boost mode parameter used by + * AUDPROC_MODULE_ID_BASS_BOOST. + */ +#define AUDPROC_PARAM_ID_BASS_BOOST_MODE 0x000108A3 +/* ID of the Bass Boost strength parameter used by + * AUDPROC_MODULE_ID_BASS_BOOST. + */ +#define AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH 0x000108A4 + +/* ID of the PBE module. + * This module supports the following parameter IDs: + * - #AUDPROC_PARAM_ID_PBE_ENABLE + * - #AUDPROC_PARAM_ID_PBE_PARAM_CONFIG + */ +#define AUDPROC_MODULE_ID_PBE 0x00010C2A +/* ID of the Bass Boost enable parameter used by + * AUDPROC_MODULE_ID_BASS_BOOST. + */ +#define AUDPROC_PARAM_ID_PBE_ENABLE 0x00010C2B +/* ID of the Bass Boost mode parameter used by + * AUDPROC_MODULE_ID_BASS_BOOST. + */ +#define AUDPROC_PARAM_ID_PBE_PARAM_CONFIG 0x00010C49 + +/* ID of the Virtualizer module. This module supports the + * following parameter IDs: + * - #AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE + * - #AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH + * - #AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE + * - #AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST + */ +#define AUDPROC_MODULE_ID_VIRTUALIZER 0x000108A5 +/* ID of the Virtualizer enable parameter used by + * AUDPROC_MODULE_ID_VIRTUALIZER. + */ +#define AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE 0x000108A6 +/* ID of the Virtualizer strength parameter used by + * AUDPROC_MODULE_ID_VIRTUALIZER. + */ +#define AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH 0x000108A7 +/* ID of the Virtualizer out type parameter used by + * AUDPROC_MODULE_ID_VIRTUALIZER. + */ +#define AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE 0x000108A8 +/* ID of the Virtualizer out type parameter used by + * AUDPROC_MODULE_ID_VIRTUALIZER. + */ +#define AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST 0x000108A9 + +/* ID of the Reverb module. This module supports the following + * parameter IDs: + * - #AUDPROC_PARAM_ID_REVERB_ENABLE + * - #AUDPROC_PARAM_ID_REVERB_MODE + * - #AUDPROC_PARAM_ID_REVERB_PRESET + * - #AUDPROC_PARAM_ID_REVERB_WET_MIX + * - #AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST + * - #AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL + * - #AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL + * - #AUDPROC_PARAM_ID_REVERB_DECAY_TIME + * - #AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO + * - #AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL + * - #AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY + * - #AUDPROC_PARAM_ID_REVERB_LEVEL + * - #AUDPROC_PARAM_ID_REVERB_DELAY + * - #AUDPROC_PARAM_ID_REVERB_DIFFUSION + * - #AUDPROC_PARAM_ID_REVERB_DENSITY + */ +#define AUDPROC_MODULE_ID_REVERB 0x000108AA +/* ID of the Reverb enable parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_ENABLE 0x000108AB +/* ID of the Reverb mode parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_MODE 0x000108AC +/* ID of the Reverb preset parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_PRESET 0x000108AD +/* ID of the Reverb wet mix parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_WET_MIX 0x000108AE +/* ID of the Reverb gain adjust parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST 0x000108AF +/* ID of the Reverb room level parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL 0x000108B0 +/* ID of the Reverb room hf level parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL 0x000108B1 +/* ID of the Reverb decay time parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_DECAY_TIME 0x000108B2 +/* ID of the Reverb decay hf ratio parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO 0x000108B3 +/* ID of the Reverb reflections level parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL 0x000108B4 +/* ID of the Reverb reflections delay parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY 0x000108B5 +/* ID of the Reverb level parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_LEVEL 0x000108B6 +/* ID of the Reverb delay parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_DELAY 0x000108B7 +/* ID of the Reverb diffusion parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_DIFFUSION 0x000108B8 +/* ID of the Reverb density parameter used by + * AUDPROC_MODULE_ID_REVERB. + */ +#define AUDPROC_PARAM_ID_REVERB_DENSITY 0x000108B9 + +/* ID of the Popless Equalizer module. This module supports the + * following parameter IDs: + * - #AUDPROC_PARAM_ID_EQ_ENABLE + * - #AUDPROC_PARAM_ID_EQ_CONFIG + * - #AUDPROC_PARAM_ID_EQ_NUM_BANDS + * - #AUDPROC_PARAM_ID_EQ_BAND_LEVELS + * - #AUDPROC_PARAM_ID_EQ_BAND_LEVEL_RANGE + * - #AUDPROC_PARAM_ID_EQ_BAND_FREQS + * - #AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ_RANGE + * - #AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ + * - #AUDPROC_PARAM_ID_EQ_BAND_INDEX + * - #AUDPROC_PARAM_ID_EQ_PRESET_ID + * - #AUDPROC_PARAM_ID_EQ_NUM_PRESETS + * - #AUDPROC_PARAM_ID_EQ_GET_PRESET_NAME + */ +#define AUDPROC_MODULE_ID_POPLESS_EQUALIZER 0x000108BA +/* ID of the Popless Equalizer enable parameter used by + * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. + */ +#define AUDPROC_PARAM_ID_EQ_ENABLE 0x000108BB +/* ID of the Popless Equalizer config parameter used by + * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. + */ +#define AUDPROC_PARAM_ID_EQ_CONFIG 0x000108BC +/* ID of the Popless Equalizer number of bands parameter used + * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is + * used for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_NUM_BANDS 0x000108BD +/* ID of the Popless Equalizer band levels parameter used by + * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is + * used for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_BAND_LEVELS 0x000108BE +/* ID of the Popless Equalizer band level range parameter used + * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is + * used for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_BAND_LEVEL_RANGE 0x000108BF +/* ID of the Popless Equalizer band frequencies parameter used + * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is + * used for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_BAND_FREQS 0x000108C0 +/* ID of the Popless Equalizer single band frequency range + * parameter used by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. + * This param ID is used for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ_RANGE 0x000108C1 +/* ID of the Popless Equalizer single band frequency parameter + * used by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID + * is used for set param only. + */ +#define AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ 0x000108C2 +/* ID of the Popless Equalizer band index parameter used by + * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. + */ +#define AUDPROC_PARAM_ID_EQ_BAND_INDEX 0x000108C3 +/* ID of the Popless Equalizer preset id parameter used by + * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is used + * for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_PRESET_ID 0x000108C4 +/* ID of the Popless Equalizer number of presets parameter used + * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is used + * for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_NUM_PRESETS 0x000108C5 +/* ID of the Popless Equalizer preset name parameter used by + * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is used + * for get param only. + */ +#define AUDPROC_PARAM_ID_EQ_PRESET_NAME 0x000108C6 + +/* Set Q6 topologies */ +#define ASM_CMD_ADD_TOPOLOGIES 0x00010DBE +#define ADM_CMD_ADD_TOPOLOGIES 0x00010335 +#define AFE_CMD_ADD_TOPOLOGIES 0x000100f8 +/* structure used for both ioctls */ +struct cmd_set_topologies { + struct apr_hdr hdr; + u32 payload_addr_lsw; + /* LSW of parameter data payload address.*/ + u32 payload_addr_msw; + /* MSW of parameter data payload address.*/ + u32 mem_map_handle; + /* Memory map handle returned by mem map command */ + u32 payload_size; + /* Size in bytes of the variable payload in shared memory */ +} __packed; + +/* This module represents the Rx processing of Feedback speaker protection. + * It contains the excursion control, thermal protection, + * analog clip manager features in it. + * This module id will support following param ids. + * - AFE_PARAM_ID_FBSP_MODE_RX_CFG + */ + +#define AFE_MODULE_FB_SPKR_PROT_RX 0x0001021C +#define AFE_MODULE_FB_SPKR_PROT_V2_RX 0x0001025F + +#define AFE_PARAM_ID_FBSP_MODE_RX_CFG 0x0001021D +#define AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG 0x00010260 + +struct asm_fbsp_mode_rx_cfg { + uint32_t minor_version; + uint32_t mode; +} __packed; + +/* This module represents the VI processing of feedback speaker protection. + * It will receive Vsens and Isens from codec and generates necessary + * parameters needed by Rx processing. + * This module id will support following param ids. + * - AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG + * - AFE_PARAM_ID_CALIB_RES_CFG + * - AFE_PARAM_ID_FEEDBACK_PATH_CFG + */ + +#define AFE_MODULE_FB_SPKR_PROT_VI_PROC 0x00010226 +#define AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2 0x0001026A + +#define AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG 0x0001022A +#define AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2 0x0001026B + +struct asm_spkr_calib_vi_proc_cfg { + uint32_t minor_version; + uint32_t operation_mode; + uint32_t r0_t0_selection_flag[SP_V2_NUM_MAX_SPKR]; + int32_t r0_cali_q24[SP_V2_NUM_MAX_SPKR]; + int16_t t0_cali_q6[SP_V2_NUM_MAX_SPKR]; + uint32_t quick_calib_flag; +} __packed; + +#define AFE_PARAM_ID_CALIB_RES_CFG 0x0001022B +#define AFE_PARAM_ID_CALIB_RES_CFG_V2 0x0001026E + +struct asm_calib_res_cfg { + uint32_t minor_version; + int32_t r0_cali_q24[SP_V2_NUM_MAX_SPKR]; + uint32_t th_vi_ca_state; +} __packed; + +#define AFE_PARAM_ID_FEEDBACK_PATH_CFG 0x0001022C +#define AFE_MODULE_FEEDBACK 0x00010257 + +struct asm_feedback_path_cfg { + uint32_t minor_version; + int32_t dst_portid; + int32_t num_channels; + int32_t chan_info[4]; +} __packed; + +#define AFE_PARAM_ID_MODE_VI_PROC_CFG 0x00010227 + +struct asm_mode_vi_proc_cfg { + uint32_t minor_version; + uint32_t cal_mode; +} __packed; + +#define AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI 0x0001026A +#define AFE_PARAM_ID_SP_V2_TH_VI_MODE_CFG 0x0001026B +#define AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG 0x0001029F +#define AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS 0x000102A0 + +struct afe_sp_th_vi_mode_cfg { + uint32_t minor_version; + uint32_t operation_mode; + /* + * Operation mode of thermal VI module. + * 0 -- Normal Running mode + * 1 -- Calibration mode + * 2 -- FTM mode + */ + uint32_t r0t0_selection_flag[SP_V2_NUM_MAX_SPKR]; + /* + * Specifies which set of R0, T0 values the algorithm will use. + * This field is valid only in Normal mode (operation_mode = 0). + * 0 -- Use calibrated R0, T0 value + * 1 -- Use safe R0, T0 value + */ + int32_t r0_cali_q24[SP_V2_NUM_MAX_SPKR]; + /* + * Calibration point resistance per device. This field is valid + * only in Normal mode (operation_mode = 0). + * values 33554432 to 1073741824 Ohms (in Q24 format) + */ + int16_t t0_cali_q6[SP_V2_NUM_MAX_SPKR]; + /* + * Calibration point temperature per device. This field is valid + * in both Normal mode and Calibration mode. + * values -1920 to 5120 degrees C (in Q6 format) + */ + uint32_t quick_calib_flag; + /* + * Indicates whether calibration is to be done in quick mode or not. + * This field is valid only in Calibration mode (operation_mode = 1). + * 0 -- Disabled + * 1 -- Enabled + */ +} __packed; + +struct afe_sp_th_vi_ftm_cfg { + uint32_t minor_version; + uint32_t wait_time_ms[SP_V2_NUM_MAX_SPKR]; + /* + * Wait time to heat up speaker before collecting statistics + * for ftm mode in ms. + * values 0 to 4294967295 ms + */ + uint32_t ftm_time_ms[SP_V2_NUM_MAX_SPKR]; + /* + * duration for which FTM statistics are collected in ms. + * values 0 to 2000 ms + */ +} __packed; + +struct afe_sp_th_vi_ftm_params { + uint32_t minor_version; + int32_t dc_res_q24[SP_V2_NUM_MAX_SPKR]; + /* + * DC resistance value in q24 format + * values 0 to 2147483647 Ohms (in Q24 format) + */ + int32_t temp_q22[SP_V2_NUM_MAX_SPKR]; + /* + * temperature value in q22 format + * values -125829120 to 2147483647 degC (in Q22 format) + */ + uint32_t status[SP_V2_NUM_MAX_SPKR]; + /* + * FTM packet status + * 0 - Incorrect operation mode.This status is returned + * when GET_PARAM is called in non FTM Mode + * 1 - Inactive mode -- Port is not yet started. + * 2 - Wait state. wait_time_ms has not yet elapsed + * 3 - In progress state. ftm_time_ms has not yet elapsed. + * 4 - Success. + * 5 - Failed. + */ +} __packed; + +struct afe_sp_th_vi_get_param { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 get_param; + struct afe_port_param_data_v2 pdata; + struct afe_sp_th_vi_ftm_params param; +} __packed; + +struct afe_sp_th_vi_get_param_resp { + uint32_t status; + struct afe_port_param_data_v2 pdata; + struct afe_sp_th_vi_ftm_params param; +} __packed; + + +#define AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI 0x0001026F +#define AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG 0x000102A1 +#define AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG 0x000102A2 +#define AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS 0x000102A3 + +struct afe_sp_ex_vi_mode_cfg { + uint32_t minor_version; + uint32_t operation_mode; + /* + * Operation mode of Excursion VI module. + * 0 - Normal Running mode + * 2 - FTM mode + */ +} __packed; + +struct afe_sp_ex_vi_ftm_cfg { + uint32_t minor_version; + uint32_t wait_time_ms[SP_V2_NUM_MAX_SPKR]; + /* + * Wait time to heat up speaker before collecting statistics + * for ftm mode in ms. + * values 0 to 4294967295 ms + */ + uint32_t ftm_time_ms[SP_V2_NUM_MAX_SPKR]; + /* + * duration for which FTM statistics are collected in ms. + * values 0 to 2000 ms + */ +} __packed; + +struct afe_sp_ex_vi_ftm_params { + uint32_t minor_version; + int32_t freq_q20[SP_V2_NUM_MAX_SPKR]; + /* + * Resonance frequency in q20 format + * values 0 to 2147483647 Hz (in Q20 format) + */ + int32_t resis_q24[SP_V2_NUM_MAX_SPKR]; + /* + * Mechanical resistance in q24 format + * values 0 to 2147483647 Ohms (in Q24 format) + */ + int32_t qmct_q24[SP_V2_NUM_MAX_SPKR]; + /* + * Mechanical Qfactor in q24 format + * values 0 to 2147483647 (in Q24 format) + */ + uint32_t status[SP_V2_NUM_MAX_SPKR]; + /* + * FTM packet status + * 0 - Incorrect operation mode.This status is returned + * when GET_PARAM is called in non FTM Mode. + * 1 - Inactive mode -- Port is not yet started. + * 2 - Wait state. wait_time_ms has not yet elapsed + * 3 - In progress state. ftm_time_ms has not yet elapsed. + * 4 - Success. + * 5 - Failed. + */ +} __packed; + +struct afe_sp_ex_vi_get_param { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 get_param; + struct afe_port_param_data_v2 pdata; + struct afe_sp_ex_vi_ftm_params param; +} __packed; + +struct afe_sp_ex_vi_get_param_resp { + uint32_t status; + struct afe_port_param_data_v2 pdata; + struct afe_sp_ex_vi_ftm_params param; +} __packed; + +union afe_spkr_prot_config { + struct asm_fbsp_mode_rx_cfg mode_rx_cfg; + struct asm_spkr_calib_vi_proc_cfg vi_proc_cfg; + struct asm_feedback_path_cfg feedback_path_cfg; + struct asm_mode_vi_proc_cfg mode_vi_proc_cfg; + struct afe_sp_th_vi_mode_cfg th_vi_mode_cfg; + struct afe_sp_th_vi_ftm_cfg th_vi_ftm_cfg; + struct afe_sp_ex_vi_mode_cfg ex_vi_mode_cfg; + struct afe_sp_ex_vi_ftm_cfg ex_vi_ftm_cfg; +} __packed; + +struct afe_spkr_prot_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + union afe_spkr_prot_config prot_config; +} __packed; + +struct afe_spkr_prot_get_vi_calib { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 get_param; + struct afe_port_param_data_v2 pdata; + struct asm_calib_res_cfg res_cfg; +} __packed; + +struct afe_spkr_prot_calib_get_resp { + uint32_t status; + struct afe_port_param_data_v2 pdata; + struct asm_calib_res_cfg res_cfg; +} __packed; + + +/* SRS TRUMEDIA start */ +/* topology */ +#define SRS_TRUMEDIA_TOPOLOGY_ID 0x00010D90 +/* module */ +#define SRS_TRUMEDIA_MODULE_ID 0x10005010 +/* parameters */ +#define SRS_TRUMEDIA_PARAMS 0x10005011 +#define SRS_TRUMEDIA_PARAMS_WOWHD 0x10005012 +#define SRS_TRUMEDIA_PARAMS_CSHP 0x10005013 +#define SRS_TRUMEDIA_PARAMS_HPF 0x10005014 +#define SRS_TRUMEDIA_PARAMS_AEQ 0x10005015 +#define SRS_TRUMEDIA_PARAMS_HL 0x10005016 +#define SRS_TRUMEDIA_PARAMS_GEQ 0x10005017 + +#define SRS_ID_GLOBAL 0x00000001 +#define SRS_ID_WOWHD 0x00000002 +#define SRS_ID_CSHP 0x00000003 +#define SRS_ID_HPF 0x00000004 +#define SRS_ID_AEQ 0x00000005 +#define SRS_ID_HL 0x00000006 +#define SRS_ID_GEQ 0x00000007 + +#define SRS_CMD_UPLOAD 0x7FFF0000 +#define SRS_PARAM_OFFSET_MASK 0x3FFF0000 +#define SRS_PARAM_VALUE_MASK 0x0000FFFF + +struct srs_trumedia_params_GLOBAL { + uint8_t v1; + uint8_t v2; + uint8_t v3; + uint8_t v4; + uint8_t v5; + uint8_t v6; + uint8_t v7; + uint8_t v8; + uint16_t v9; +} __packed; + +struct srs_trumedia_params_WOWHD { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v4; + uint16_t v5; + uint16_t v6; + uint16_t v7; + uint16_t v8; + uint16_t v____A1; + uint32_t v9; + uint16_t v10; + uint16_t v11; + uint32_t v12[16]; + uint32_t v13[16]; + uint32_t v14[16]; + uint32_t v15[16]; + uint32_t v16; + uint16_t v17; + uint16_t v18; +} __packed; + +struct srs_trumedia_params_CSHP { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v4; + uint16_t v5; + uint16_t v6; + uint16_t v____A1; + uint32_t v7; + uint16_t v8; + uint16_t v9; + uint32_t v10[16]; +} __packed; + +struct srs_trumedia_params_HPF { + uint32_t v1; + uint32_t v2[26]; +} __packed; + +struct srs_trumedia_params_AEQ { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v4; + uint16_t v____A1; + uint32_t v5[74]; + uint32_t v6[74]; + uint16_t v7[2048]; +} __packed; + +struct srs_trumedia_params_HL { + uint16_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v____A1; + int32_t v4; + uint32_t v5; + uint16_t v6; + uint16_t v____A2; + uint32_t v7; +} __packed; + +struct srs_trumedia_params_GEQ { + int16_t v1[10]; +} __packed; +struct srs_trumedia_params { + struct srs_trumedia_params_GLOBAL global; + struct srs_trumedia_params_WOWHD wowhd; + struct srs_trumedia_params_CSHP cshp; + struct srs_trumedia_params_HPF hpf; + struct srs_trumedia_params_AEQ aeq; + struct srs_trumedia_params_HL hl; + struct srs_trumedia_params_GEQ geq; +} __packed; +/* SRS TruMedia end */ + +#define AUDPROC_PARAM_ID_ENABLE 0x00010904 +#define ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS 0x1000FFFF +/* DTS Eagle */ +#define AUDPROC_MODULE_ID_DTS_HPX_PREMIX 0x0001077C +#define AUDPROC_MODULE_ID_DTS_HPX_POSTMIX 0x0001077B +#define ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX 0x00010DED +#define ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS 0x10015000 +#define ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER 0x10015001 +struct asm_dts_eagle_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 param; + struct asm_stream_param_data_v2 data; +} __packed; + +struct asm_dts_eagle_param_get { + struct apr_hdr hdr; + struct asm_stream_cmd_get_pp_params_v2 param; +} __packed; + +/* LSM Specific */ +#define VW_FEAT_DIM (39) + +#define APRV2_IDS_SERVICE_ID_ADSP_LSM_V (0xD) +#define APRV2_IDS_DOMAIN_ID_ADSP_V (0x4) +#define APRV2_IDS_DOMAIN_ID_APPS_V (0x5) + +#define LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS (0x00012A7F) +#define LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS (0x00012A80) +#define LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS (0x00012A81) +#define LSM_SESSION_CMD_OPEN_TX (0x00012A82) +#define LSM_SESSION_CMD_CLOSE_TX (0x00012A88) +#define LSM_SESSION_CMD_SET_PARAMS (0x00012A83) +#define LSM_SESSION_CMD_SET_PARAMS_V2 (0x00012A8F) +#define LSM_SESSION_CMD_REGISTER_SOUND_MODEL (0x00012A84) +#define LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL (0x00012A85) +#define LSM_SESSION_CMD_START (0x00012A86) +#define LSM_SESSION_CMD_STOP (0x00012A87) +#define LSM_SESSION_CMD_EOB (0x00012A89) +#define LSM_SESSION_CMD_READ (0x00012A8A) +#define LSM_SESSION_CMD_OPEN_TX_V2 (0x00012A8B) +#define LSM_CMD_ADD_TOPOLOGIES (0x00012A8C) + +#define LSM_SESSION_EVENT_DETECTION_STATUS (0x00012B00) +#define LSM_SESSION_EVENT_DETECTION_STATUS_V2 (0x00012B01) +#define LSM_DATA_EVENT_READ_DONE (0x00012B02) +#define LSM_DATA_EVENT_STATUS (0x00012B03) + +#define LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00) +#define LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01) +#define LSM_PARAM_ID_OPERATION_MODE (0x00012C02) +#define LSM_PARAM_ID_GAIN (0x00012C03) +#define LSM_PARAM_ID_CONNECT_TO_PORT (0x00012C04) +#define LSM_PARAM_ID_FEATURE_COMPENSATION_DATA (0x00012C07) +#define LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS (0x00012C07) +#define LSM_MODULE_ID_LAB (0x00012C08) +#define LSM_PARAM_ID_LAB_ENABLE (0x00012C09) +#define LSM_PARAM_ID_LAB_CONFIG (0x00012C0A) +#define LSM_MODULE_ID_FRAMEWORK (0x00012C0E) + +/* HW MAD specific */ +#define AFE_MODULE_HW_MAD (0x00010230) +#define AFE_PARAM_ID_HW_MAD_CFG (0x00010231) +#define AFE_PARAM_ID_HW_MAD_CTRL (0x00010232) +#define AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG (0x00010233) + +/* SW MAD specific */ +#define AFE_MODULE_SW_MAD (0x0001022D) +#define AFE_PARAM_ID_SW_MAD_CFG (0x0001022E) +#define AFE_PARAM_ID_SVM_MODEL (0x0001022F) + +/* Commands/Params to pass the codec/slimbus data to DSP */ +#define AFE_SVC_CMD_SET_PARAM (0x000100f3) +#define AFE_MODULE_CDC_DEV_CFG (0x00010234) +#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG (0x00010235) +#define AFE_PARAM_ID_CDC_REG_CFG (0x00010236) +#define AFE_PARAM_ID_CDC_REG_CFG_INIT (0x00010237) +#define AFE_PARAM_ID_CDC_REG_PAGE_CFG (0x00010296) + +#define AFE_MAX_CDC_REGISTERS_TO_CONFIG (20) + +/* AANC Port Config Specific */ +#define AFE_PARAM_ID_AANC_PORT_CONFIG (0x00010215) +#define AFE_API_VERSION_AANC_PORT_CONFIG (0x1) +#define AANC_TX_MIC_UNUSED (0) +#define AANC_TX_VOICE_MIC (1) +#define AANC_TX_ERROR_MIC (2) +#define AANC_TX_NOISE_MIC (3) +#define AFE_PORT_MAX_CHANNEL_CNT (8) +#define AFE_MODULE_AANC (0x00010214) +#define AFE_PARAM_ID_CDC_AANC_VERSION (0x0001023A) +#define AFE_API_VERSION_CDC_AANC_VERSION (0x1) +#define AANC_HW_BLOCK_VERSION_1 (1) +#define AANC_HW_BLOCK_VERSION_2 (2) + +/*Clip bank selection*/ +#define AFE_API_VERSION_CLIP_BANK_SEL_CFG 0x1 +#define AFE_CLIP_MAX_BANKS 4 +#define AFE_PARAM_ID_CLIP_BANK_SEL_CFG 0x00010242 + +struct afe_param_aanc_port_cfg { + /* Minor version used for tracking the version of the module's + * source port configuration. + */ + uint32_t aanc_port_cfg_minor_version; + + /* Sampling rate of the source Tx port. 8k - 192k*/ + uint32_t tx_port_sample_rate; + + /* Channel mapping for the Tx port signal carrying Noise (X), + * Error (E), and Voice (V) signals. + */ + uint8_t tx_port_channel_map[AFE_PORT_MAX_CHANNEL_CNT]; + + /* Number of channels on the source Tx port. */ + uint16_t tx_port_num_channels; + + /* Port ID of the Rx path reference signal. */ + uint16_t rx_path_ref_port_id; + + /* Sampling rate of the reference port. 8k - 192k*/ + uint32_t ref_port_sample_rate; +} __packed; + +struct afe_param_id_cdc_aanc_version { + /* Minor version used for tracking the version of the module's + * hw version + */ + uint32_t cdc_aanc_minor_version; + + /* HW version. */ + uint32_t aanc_hw_version; +} __packed; + +struct afe_param_id_clip_bank_sel { + /* Minor version used for tracking the version of the module's + * hw version + */ + uint32_t minor_version; + + /* Number of banks to be read */ + uint32_t num_banks; + + uint32_t bank_map[AFE_CLIP_MAX_BANKS]; +} __packed; + +/* ERROR CODES */ +/* Success. The operation completed with no errors. */ +#define ADSP_EOK 0x00000000 +/* General failure. */ +#define ADSP_EFAILED 0x00000001 +/* Bad operation parameter. */ +#define ADSP_EBADPARAM 0x00000002 +/* Unsupported routine or operation. */ +#define ADSP_EUNSUPPORTED 0x00000003 +/* Unsupported version. */ +#define ADSP_EVERSION 0x00000004 +/* Unexpected problem encountered. */ +#define ADSP_EUNEXPECTED 0x00000005 +/* Unhandled problem occurred. */ +#define ADSP_EPANIC 0x00000006 +/* Unable to allocate resource. */ +#define ADSP_ENORESOURCE 0x00000007 +/* Invalid handle. */ +#define ADSP_EHANDLE 0x00000008 +/* Operation is already processed. */ +#define ADSP_EALREADY 0x00000009 +/* Operation is not ready to be processed. */ +#define ADSP_ENOTREADY 0x0000000A +/* Operation is pending completion. */ +#define ADSP_EPENDING 0x0000000B +/* Operation could not be accepted or processed. */ +#define ADSP_EBUSY 0x0000000C +/* Operation aborted due to an error. */ +#define ADSP_EABORTED 0x0000000D +/* Operation preempted by a higher priority. */ +#define ADSP_EPREEMPTED 0x0000000E +/* Operation requests intervention to complete. */ +#define ADSP_ECONTINUE 0x0000000F +/* Operation requests immediate intervention to complete. */ +#define ADSP_EIMMEDIATE 0x00000010 +/* Operation is not implemented. */ +#define ADSP_ENOTIMPL 0x00000011 +/* Operation needs more data or resources. */ +#define ADSP_ENEEDMORE 0x00000012 +/* Operation does not have memory. */ +#define ADSP_ENOMEMORY 0x00000014 +/* Item does not exist. */ +#define ADSP_ENOTEXIST 0x00000015 +/* Max count for adsp error code sent to HLOS*/ +#define ADSP_ERR_MAX (ADSP_ENOTEXIST + 1) +/* Operation is finished. */ +#define ADSP_ETERMINATED 0x00011174 + +/*bharath, adsp_error_codes.h */ + +/* LPASS clock for I2S Interface */ + +/* Supported OSR clock values */ +#define Q6AFE_LPASS_OSR_CLK_12_P288_MHZ 0xBB8000 +#define Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ 0xAC4400 +#define Q6AFE_LPASS_OSR_CLK_9_P600_MHZ 0x927C00 +#define Q6AFE_LPASS_OSR_CLK_8_P192_MHZ 0x7D0000 +#define Q6AFE_LPASS_OSR_CLK_6_P144_MHZ 0x5DC000 +#define Q6AFE_LPASS_OSR_CLK_4_P096_MHZ 0x3E8000 +#define Q6AFE_LPASS_OSR_CLK_3_P072_MHZ 0x2EE000 +#define Q6AFE_LPASS_OSR_CLK_2_P048_MHZ 0x1F4000 +#define Q6AFE_LPASS_OSR_CLK_1_P536_MHZ 0x177000 +#define Q6AFE_LPASS_OSR_CLK_1_P024_MHZ 0xFA000 +#define Q6AFE_LPASS_OSR_CLK_768_kHZ 0xBB800 +#define Q6AFE_LPASS_OSR_CLK_512_kHZ 0x7D000 +#define Q6AFE_LPASS_OSR_CLK_DISABLE 0x0 + +/* Supported Bit clock values */ +#define Q6AFE_LPASS_IBIT_CLK_12_P288_MHZ 0xBB8000 +#define Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ 0xAC4400 +#define Q6AFE_LPASS_IBIT_CLK_8_P192_MHZ 0x7D0000 +#define Q6AFE_LPASS_IBIT_CLK_6_P144_MHZ 0x5DC000 +#define Q6AFE_LPASS_IBIT_CLK_4_P096_MHZ 0x3E8000 +#define Q6AFE_LPASS_IBIT_CLK_3_P072_MHZ 0x2EE000 +#define Q6AFE_LPASS_IBIT_CLK_2_P8224_MHZ 0x2b1100 +#define Q6AFE_LPASS_IBIT_CLK_2_P048_MHZ 0x1F4000 +#define Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ 0x177000 +#define Q6AFE_LPASS_IBIT_CLK_1_P4112_MHZ 0x158880 +#define Q6AFE_LPASS_IBIT_CLK_1_P024_MHZ 0xFA000 +#define Q6AFE_LPASS_IBIT_CLK_768_KHZ 0xBB800 +#define Q6AFE_LPASS_IBIT_CLK_512_KHZ 0x7D000 +#define Q6AFE_LPASS_IBIT_CLK_256_KHZ 0x3E800 +#define Q6AFE_LPASS_IBIT_CLK_DISABLE 0x0 + +/* Supported LPASS CLK sources */ +#define Q6AFE_LPASS_CLK_SRC_EXTERNAL 0 +#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1 + +/* Supported LPASS CLK root*/ +#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 + +enum afe_lpass_clk_mode { + Q6AFE_LPASS_MODE_BOTH_INVALID, + Q6AFE_LPASS_MODE_CLK1_VALID, + Q6AFE_LPASS_MODE_CLK2_VALID, + Q6AFE_LPASS_MODE_BOTH_VALID, +} __packed; + +/* Clock ID Enumeration Define. */ +/* Clock ID for Primary I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 +/* Clock ID for Primary I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT 0x101 +/* Clock ID for Secondary I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT 0x102 +/* Clock ID for Secondary I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT 0x103 +/* Clock ID for Tertiary I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT 0x104 +/* Clock ID for Tertiary I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT 0x105 +/* Clock ID for Quartnery I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT 0x106 +/* Clock ID for Quartnery I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT 0x107 +/* Clock ID for Speaker I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_IBIT 0x108 +/* Clock ID for Speaker I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_EBIT 0x109 +/* Clock ID for Speaker I2S OSR */ +#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR 0x10A + +/* Clock ID for QUINARY I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT 0x10B +/* Clock ID for QUINARY I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT 0x10C +/* Clock ID for SENARY I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT 0x10D +/* Clock ID for SENARY I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_EBIT 0x10E +/* Clock ID for INT0 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT 0x10F +/* Clock ID for INT1 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT 0x110 +/* Clock ID for INT2 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT 0x111 +/* Clock ID for INT3 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT 0x112 +/* Clock ID for INT4 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT 0x113 +/* Clock ID for INT5 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT 0x114 +/* Clock ID for INT6 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115 + +/* Clock ID for Primary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT 0x200 +/* Clock ID for Primary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT 0x201 +/* Clock ID for Secondary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT 0x202 +/* Clock ID for Secondary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT 0x203 +/* Clock ID for Tertiary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT 0x204 +/* Clock ID for Tertiary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT 0x205 +/* Clock ID for Quartery PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT 0x206 +/* Clock ID for Quartery PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT 0x207 + +/** Clock ID for Primary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT 0x200 +/** Clock ID for Primary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT 0x201 +/** Clock ID for Secondary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT 0x202 +/** Clock ID for Secondary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT 0x203 +/** Clock ID for Tertiary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT 0x204 +/** Clock ID for Tertiary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT 0x205 +/** Clock ID for Quartery TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT 0x206 +/** Clock ID for Quartery TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT 0x207 + +/* Clock ID for MCLK1 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_1 0x300 +/* Clock ID for MCLK2 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_2 0x301 +/* Clock ID for MCLK3 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_3 0x302 +/* Clock ID for MCLK4 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_4 0x304 +/* Clock ID for Internal Digital Codec Core */ +#define Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 0x303 +/* Clock ID for INT MCLK0 */ +#define Q6AFE_LPASS_CLK_ID_INT_MCLK_0 0x305 +/* Clock ID for INT MCLK1 */ +#define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306 +/* + * Clock ID for soundwire NPL. + * This is the clock to be used to enable NPL clock for internal Soundwire. + */ +#define AFE_CLOCK_SET_CLOCK_ID_SWR_NPL_CLK 0x307 + +/* Clock ID for AHB HDMI input */ +#define Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT 0x400 + +/* Clock ID for SPDIF core */ +#define Q6AFE_LPASS_CLK_ID_SPDIF_CORE 0x500 + + +/* Clock attribute for invalid use (reserved for internal usage) */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID 0x0 +/* Clock attribute for no couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 +/* Clock attribute for dividend couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 +/* Clock attribute for divisor couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3 +/* Clock attribute for invert and no couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO 0x4 +/* Clock set API version */ +#define Q6AFE_LPASS_CLK_CONFIG_API_VERSION 0x1 + +struct afe_clk_set { + /* + * Minor version used for tracking clock set. + * @values #AFE_API_VERSION_CLOCK_SET + */ + uint32_t clk_set_minor_version; + + /* + * Clock ID + * @values + * - 0x100 to 0x10A - MSM8996 + * - 0x200 to 0x207 - MSM8996 + * - 0x300 to 0x302 - MSM8996 @tablebulletend + */ + uint32_t clk_id; + + /* + * Clock frequency (in Hertz) to be set. + * @values + * - >= 0 for clock frequency to set @tablebulletend + */ + uint32_t clk_freq_in_hz; + + /* Use to specific divider for two clocks if needed. + * Set to Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO for no divider + * relation clocks + * @values + * - #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO + * - #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND + * - #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR @tablebulletend + */ + uint16_t clk_attri; + + /* + * Specifies the root clock source. + * Currently, only Q6AFE_LPASS_CLK_ROOT_DEFAULT is valid + * @values + * - 0 @tablebulletend + */ + uint16_t clk_root; + + /* + * for enable and disable clock. + * "clk_freq_in_hz", "clk_attri", and "clk_root" + * are ignored in disable clock case. + * @values  + * - 0 -- Disabled + * - 1 -- Enabled @tablebulletend + */ + uint32_t enable; +}; + +struct afe_clk_cfg { +/* Minor version used for tracking the version of the I2S + * configuration interface. + * Supported values: #AFE_API_VERSION_I2S_CONFIG + */ + u32 i2s_cfg_minor_version; + +/* clk value 1 in MHz. */ + u32 clk_val1; + +/* clk value 2 in MHz. */ + u32 clk_val2; + +/* clk_src + * #Q6AFE_LPASS_CLK_SRC_EXTERNAL + * #Q6AFE_LPASS_CLK_SRC_INTERNAL + */ + + u16 clk_src; + +/* clk_root -0 for default */ + u16 clk_root; + +/* clk_set_mode + * #Q6AFE_LPASS_MODE_BOTH_INVALID + * #Q6AFE_LPASS_MODE_CLK1_VALID + * #Q6AFE_LPASS_MODE_CLK2_VALID + * #Q6AFE_LPASS_MODE_BOTH_VALID + */ + u16 clk_set_mode; + +/* This param id is used to configure I2S clk */ + u16 reserved; +} __packed; + +/* This param id is used to configure I2S clk */ +#define AFE_PARAM_ID_LPAIF_CLK_CONFIG 0x00010238 +#define AFE_MODULE_CLOCK_SET 0x0001028F +#define AFE_PARAM_ID_CLOCK_SET 0x00010290 + +struct afe_lpass_clk_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_clk_cfg clk_cfg; +} __packed; + +enum afe_lpass_digital_clk_src { + Q6AFE_LPASS_DIGITAL_ROOT_INVALID, + Q6AFE_LPASS_DIGITAL_ROOT_PRI_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_SEC_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_TER_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_QUAD_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_CDC_ROOT_CLK, +} __packed; + +/* This param id is used to configure internal clk */ +#define AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG 0x00010239 + +struct afe_digital_clk_cfg { +/* Minor version used for tracking the version of the I2S + * configuration interface. + * Supported values: #AFE_API_VERSION_I2S_CONFIG + */ + u32 i2s_cfg_minor_version; + +/* clk value in MHz. */ + u32 clk_val; + +/* INVALID + * PRI_MI2S_OSR + * SEC_MI2S_OSR + * TER_MI2S_OSR + * QUAD_MI2S_OSR + * DIGT_CDC_ROOT + */ + u16 clk_root; + +/* This field must be set to zero. */ + u16 reserved; +} __packed; + + +struct afe_lpass_digital_clk_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_digital_clk_cfg clk_cfg; +} __packed; + +/* + * Opcode for AFE to start DTMF. + */ +#define AFE_PORTS_CMD_DTMF_CTL 0x00010102 + +/** DTMF payload.*/ +struct afe_dtmf_generation_command { + struct apr_hdr hdr; + + /* + * Duration of the DTMF tone in ms. + * -1 -> continuous, + * 0 -> disable + */ + int64_t duration_in_ms; + + /* + * The DTMF high tone frequency. + */ + uint16_t high_freq; + + /* + * The DTMF low tone frequency. + */ + uint16_t low_freq; + + /* + * The DTMF volume setting + */ + uint16_t gain; + + /* + * The number of ports to enable/disable on. + */ + uint16_t num_ports; + + /* + * The Destination ports - array . + * For DTMF on multiple ports, portIds needs to + * be populated numPorts times. + */ + uint16_t port_ids; + + /* + * variable for 32 bit alignment of APR packet. + */ + uint16_t reserved; +} __packed; + +enum afe_config_type { + AFE_SLIMBUS_SLAVE_PORT_CONFIG, + AFE_SLIMBUS_SLAVE_CONFIG, + AFE_CDC_REGISTERS_CONFIG, + AFE_AANC_VERSION, + AFE_CDC_CLIP_REGISTERS_CONFIG, + AFE_CLIP_BANK_SEL, + AFE_CDC_REGISTER_PAGE_CONFIG, + AFE_MAX_CONFIG_TYPES, +}; + +struct afe_param_slimbus_slave_port_cfg { + uint32_t minor_version; + uint16_t slimbus_dev_id; + uint16_t slave_dev_pgd_la; + uint16_t slave_dev_intfdev_la; + uint16_t bit_width; + uint16_t data_format; + uint16_t num_channels; + uint16_t slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +} __packed; + +struct afe_param_cdc_slimbus_slave_cfg { + uint32_t minor_version; + uint32_t device_enum_addr_lsw; + uint32_t device_enum_addr_msw; + uint16_t tx_slave_port_offset; + uint16_t rx_slave_port_offset; +} __packed; + +struct afe_param_cdc_reg_cfg { + uint32_t minor_version; + uint32_t reg_logical_addr; + uint32_t reg_field_type; + uint32_t reg_field_bit_mask; + uint16_t reg_bit_width; + uint16_t reg_offset_scale; +} __packed; + +#define AFE_API_VERSION_CDC_REG_PAGE_CFG 1 + +enum { + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_0 = 0, + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_2, + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_3, +}; + +struct afe_param_cdc_reg_page_cfg { + uint32_t minor_version; + uint32_t enable; + uint32_t proc_id; +} __packed; + +struct afe_param_cdc_reg_cfg_data { + uint32_t num_registers; + struct afe_param_cdc_reg_cfg *reg_data; +} __packed; + +struct afe_svc_cmd_set_param { + uint32_t payload_size; + uint32_t payload_address_lsw; + uint32_t payload_address_msw; + uint32_t mem_map_handle; +} __packed; + +struct afe_svc_param_data { + uint32_t module_id; + uint32_t param_id; + uint16_t param_size; + uint16_t reserved; +} __packed; + +struct afe_param_hw_mad_ctrl { + uint32_t minor_version; + uint16_t mad_type; + uint16_t mad_enable; +} __packed; + +struct afe_cmd_hw_mad_ctrl { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_hw_mad_ctrl payload; +} __packed; + +struct afe_cmd_hw_mad_slimbus_slave_port_cfg { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + struct afe_param_slimbus_slave_port_cfg sb_port_cfg; +} __packed; + +struct afe_cmd_sw_mad_enable { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; +} __packed; + +struct afe_param_cdc_reg_cfg_payload { + struct afe_svc_param_data common; + struct afe_param_cdc_reg_cfg reg_cfg; +} __packed; + +struct afe_lpass_clk_config_command_v2 { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_svc_param_data pdata; + struct afe_clk_set clk_cfg; +} __packed; + +/* + * reg_data's size can be up to AFE_MAX_CDC_REGISTERS_TO_CONFIG + */ +struct afe_svc_cmd_cdc_reg_cfg { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_param_cdc_reg_cfg_payload reg_data[0]; +} __packed; + +struct afe_svc_cmd_init_cdc_reg_cfg { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_port_param_data_v2 init; +} __packed; + +struct afe_svc_cmd_sb_slave_cfg { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_port_param_data_v2 pdata; + struct afe_param_cdc_slimbus_slave_cfg sb_slave_cfg; +} __packed; + +struct afe_svc_cmd_cdc_reg_page_cfg { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_port_param_data_v2 pdata; + struct afe_param_cdc_reg_page_cfg cdc_reg_page_cfg; +} __packed; + +struct afe_svc_cmd_cdc_aanc_version { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_cdc_aanc_version version; +} __packed; + +struct afe_port_cmd_set_aanc_param { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + union { + struct afe_param_aanc_port_cfg aanc_port_cfg; + struct afe_mod_enable_param mod_enable; + } __packed data; +} __packed; + +struct afe_port_cmd_set_aanc_acdb_table { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; +} __packed; + +/* Dolby DAP topology */ +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0x0001033B +#define DS2_ADM_COPP_TOPOLOGY_ID 0x1301033B + +/* RMS value from DSP */ +#define RMS_MODULEID_APPI_PASSTHRU 0x10009011 +#define RMS_PARAM_FIRST_SAMPLE 0x10009012 +#define RMS_PAYLOAD_LEN 4 + +/* Customized mixing in matix mixer */ +#define MTMX_MODULE_ID_DEFAULT_CHMIXER 0x00010341 +#define DEFAULT_CHMIXER_PARAM_ID_COEFF 0x00010342 +#define CUSTOM_STEREO_PAYLOAD_SIZE 9 +#define CUSTOM_STEREO_CMD_PARAM_SIZE 24 +#define CUSTOM_STEREO_NUM_OUT_CH 0x0002 +#define CUSTOM_STEREO_NUM_IN_CH 0x0002 +#define CUSTOM_STEREO_INDEX_PARAM 0x0002 +#define Q14_GAIN_ZERO_POINT_FIVE 0x2000 +#define Q14_GAIN_UNITY 0x4000 + +struct afe_svc_cmd_set_clip_bank_selection { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_clip_bank_sel bank_sel; +} __packed; + +/* Ultrasound supported formats */ +#define US_POINT_EPOS_FORMAT_V2 0x0001272D +#define US_RAW_FORMAT_V2 0x0001272C +#define US_PROX_FORMAT_V4 0x0001273B +#define US_RAW_SYNC_FORMAT 0x0001272F +#define US_GES_SYNC_FORMAT 0x00012730 + +#define AFE_MODULE_GROUP_DEVICE 0x00010254 +#define AFE_PARAM_ID_GROUP_DEVICE_CFG 0x00010255 +#define AFE_PARAM_ID_GROUP_DEVICE_ENABLE 0x00010256 +#define AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX 0x1102 + +/* Payload of the #AFE_PARAM_ID_GROUP_DEVICE_CFG + * parameter, which configures max of 8 AFE ports + * into a group. + * The fixed size of this structure is sixteen bytes. + */ +struct afe_group_device_group_cfg { + u32 minor_version; + u16 group_id; + u16 num_channels; + u16 port_id[8]; +} __packed; + +#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x100) + +/* ID of the parameter used by #AFE_MODULE_GROUP_DEVICE to configure the + * group device. #AFE_SVC_CMD_SET_PARAM can use this parameter ID. + * + * Requirements: + * - Configure the group before the member ports in the group are + * configured and started. + * - Enable the group only after it is configured. + * - Stop all member ports in the group before disabling the group. + */ +#define AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG 0x0001029E + +/* Version information used to handle future additions to + * AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG processing (for backward compatibility). + */ +#define AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG 0x1 + +/* Number of AFE ports in group device */ +#define AFE_GROUP_DEVICE_NUM_PORTS 8 + +/* Payload of the AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG parameter ID + * used by AFE_MODULE_GROUP_DEVICE. + */ +struct afe_param_id_group_device_tdm_cfg { + u32 group_device_cfg_minor_version; + /* Minor version used to track group device configuration. + * @values #AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG + */ + + u16 group_id; + /* ID for the group device. + * @values + * - #AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX + * - #AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX + * - #AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX + * - #AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX + */ + + u16 reserved; + /* 0 */ + + u16 port_id[AFE_GROUP_DEVICE_NUM_PORTS]; + /* Array of member port IDs of this group. + * @values + * - #AFE_PORT_ID_PRIMARY_TDM_RX + * - #AFE_PORT_ID_PRIMARY_TDM_RX_1 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_2 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_3 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_4 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_5 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_6 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_7 + + * - #AFE_PORT_ID_PRIMARY_TDM_TX + * - #AFE_PORT_ID_PRIMARY_TDM_TX_1 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_2 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_3 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_4 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_5 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_6 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_7 + + * - #AFE_PORT_ID_SECONDARY_TDM_RX + * - #AFE_PORT_ID_SECONDARY_TDM_RX_1 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_2 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_3 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_4 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_5 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_6 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_7 + + * - #AFE_PORT_ID_SECONDARY_TDM_TX + * - #AFE_PORT_ID_SECONDARY_TDM_TX_1 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_2 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_3 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_4 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_5 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_6 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_7 + + * - #AFE_PORT_ID_TERTIARY_TDM_RX + * - #AFE_PORT_ID_TERTIARY_TDM_RX_1 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_2 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_3 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_4 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_5 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_6 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_7 + + * - #AFE_PORT_ID_TERTIARY_TDM_TX + * - #AFE_PORT_ID_TERTIARY_TDM_TX_1 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_2 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_3 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_4 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_5 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_6 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_7 + + * - #AFE_PORT_ID_QUATERNARY_TDM_RX + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_1 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_2 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_3 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_4 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_5 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_6 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_7 + + * - #AFE_PORT_ID_QUATERNARY_TDM_TX + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_1 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_2 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_3 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_4 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_5 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_6 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_7 + * @tablebulletend + */ + + u32 num_channels; + /* Number of enabled slots for TDM frame. + * @values 1 to 8 + */ + + u32 sample_rate; + /* Sampling rate of the port. + * @values + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_24K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_48K @tablebulletend + */ + + u32 bit_width; + /* Bit width of the sample. + * @values 16, 24, (32) + */ + + u16 nslots_per_frame; + /* Number of slots per frame. Typical : 1, 2, 4, 8, 16, 32. + * @values 1 - 32 + */ + + u16 slot_width; + /* Slot width of the slot in a TDM frame. (slot_width >= bit_width) + * have to be satisfied. + * @values 16, 24, 32 + */ + + u32 slot_mask; + /* Position of active slots. When that bit is set, that paricular + * slot is active. + * Number of active slots can be inferred by number of bits set in + * the mask. Only 8 individual bits can be enabled. + * Bits 0..31 corresponding to slot 0..31 + * @values 1 to 2^32 -1 + */ +} __packed; + +/* Payload of the #AFE_PARAM_ID_GROUP_DEVICE_ENABLE + * parameter, which enables or + * disables any module. + * The fixed size of this structure is four bytes. + */ + +struct afe_group_device_enable { + u16 group_id; + /* valid value is AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX */ + u16 enable; + /* Enables (1) or disables (0) the module. */ +} __packed; + +union afe_port_group_config { + struct afe_group_device_group_cfg group_cfg; + struct afe_group_device_enable group_enable; + struct afe_param_id_group_device_tdm_cfg tdm_cfg; +} __packed; + +struct afe_port_group_create { + struct apr_hdr hdr; + struct afe_svc_cmd_set_param param; + struct afe_port_param_data_v2 pdata; + union afe_port_group_config data; +} __packed; + +/* Command for Matrix or Stream Router */ +#define ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2 0x00010DCE +/* Module for AVSYNC */ +#define ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC 0x00010DC6 + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to specify the + * render window start value. This parameter is supported only for a Set + * command (not a Get command) in the Rx direction + * (#ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2). + * Render window start is a value (session time minus timestamp, or ST-TS) + * below which frames are held, and after which frames are immediately + * rendered. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2 0x00010DD1 + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to specify the + * render window end value. This parameter is supported only for a Set + * command (not a Get command) in the Rx direction + * (#ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2). Render window end is a value + * (session time minus timestamp) above which frames are dropped, and below + * which frames are immediately rendered. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2 0x00010DD2 + +/* Generic payload of the window parameters in the + * #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC module. + * This payload is supported only for a Set command + * (not a Get command) on the Rx path. + */ +struct asm_session_mtmx_strtr_param_window_v2_t { + u32 window_lsw; + /* Lower 32 bits of the render window start value. */ + + u32 window_msw; + /* Upper 32 bits of the render window start value. + * + * The 64-bit number formed by window_lsw and window_msw specifies a + * signed 64-bit window value in microseconds. The sign extension is + * necessary. This value is used by the following parameter IDs: + * #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2 + * #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2 + * #ASM_SESSION_MTMX_STRTR_PARAM_STAT_WINDOW_START_V2 + * #ASM_SESSION_MTMX_STRTR_PARAM_STAT_WINDOW_END_V2 + * The value depends on which parameter ID is used. + * The aDSP honors the windows at a granularity of 1 ms. + */ +}; + +struct asm_session_cmd_set_mtmx_strstr_params_v2 { + uint32_t data_payload_addr_lsw; + /* Lower 32 bits of the 64-bit data payload address. */ + + uint32_t data_payload_addr_msw; + /* Upper 32 bits of the 64-bit data payload address. + * If the address is not sent (NULL), the message is in the payload. + * If the address is sent (non-NULL), the parameter data payloads + * begin at the specified address. + */ + + uint32_t mem_map_handle; + /* Unique identifier for an address. This memory map handle is returned + * by the aDSP through the #ASM_CMD_SHARED_MEM_MAP_REGIONS command. + * values + * - NULL -- Parameter data payloads are within the message payload + * (in-band). + * - Non-NULL -- Parameter data payloads begin at the address specified + * in the data_payload_addr_lsw and data_payload_addr_msw fields + * (out-of-band). + */ + + uint32_t data_payload_size; + /* Actual size of the variable payload accompanying the message, or in + * shared memory. This field is used for parsing the parameter payload. + * values > 0 bytes + */ + + uint32_t direction; + /* Direction of the entity (matrix mixer or stream router) on which + * the parameter is to be set. + * values + * - 0 -- Rx (for Rx stream router or Rx matrix mixer) + * - 1 -- Tx (for Tx stream router or Tx matrix mixer) + */ +}; + +struct asm_mtmx_strtr_params { + struct apr_hdr hdr; + struct asm_session_cmd_set_mtmx_strstr_params_v2 param; + struct asm_stream_param_data_v2 data; + u32 window_lsw; + u32 window_msw; +} __packed; + +#define ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2 0x00010DCF +#define ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2 0x00010DD0 + +#define ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3 0x00012F0B +#define ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK (0x80000000UL) + +struct asm_session_cmd_get_mtmx_strstr_params_v2 { + uint32_t data_payload_addr_lsw; + /* Lower 32 bits of the 64-bit data payload address. */ + + uint32_t data_payload_addr_msw; + /* + * Upper 32 bits of the 64-bit data payload address. + * If the address is not sent (NULL), the message is in the payload. + * If the address is sent (non-NULL), the parameter data payloads + * begin at the specified address. + */ + + uint32_t mem_map_handle; + /* + * Unique identifier for an address. This memory map handle is returned + * by the aDSP through the #ASM_CMD_SHARED_MEM_MAP_REGIONS command. + * values + * - NULL -- Parameter data payloads are within the message payload + * (in-band). + * - Non-NULL -- Parameter data payloads begin at the address specified + * in the data_payload_addr_lsw and data_payload_addr_msw fields + * (out-of-band). + */ + uint32_t direction; + /* + * Direction of the entity (matrix mixer or stream router) on which + * the parameter is to be set. + * values + * - 0 -- Rx (for Rx stream router or Rx matrix mixer) + * - 1 -- Tx (for Tx stream router or Tx matrix mixer) + */ + uint32_t module_id; + /* Unique module ID. */ + + uint32_t param_id; + /* Unique parameter ID. */ + + uint32_t param_max_size; +}; + +struct asm_session_mtmx_strtr_param_session_time_v3_t { + uint32_t session_time_lsw; + /* Lower 32 bits of the current session time in microseconds */ + + uint32_t session_time_msw; + /* + * Upper 32 bits of the current session time in microseconds. + * The 64-bit number formed by session_time_lsw and session_time_msw + * is treated as signed. + */ + + uint32_t absolute_time_lsw; + /* + * Lower 32 bits of the 64-bit absolute time in microseconds. + * This is the time when the sample corresponding to the + * session_time_lsw is rendered to the hardware. This absolute + * time can be slightly in the future or past. + */ + + uint32_t absolute_time_msw; + /* + * Upper 32 bits of the 64-bit absolute time in microseconds. + * This is the time when the sample corresponding to the + * session_time_msw is rendered to hardware. This absolute + * time can be slightly in the future or past. The 64-bit number + * formed by absolute_time_lsw and absolute_time_msw is treated as + * unsigned. + */ + + uint32_t time_stamp_lsw; + /* Lower 32 bits of the last processed timestamp in microseconds */ + + uint32_t time_stamp_msw; + /* + * Upper 32 bits of the last processed timestamp in microseconds. + * The 64-bit number formed by time_stamp_lsw and time_stamp_lsw + * is treated as unsigned. + */ + + uint32_t flags; + /* + * Keeps track of any additional flags needed. + * @values{for bit 31} + * - 0 -- Uninitialized/invalid + * - 1 -- Valid + * All other bits are reserved; clients must set them to zero. + */ +}; + +union asm_session_mtmx_strtr_data_type { + struct asm_session_mtmx_strtr_param_session_time_v3_t session_time; +}; + +struct asm_mtmx_strtr_get_params { + struct apr_hdr hdr; + struct asm_session_cmd_get_mtmx_strstr_params_v2 param_info; +} __packed; + +struct asm_mtmx_strtr_get_params_cmdrsp { + uint32_t err_code; + struct asm_stream_param_data_v2 param_info; + union asm_session_mtmx_strtr_data_type param_data; +} __packed; + +#define AUDPROC_MODULE_ID_RESAMPLER 0x00010719 + +enum { + LEGACY_PCM = 0, + COMPRESSED_PASSTHROUGH, + COMPRESSED_PASSTHROUGH_CONVERT, + COMPRESSED_PASSTHROUGH_DSD, +}; + +#define AUDPROC_MODULE_ID_COMPRESSED_MUTE 0x00010770 +#define AUDPROC_PARAM_ID_COMPRESSED_MUTE 0x00010771 + +struct adm_set_compressed_device_mute { + struct adm_cmd_set_pp_params_v5 command; + struct adm_param_data_v5 params; + u32 mute_on; +} __packed; + +#define AUDPROC_MODULE_ID_COMPRESSED_LATENCY 0x0001076E +#define AUDPROC_PARAM_ID_COMPRESSED_LATENCY 0x0001076F + +struct adm_set_compressed_device_latency { + struct adm_cmd_set_pp_params_v5 command; + struct adm_param_data_v5 params; + u32 latency; +} __packed; + +#define VOICEPROC_MODULE_ID_GENERIC_TX 0x00010EF6 +#define VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS 0x00010E37 +#define VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING 0x00010E38 +#define MAX_SECTORS 8 +#define MAX_NOISE_SOURCE_INDICATORS 3 +#define MAX_POLAR_ACTIVITY_INDICATORS 360 + +struct sound_focus_param { + uint16_t start_angle[MAX_SECTORS]; + uint8_t enable[MAX_SECTORS]; + uint16_t gain_step; +} __packed; + +struct source_tracking_param { + uint8_t vad[MAX_SECTORS]; + uint16_t doa_speech; + uint16_t doa_noise[MAX_NOISE_SOURCE_INDICATORS]; + uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS]; +} __packed; + +struct adm_param_fluence_soundfocus_t { + uint16_t start_angles[MAX_SECTORS]; + uint8_t enables[MAX_SECTORS]; + uint16_t gain_step; + uint16_t reserved; +} __packed; + +struct adm_set_fluence_soundfocus_param { + struct adm_cmd_set_pp_params_v5 params; + struct adm_param_data_v5 data; + struct adm_param_fluence_soundfocus_t soundfocus_data; +} __packed; + +struct adm_param_fluence_sourcetracking_t { + uint8_t vad[MAX_SECTORS]; + uint16_t doa_speech; + uint16_t doa_noise[MAX_NOISE_SOURCE_INDICATORS]; + uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS]; +} __packed; + +#define AUDPROC_MODULE_ID_AUDIOSPHERE 0x00010916 +#define AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE 0x00010917 +#define AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH 0x00010918 +#define AUDPROC_PARAM_ID_AUDIOSPHERE_CONFIG_MODE 0x00010919 + +#define AUDPROC_PARAM_ID_AUDIOSPHERE_COEFFS_STEREO_INPUT 0x0001091A +#define AUDPROC_PARAM_ID_AUDIOSPHERE_COEFFS_MULTICHANNEL_INPUT 0x0001091B +#define AUDPROC_PARAM_ID_AUDIOSPHERE_DESIGN_STEREO_INPUT 0x0001091C +#define AUDPROC_PARAM_ID_AUDIOSPHERE_DESIGN_MULTICHANNEL_INPUT 0x0001091D + +#define AUDPROC_PARAM_ID_AUDIOSPHERE_OPERATING_INPUT_MEDIA_INFO 0x0001091E +#endif /*_APR_AUDIO_V2_H_ */ diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h new file mode 100644 index 000000000000..eb35645c759f --- /dev/null +++ b/include/sound/apr_audio.h @@ -0,0 +1,1931 @@ +/* + * + * Copyright (c) 2010-2013, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _APR_AUDIO_H_ +#define _APR_AUDIO_H_ + +/* ASM opcodes without APR payloads*/ +#include + +/* + * Audio Front End (AFE) + */ + +/* Port ID. Update afe_get_port_index when a new port is added here. */ +#define PRIMARY_I2S_RX 0 /* index = 0 */ +#define PRIMARY_I2S_TX 1 /* index = 1 */ +#define PCM_RX 2 /* index = 2 */ +#define PCM_TX 3 /* index = 3 */ +#define SECONDARY_I2S_RX 4 /* index = 4 */ +#define SECONDARY_I2S_TX 5 /* index = 5 */ +#define MI2S_RX 6 /* index = 6 */ +#define MI2S_TX 7 /* index = 7 */ +#define HDMI_RX 8 /* index = 8 */ +#define RSVD_2 9 /* index = 9 */ +#define RSVD_3 10 /* index = 10 */ +#define DIGI_MIC_TX 11 /* index = 11 */ +#define VOICE_RECORD_RX 0x8003 /* index = 12 */ +#define VOICE_RECORD_TX 0x8004 /* index = 13 */ +#define VOICE_PLAYBACK_TX 0x8005 /* index = 14 */ + +/* Slimbus Multi channel port id pool */ +#define SLIMBUS_0_RX 0x4000 /* index = 15 */ +#define SLIMBUS_0_TX 0x4001 /* index = 16 */ +#define SLIMBUS_1_RX 0x4002 /* index = 17 */ +#define SLIMBUS_1_TX 0x4003 /* index = 18 */ +#define SLIMBUS_2_RX 0x4004 +#define SLIMBUS_2_TX 0x4005 +#define SLIMBUS_3_RX 0x4006 +#define SLIMBUS_3_TX 0x4007 +#define SLIMBUS_4_RX 0x4008 +#define SLIMBUS_4_TX 0x4009 /* index = 24 */ + +#define INT_BT_SCO_RX 0x3000 /* index = 25 */ +#define INT_BT_SCO_TX 0x3001 /* index = 26 */ +#define INT_BT_A2DP_RX 0x3002 /* index = 27 */ +#define INT_FM_RX 0x3004 /* index = 28 */ +#define INT_FM_TX 0x3005 /* index = 29 */ +#define RT_PROXY_PORT_001_RX 0x2000 /* index = 30 */ +#define RT_PROXY_PORT_001_TX 0x2001 /* index = 31 */ +#define SECONDARY_PCM_RX 12 /* index = 32 */ +#define SECONDARY_PCM_TX 13 /* index = 33 */ +#define PSEUDOPORT_01 0x8001 /* index =34 */ + +#define AFE_PORT_INVALID 0xFFFF +#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID + +#define AFE_PORT_CMD_START 0x000100ca + +#define AFE_EVENT_RTPORT_START 0 +#define AFE_EVENT_RTPORT_STOP 1 +#define AFE_EVENT_RTPORT_LOW_WM 2 +#define AFE_EVENT_RTPORT_HI_WM 3 + +struct afe_port_start_command { + struct apr_hdr hdr; + u16 port_id; + u16 gain; /* Q13 */ + u32 sample_rate; /* 8 , 16, 48khz */ +} __packed; + +#define AFE_PORT_CMD_STOP 0x000100cb +struct afe_port_stop_command { + struct apr_hdr hdr; + u16 port_id; + u16 reserved; +} __packed; + +#define AFE_PORT_CMD_APPLY_GAIN 0x000100cc +struct afe_port_gain_command { + struct apr_hdr hdr; + u16 port_id; + u16 gain;/* Q13 */ +} __packed; + +#define AFE_PORT_CMD_SIDETONE_CTL 0x000100cd +struct afe_port_sidetone_command { + struct apr_hdr hdr; + u16 rx_port_id; /* Primary i2s tx = 1 */ + /* PCM tx = 3 */ + /* Secondary i2s tx = 5 */ + /* Mi2s tx = 7 */ + /* Digital mic tx = 11 */ + u16 tx_port_id; /* Primary i2s rx = 0 */ + /* PCM rx = 2 */ + /* Secondary i2s rx = 4 */ + /* Mi2S rx = 6 */ + /* HDMI rx = 8 */ + u16 gain; /* Q13 */ + u16 enable; /* 1 = enable, 0 = disable */ +} __packed; + +#define AFE_PORT_CMD_LOOPBACK 0x000100ce +struct afe_loopback_command { + struct apr_hdr hdr; + u16 tx_port_id; /* Primary i2s rx = 0 */ + /* PCM rx = 2 */ + /* Secondary i2s rx = 4 */ + /* Mi2S rx = 6 */ + /* HDMI rx = 8 */ + u16 rx_port_id; /* Primary i2s tx = 1 */ + /* PCM tx = 3 */ + /* Secondary i2s tx = 5 */ + /* Mi2s tx = 7 */ + /* Digital mic tx = 11 */ + u16 mode; /* Default -1, DSP will conver + * the tx to rx format + */ + u16 enable; /* 1 = enable, 0 = disable */ +} __packed; + +#define AFE_PSEUDOPORT_CMD_START 0x000100cf +struct afe_pseudoport_start_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 timing; /* FTRT = 0 , AVTimer = 1, */ +} __packed; + +#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0 +struct afe_pseudoport_stop_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 reserved; +} __packed; + +#define AFE_CMD_GET_ACTIVE_PORTS 0x000100d1 + + +#define AFE_CMD_GET_ACTIVE_HANDLES_FOR_PORT 0x000100d2 +struct afe_get_active_handles_command { + struct apr_hdr hdr; + u16 port_id; + u16 reserved; +} __packed; + +/* + * Opcode for AFE to start DTMF. + */ +#define AFE_PORTS_CMD_DTMF_CTL 0x00010102 + +/** DTMF payload.*/ +struct afe_dtmf_generation_command { + struct apr_hdr hdr; + + /* + * Duration of the DTMF tone in ms. + * -1 -> continuous, + * 0 -> disable + */ + int64_t duration_in_ms; + + /* + * The DTMF high tone frequency. + */ + uint16_t high_freq; + + /* + * The DTMF low tone frequency. + */ + uint16_t low_freq; + + /* + * The DTMF volume setting + */ + uint16_t gain; + + /* + * The number of ports to enable/disable on. + */ + uint16_t num_ports; + + /* + * The Destination ports - array . + * For DTMF on multiple ports, portIds needs to + * be populated numPorts times. + */ + uint16_t port_ids; + + /* + * variable for 32 bit alignment of APR packet. + */ + uint16_t reserved; +} __packed; + +#define AFE_PCM_CFG_MODE_PCM 0x0 +#define AFE_PCM_CFG_MODE_AUX 0x1 +#define AFE_PCM_CFG_SYNC_EXT 0x0 +#define AFE_PCM_CFG_SYNC_INT 0x1 +#define AFE_PCM_CFG_FRM_8BPF 0x0 +#define AFE_PCM_CFG_FRM_16BPF 0x1 +#define AFE_PCM_CFG_FRM_32BPF 0x2 +#define AFE_PCM_CFG_FRM_64BPF 0x3 +#define AFE_PCM_CFG_FRM_128BPF 0x4 +#define AFE_PCM_CFG_FRM_256BPF 0x5 +#define AFE_PCM_CFG_QUANT_ALAW_NOPAD 0x0 +#define AFE_PCM_CFG_QUANT_MULAW_NOPAD 0x1 +#define AFE_PCM_CFG_QUANT_LINEAR_NOPAD 0x2 +#define AFE_PCM_CFG_QUANT_ALAW_PAD 0x3 +#define AFE_PCM_CFG_QUANT_MULAW_PAD 0x4 +#define AFE_PCM_CFG_QUANT_LINEAR_PAD 0x5 +#define AFE_PCM_CFG_CDATAOE_MASTER 0x0 +#define AFE_PCM_CFG_CDATAOE_SHARE 0x1 + +struct afe_port_pcm_cfg { + u16 mode; /* PCM (short sync) = 0, AUXPCM (long sync) = 1 */ + u16 sync; /* external = 0 , internal = 1 */ + u16 frame; /* 8 bpf = 0 */ + /* 16 bpf = 1 */ + /* 32 bpf = 2 */ + /* 64 bpf = 3 */ + /* 128 bpf = 4 */ + /* 256 bpf = 5 */ + u16 quant; + u16 slot; /* Slot for PCM stream , 0 - 31 */ + u16 data; /* 0, PCM block is the only master */ + /* 1, PCM block is shares to driver data out signal */ + /* other master */ + u16 reserved; +} __packed; + +enum { + AFE_I2S_SD0 = 1, + AFE_I2S_SD1, + AFE_I2S_SD2, + AFE_I2S_SD3, + AFE_I2S_QUAD01, + AFE_I2S_QUAD23, + AFE_I2S_6CHS, + AFE_I2S_8CHS, +}; + +#define AFE_MI2S_MONO 0 +#define AFE_MI2S_STEREO 3 +#define AFE_MI2S_4CHANNELS 4 +#define AFE_MI2S_6CHANNELS 6 +#define AFE_MI2S_8CHANNELS 8 + +struct afe_port_mi2s_cfg { + u16 bitwidth; /* 16,24,32 */ + u16 line; /* Called ChannelMode in documentation */ + /* i2s_sd0 = 1 */ + /* i2s_sd1 = 2 */ + /* i2s_sd2 = 3 */ + /* i2s_sd3 = 4 */ + /* i2s_quad01 = 5 */ + /* i2s_quad23 = 6 */ + /* i2s_6chs = 7 */ + /* i2s_8chs = 8 */ + u16 channel; /* Called MonoStereo in documentation */ + /* i2s mono = 0 */ + /* i2s mono right = 1 */ + /* i2s mono left = 2 */ + /* i2s stereo = 3 */ + u16 ws; /* 0, word select signal from external source */ + /* 1, word select signal from internal source */ + u16 format; /* don't touch this field if it is not for */ + /* AFE_PORT_CMD_I2S_CONFIG opcode */ +} __packed; + +struct afe_port_hdmi_cfg { + u16 bitwidth; /* 16,24,32 */ + u16 channel_mode; /* HDMI Stereo = 0 */ + /* HDMI_3Point1 (4-ch) = 1 */ + /* HDMI_5Point1 (6-ch) = 2 */ + /* HDMI_6Point1 (8-ch) = 3 */ + u16 data_type; /* HDMI_Linear = 0 */ + /* HDMI_non_Linear = 1 */ +} __packed; + + +struct afe_port_hdmi_multi_ch_cfg { + u16 data_type; /* HDMI_Linear = 0 */ + /* HDMI_non_Linear = 1 */ + u16 channel_allocation; /* The default is 0 (Stereo) */ + u16 reserved; /* must be set to 0 */ +} __packed; + + +/* Slimbus Device Ids */ +#define AFE_SLIMBUS_DEVICE_1 0x0 +#define AFE_SLIMBUS_DEVICE_2 0x1 +#define AFE_PORT_MAX_AUDIO_CHAN_CNT 16 + +struct afe_port_slimbus_cfg { + u16 slimbus_dev_id; /* SLIMBUS Device id.*/ + + u16 slave_dev_pgd_la; /* Slave ported generic device + * logical address. + */ + u16 slave_dev_intfdev_la; /* Slave interface device logical + * address. + */ + u16 bit_width; /* bit width of the samples, 16, 24.*/ + + u16 data_format; /* data format.*/ + + u16 num_channels; /* Number of channels.*/ + + /* Slave port mapping for respective channels.*/ + u16 slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; + + u16 reserved; +} __packed; + +struct afe_port_slimbus_sch_cfg { + u16 slimbus_dev_id; /* SLIMBUS Device id.*/ + u16 bit_width; /* bit width of the samples, 16, 24.*/ + u16 data_format; /* data format.*/ + u16 num_channels; /* Number of channels.*/ + u16 reserved; + /* Slave channel mapping for respective channels.*/ + u8 slave_ch_mapping[8]; +} __packed; + +struct afe_port_rtproxy_cfg { + u16 bitwidth; /* 16,24,32 */ + u16 interleaved; /* interleaved = 1 */ + /* Noninterleaved = 0 */ + u16 frame_sz; /* 5ms buffers = 160bytes */ + u16 jitter; /* 10ms of jitter = 320 */ + u16 lw_mark; /* Low watermark in bytes for triggering event*/ + u16 hw_mark; /* High watermark bytes for triggering event*/ + u16 rsvd; + int num_ch; /* 1 to 8 */ +} __packed; + +struct afe_port_pseudo_cfg { + u16 bit_width; + u16 num_channels; + u16 data_format; + u16 timing_mode; + u16 reserved; +} __packed; + +#define AFE_PORT_AUDIO_IF_CONFIG 0x000100d3 +#define AFE_PORT_AUDIO_SLIM_SCH_CONFIG 0x000100e4 +#define AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG 0x000100D9 +#define AFE_PORT_CMD_I2S_CONFIG 0x000100E7 + +union afe_port_config { + struct afe_port_pcm_cfg pcm; + struct afe_port_mi2s_cfg mi2s; + struct afe_port_hdmi_cfg hdmi; + struct afe_port_hdmi_multi_ch_cfg hdmi_multi_ch; + struct afe_port_slimbus_cfg slimbus; + struct afe_port_slimbus_sch_cfg slim_sch; + struct afe_port_rtproxy_cfg rtproxy; + struct afe_port_pseudo_cfg pseudo; +} __packed; + +struct afe_audioif_config_command { + struct apr_hdr hdr; + u16 port_id; + union afe_port_config port; +} __packed; + +#define AFE_TEST_CODEC_LOOPBACK_CTL 0x000100d5 +struct afe_codec_loopback_command { + u16 port_inf; /* Primary i2s = 0 */ + /* PCM = 2 */ + /* Secondary i2s = 4 */ + /* Mi2s = 6 */ + u16 enable; /* 0, disable. 1, enable */ +} __packed; + + +#define AFE_PARAM_ID_SIDETONE_GAIN 0x00010300 +struct afe_param_sidetone_gain { + u16 gain; + u16 reserved; +} __packed; + +#define AFE_PARAM_ID_SAMPLING_RATE 0x00010301 +struct afe_param_sampling_rate { + u32 sampling_rate; +} __packed; + + +#define AFE_PARAM_ID_CHANNELS 0x00010302 +struct afe_param_channels { + u16 channels; + u16 reserved; +} __packed; + + +#define AFE_PARAM_ID_LOOPBACK_GAIN 0x00010303 +struct afe_param_loopback_gain { + u16 gain; + u16 reserved; +} __packed; + +/* Parameter ID used to configure and enable/disable the loopback path. The + * difference with respect to the existing API, AFE_PORT_CMD_LOOPBACK, is that + * it allows Rx port to be configured as source port in loopback path. Port-id + * in AFE_PORT_CMD_SET_PARAM cmd is the source port which can be Tx or Rx port. + * In addition, we can configure the type of routing mode to handle different + * use cases. + */ +enum { + /* Regular loopback from source to destination port */ + LB_MODE_DEFAULT = 1, + /* Sidetone feed from Tx source to Rx destination port */ + LB_MODE_SIDETONE, + /* Echo canceller reference, voice + audio + DTMF */ + LB_MODE_EC_REF_VOICE_AUDIO, + /* Echo canceller reference, voice alone */ + LB_MODE_EC_REF_VOICE +}; + +#define AFE_PARAM_ID_LOOPBACK_CONFIG 0x0001020B +#define AFE_API_VERSION_LOOPBACK_CONFIG 0x1 +struct afe_param_loopback_cfg { + /* Minor version used for tracking the version of the configuration + * interface. + */ + uint32_t loopback_cfg_minor_version; + + /* Destination Port Id. */ + uint16_t dst_port_id; + + /* Specifies data path type from src to dest port. Supported values: + * LB_MODE_DEFAULT + * LB_MODE_SIDETONE + * LB_MODE_EC_REF_VOICE_AUDIO + * LB_MODE_EC_REF_VOICE + */ + uint16_t routing_mode; + + /* Specifies whether to enable (1) or disable (0) an AFE loopback. */ + uint16_t enable; + + /* Reserved for 32-bit alignment. This field must be set to 0. */ + uint16_t reserved; +} __packed; + +#define AFE_MODULE_ID_PORT_INFO 0x00010200 +/* Module ID for the loopback-related parameters. */ +#define AFE_MODULE_LOOPBACK 0x00010205 +struct afe_param_payload_base { + u32 module_id; + u32 param_id; + u16 param_size; + u16 reserved; +} __packed; + +struct afe_param_payload { + struct afe_param_payload_base base; + union { + struct afe_param_sidetone_gain sidetone_gain; + struct afe_param_sampling_rate sampling_rate; + struct afe_param_channels channels; + struct afe_param_loopback_gain loopback_gain; + struct afe_param_loopback_cfg loopback_cfg; + } __packed param; +} __packed; + +#define AFE_PORT_CMD_SET_PARAM 0x000100dc + +struct afe_port_cmd_set_param { + struct apr_hdr hdr; + u16 port_id; + u16 payload_size; + u32 payload_address; + struct afe_param_payload payload; +} __packed; + +struct afe_port_cmd_set_param_no_payload { + struct apr_hdr hdr; + u16 port_id; + u16 payload_size; + u32 payload_address; +} __packed; + +#define AFE_EVENT_GET_ACTIVE_PORTS 0x00010100 +struct afe_get_active_ports_rsp { + u16 num_ports; + u16 port_id; +} __packed; + + +#define AFE_EVENT_GET_ACTIVE_HANDLES 0x00010102 +struct afe_get_active_handles_rsp { + u16 port_id; + u16 num_handles; + u16 mode; /* 0, voice rx */ + /* 1, voice tx */ + /* 2, audio rx */ + /* 3, audio tx */ + u16 handle; +} __packed; + +#define AFE_SERVICE_CMD_MEMORY_MAP 0x000100DE +struct afe_cmd_memory_map { + struct apr_hdr hdr; + u32 phy_addr; + u32 mem_sz; + u16 mem_id; + u16 rsvd; +} __packed; + +#define AFE_SERVICE_CMD_MEMORY_UNMAP 0x000100DF +struct afe_cmd_memory_unmap { + struct apr_hdr hdr; + u32 phy_addr; +} __packed; + +#define AFE_SERVICE_CMD_REG_RTPORT 0x000100E0 +struct afe_cmd_reg_rtport { + struct apr_hdr hdr; + u16 port_id; + u16 rsvd; +} __packed; + +#define AFE_SERVICE_CMD_UNREG_RTPORT 0x000100E1 +struct afe_cmd_unreg_rtport { + struct apr_hdr hdr; + u16 port_id; + u16 rsvd; +} __packed; + +#define AFE_SERVICE_CMD_RTPORT_WR 0x000100E2 +struct afe_cmd_rtport_wr { + struct apr_hdr hdr; + u16 port_id; + u16 rsvd; + u32 buf_addr; + u32 bytes_avail; +} __packed; + +#define AFE_SERVICE_CMD_RTPORT_RD 0x000100E3 +struct afe_cmd_rtport_rd { + struct apr_hdr hdr; + u16 port_id; + u16 rsvd; + u32 buf_addr; + u32 bytes_avail; +} __packed; + +#define AFE_EVENT_RT_PROXY_PORT_STATUS 0x00010105 + +#define ADM_MAX_COPPS 5 + +#define ADM_SERVICE_CMD_GET_COPP_HANDLES 0x00010300 +struct adm_get_copp_handles_command { + struct apr_hdr hdr; +} __packed; + +#define ADM_CMD_MATRIX_MAP_ROUTINGS 0x00010301 +struct adm_routings_session { + u16 id; + u16 num_copps; + u16 copp_id[ADM_MAX_COPPS+1]; /*Padding if numCopps is odd */ +} __packed; + +struct adm_routings_command { + struct apr_hdr hdr; + u32 path; /* 0 = Rx, 1 Tx */ + u32 num_sessions; + struct adm_routings_session session[8]; +} __packed; + + +#define ADM_CMD_MATRIX_RAMP_GAINS 0x00010302 +struct adm_ramp_gain { + struct apr_hdr hdr; + u16 session_id; + u16 copp_id; + u16 initial_gain; + u16 gain_increment; + u16 ramp_duration; + u16 reserved; +} __packed; + +struct adm_ramp_gains_command { + struct apr_hdr hdr; + u32 id; + u32 num_gains; + struct adm_ramp_gain gains[ADM_MAX_COPPS]; +} __packed; + + +#define ADM_CMD_COPP_OPEN 0x00010304 +struct adm_copp_open_command { + struct apr_hdr hdr; + u16 flags; + u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */ + u16 endpoint_id1; + u16 endpoint_id2; + u32 topology_id; + u16 channel_config; + u16 reserved; + u32 rate; +} __packed; + +#define ADM_CMD_COPP_CLOSE 0x00010305 + +#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN 0x00010310 +#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3 0x00010333 +struct adm_multi_ch_copp_open_command { + struct apr_hdr hdr; + u16 flags; + u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */ + u16 endpoint_id1; + u16 endpoint_id2; + u32 topology_id; + u16 channel_config; + u16 reserved; + u32 rate; + u8 dev_channel_mapping[8]; +} __packed; + +struct adm_multi_channel_copp_open_v3 { + struct apr_hdr hdr; + u16 flags; + u16 mode; + u16 endpoint_id1; + u16 endpoint_id2; + u32 topology_id; + u16 channel_config; + u16 bit_width; + u32 rate; + u8 dev_channel_mapping[8]; +}; + +#define ADM_CMD_MEMORY_MAP 0x00010C30 +struct adm_cmd_memory_map { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u16 mempool_id; + u16 reserved; +} __packed; + +#define ADM_CMD_MEMORY_UNMAP 0x00010C31 +struct adm_cmd_memory_unmap { + struct apr_hdr hdr; + u32 buf_add; +} __packed; + +#define ADM_CMD_MEMORY_MAP_REGIONS 0x00010C47 +struct adm_memory_map_regions { + u32 phys; + u32 buf_size; +} __packed; + +struct adm_cmd_memory_map_regions { + struct apr_hdr hdr; + u16 mempool_id; + u16 nregions; +} __packed; + +#define ADM_CMD_MEMORY_UNMAP_REGIONS 0x00010C48 +struct adm_memory_unmap_regions { + u32 phys; +} __packed; + +struct adm_cmd_memory_unmap_regions { + struct apr_hdr hdr; + u16 nregions; + u16 reserved; +} __packed; + +#define DEFAULT_COPP_TOPOLOGY 0x00010be3 +#define DEFAULT_POPP_TOPOLOGY 0x00010be4 +#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71 +#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72 +#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75 + +#define LOWLATENCY_POPP_TOPOLOGY 0x00010C68 +#define LOWLATENCY_COPP_TOPOLOGY 0x00010312 +#define PCM_BITS_PER_SAMPLE 16 + +#define ASM_OPEN_WRITE_PERF_MODE_BIT (1<<28) +#define ASM_OPEN_READ_PERF_MODE_BIT (1<<29) +#define ADM_MULTI_CH_COPP_OPEN_PERF_MODE_BIT (1<<13) + + +#define ASM_MAX_EQ_BANDS 12 + +struct asm_eq_band { + u32 band_idx; /* The band index, 0 .. 11 */ + u32 filter_type; /* Filter band type */ + u32 center_freq_hz; /* Filter band center frequency */ + u32 filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + u32 q_factor; +} __packed; + +struct asm_equalizer_params { + u32 enable; + u32 num_bands; + struct asm_eq_band eq_bands[ASM_MAX_EQ_BANDS]; +} __packed; + +struct asm_master_gain_params { + u16 master_gain; + u16 padding; +} __packed; + +struct asm_lrchannel_gain_params { + u16 left_gain; + u16 right_gain; +} __packed; + +struct asm_mute_params { + u32 muteflag; +} __packed; + +struct asm_softvolume_params { + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +struct asm_softpause_params { + u32 enable; + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +struct asm_pp_param_data_hdr { + u32 module_id; + u32 param_id; + u16 param_size; + u16 reserved; +} __packed; + +struct asm_pp_params_command { + struct apr_hdr hdr; + u32 *payload; + u32 payload_size; + struct asm_pp_param_data_hdr params; +} __packed; + +#define EQUALIZER_MODULE_ID 0x00010c27 +#define EQUALIZER_PARAM_ID 0x00010c28 + +#define VOLUME_CONTROL_MODULE_ID 0x00010bfe +#define MASTER_GAIN_PARAM_ID 0x00010bff +#define L_R_CHANNEL_GAIN_PARAM_ID 0x00010c00 +#define MUTE_CONFIG_PARAM_ID 0x00010c01 +#define SOFT_PAUSE_PARAM_ID 0x00010D6A +#define SOFT_VOLUME_PARAM_ID 0x00010C29 + +#define IIR_FILTER_ENABLE_PARAM_ID 0x00010c03 +#define IIR_FILTER_PREGAIN_PARAM_ID 0x00010c04 +#define IIR_FILTER_CONFIG_PARAM_ID 0x00010c05 + +#define MBADRC_MODULE_ID 0x00010c06 +#define MBADRC_ENABLE_PARAM_ID 0x00010c07 +#define MBADRC_CONFIG_PARAM_ID 0x00010c08 + + +#define ADM_CMD_SET_PARAMS 0x00010306 +#define ADM_CMD_GET_PARAMS 0x0001030B +#define ADM_CMDRSP_GET_PARAMS 0x0001030C +struct adm_set_params_command { + struct apr_hdr hdr; + u32 payload; + u32 payload_size; +} __packed; + + +#define ADM_CMD_TAP_COPP_PCM 0x00010307 +struct adm_tap_copp_pcm_command { + struct apr_hdr hdr; +} __packed; + + +/* QDSP6 to Client messages */ +#define ADM_SERVICE_CMDRSP_GET_COPP_HANDLES 0x00010308 +struct adm_get_copp_handles_respond { + struct apr_hdr hdr; + u32 handles; + u32 copp_id; +} __packed; + +#define ADM_CMDRSP_COPP_OPEN 0x0001030A +struct adm_copp_open_respond { + u32 status; + u16 copp_id; + u16 reserved; +} __packed; + +#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN 0x00010311 +#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3 0x00010334 + + +#define ASM_STREAM_PRIORITY_NORMAL 0 +#define ASM_STREAM_PRIORITY_LOW 1 +#define ASM_STREAM_PRIORITY_HIGH 2 +#define ASM_STREAM_PRIORITY_RESERVED 3 + +#define ASM_END_POINT_DEVICE_MATRIX 0 +#define ASM_END_POINT_STREAM 1 + +#define AAC_ENC_MODE_AAC_LC 0x02 +#define AAC_ENC_MODE_AAC_P 0x05 +#define AAC_ENC_MODE_EAAC_P 0x1D + +#define ASM_STREAM_CMD_CLOSE 0x00010BCD +#define ASM_STREAM_CMD_FLUSH 0x00010BCE +#define ASM_STREAM_CMD_SET_PP_PARAMS 0x00010BCF +#define ASM_STREAM_CMD_GET_PP_PARAMS 0x00010BD0 +#define ASM_STREAM_CMDRSP_GET_PP_PARAMS 0x00010BD1 +#define ASM_SESSION_CMD_PAUSE 0x00010BD3 +#define ASM_SESSION_CMD_GET_SESSION_TIME 0x00010BD4 +#define ASM_DATA_CMD_EOS 0x00010BDB +#define ASM_DATA_EVENT_EOS 0x00010BDD + +#define ASM_SERVICE_CMD_GET_STREAM_HANDLES 0x00010C0B +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 + +#define ASM_SESSION_EVENT_RX_UNDERFLOW 0x00010C17 +#define ASM_SESSION_EVENT_TX_OVERFLOW 0x00010C18 +#define ASM_SERVICE_CMD_GET_WALLCLOCK_TIME 0x00010C19 +#define ASM_DATA_CMDRSP_EOS 0x00010C1C + +/* ASM Data structures */ + +/* common declarations */ +struct asm_pcm_cfg { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u16 is_signed; + u16 interleaved; +}; + +#define PCM_CHANNEL_NULL 0 + +/* Front left channel. */ +#define PCM_CHANNEL_FL 1 + +/* Front right channel. */ +#define PCM_CHANNEL_FR 2 + +/* Front center channel. */ +#define PCM_CHANNEL_FC 3 + +/* Left surround channel.*/ +#define PCM_CHANNEL_LS 4 + +/* Right surround channel.*/ +#define PCM_CHANNEL_RS 5 + +/* Low frequency effect channel. */ +#define PCM_CHANNEL_LFE 6 + +/* Center surround channel; Rear center channel. */ +#define PCM_CHANNEL_CS 7 + +/* Left back channel; Rear left channel. */ +#define PCM_CHANNEL_LB 8 + +/* Right back channel; Rear right channel. */ +#define PCM_CHANNEL_RB 9 + +/* Top surround channel. */ +#define PCM_CHANNEL_TS 10 + +/* Center vertical height channel.*/ +#define PCM_CHANNEL_CVH 11 + +/* Mono surround channel.*/ +#define PCM_CHANNEL_MS 12 + +/* Front left of center. */ +#define PCM_CHANNEL_FLC 13 + +/* Front right of center. */ +#define PCM_CHANNEL_FRC 14 + +/* Rear left of center. */ +#define PCM_CHANNEL_RLC 15 + +/* Rear right of center. */ +#define PCM_CHANNEL_RRC 16 + +#define PCM_FORMAT_MAX_NUM_CHANNEL 8 + +/* Maximum number of channels supported + * in ASM_ENCDEC_DEC_CHAN_MAP command + */ +#define MAX_CHAN_MAP_CHANNELS 16 +/* + * Multiple-channel PCM decoder format block structure used in the + * #ASM_STREAM_CMD_OPEN_WRITE command. + * The data must be in little-endian format. + */ +struct asm_multi_channel_pcm_fmt_blk { + + u16 num_channels; /* + * Number of channels. + * Supported values:1 to 8 + */ + + u16 bits_per_sample; /* + * Number of bits per sample per channel. + * Supported values: 16, 24 When used for + * playback, the client must send 24-bit + * samples packed in 32-bit words. The + * 24-bit samples must be placed in the most + * significant 24 bits of the 32-bit word. When + * used for recording, the aDSP sends 24-bit + * samples packed in 32-bit words. The 24-bit + * samples are placed in the most significant + * 24 bits of the 32-bit word. + */ + + u32 sample_rate; /* + * Number of samples per second + * (in Hertz). Supported values: + * 2000 to 48000 + */ + + u16 is_signed; /* + * Flag that indicates the samples + * are signed (1). + */ + + u16 is_interleaved; /* + * Flag that indicates whether the channels are + * de-interleaved (0) or interleaved (1). + * Interleaved format means corresponding + * samples from the left and right channels are + * interleaved within the buffer. + * De-interleaved format means samples from + * each channel are contiguous in the buffer. + * The samples from one channel immediately + * follow those of the previous channel. + */ + + u8 channel_mapping[8]; /* + * Supported values: + * PCM_CHANNEL_NULL, PCM_CHANNEL_FL, + * PCM_CHANNEL_FR, PCM_CHANNEL_FC, + * PCM_CHANNEL_LS, PCM_CHANNEL_RS, + * PCM_CHANNEL_LFE, PCM_CHANNEL_CS, + * PCM_CHANNEL_LB, PCM_CHANNEL_RB, + * PCM_CHANNEL_TS, PCM_CHANNEL_CVH, + * PCM_CHANNEL_MS, PCM_CHANNEL_FLC, + * PCM_CHANNEL_FRC, PCM_CHANNEL_RLC, + * PCM_CHANNEL_RRC. + * Channel[i] mapping describes channel I. Each + * element i of the array describes channel I + * inside the buffer where I < num_channels. + * An unused channel is set to zero. + */ +}; +struct asm_dts_enc_cfg { + uint32_t sample_rate; + /* + * Samples at which input is to be encoded. + * Supported values: + * 44100 -- encode at 44.1 Khz + * 48000 -- encode at 48 Khz + */ + + uint32_t num_channels; + /* + * Number of channels for multi-channel encoding. + * Supported values: 1 to 6 + */ + + uint8_t channel_mapping[6]; + /* + * Channel array of size 16. Channel[i] mapping describes channel I. + * Each element i of the array describes channel I inside the buffer + * where num_channels. An unused channel is set to zero. Only first + * num_channels elements are valid + * + * Supported values: + * - # PCM_CHANNEL_L + * - # PCM_CHANNEL_R + * - # PCM_CHANNEL_C + * - # PCM_CHANNEL_LS + * - # PCM_CHANNEL_RS + * - # PCM_CHANNEL_LFE + */ + +}; +struct asm_adpcm_cfg { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u32 block_size; +}; + +struct asm_yadpcm_cfg { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; +}; + +struct asm_midi_cfg { + u32 nMode; +}; + +struct asm_wma_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +}; + +struct asm_wmapro_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +}; + +struct asm_aac_cfg { + u16 format; + u16 aot; + u16 ep_config; + u16 section_data_resilience; + u16 scalefactor_data_resilience; + u16 spectral_data_resilience; + u16 ch_cfg; + u16 reserved; + u32 sample_rate; +}; + +struct asm_amrwbplus_cfg { + u32 size_bytes; + u32 version; + u32 num_channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_fmt; + u32 amr_lsf_idx; +}; + +struct asm_flac_cfg { + u16 stream_info_present; + u16 min_blk_size; + u16 max_blk_size; + u16 ch_cfg; + u16 sample_size; + u16 sample_rate; + u16 md5_sum; + u32 ext_sample_rate; + u32 min_frame_size; + u32 max_frame_size; +}; + +struct asm_vorbis_cfg { + u32 ch_cfg; + u32 bit_rate; + u32 min_bit_rate; + u32 max_bit_rate; + u16 bit_depth_pcm_sample; + u16 bit_stream_format; +}; + +struct asm_aac_read_cfg { + u32 bitrate; + u32 enc_mode; + u16 format; + u16 ch_cfg; + u32 sample_rate; +}; + +struct asm_amrnb_read_cfg { + u16 mode; + u16 dtx_mode; +}; + +struct asm_amrwb_read_cfg { + u16 mode; + u16 dtx_mode; +}; + +struct asm_evrc_read_cfg { + u16 max_rate; + u16 min_rate; + u16 rate_modulation_cmd; + u16 reserved; +}; + +struct asm_qcelp13_read_cfg { + u16 max_rate; + u16 min_rate; + u16 reduced_rate_level; + u16 rate_modulation_cmd; +}; + +struct asm_sbc_read_cfg { + u32 subband; + u32 block_len; + u32 ch_mode; + u32 alloc_method; + u32 bit_rate; + u32 sample_rate; +}; + +struct asm_sbc_bitrate { + u32 bitrate; +}; + +struct asm_immed_decode { + u32 mode; +}; + +struct asm_sbr_ps { + u32 enable; +}; + +struct asm_dual_mono { + u16 sce_left; + u16 sce_right; +}; + +struct asm_dec_chan_map { + u32 num_channels; /* Number of decoder output + * channels. A value of 0 + * indicates native channel + * mapping, which is valid + * only for NT mode. This + * means the output of the + * decoder is to be preserved + * as is. + */ + + u8 channel_mapping[MAX_CHAN_MAP_CHANNELS];/* Channel array of size + * num_channels. It can grow + * till MAX_CHAN_MAP_CHANNELS. + * Channel[i] mapping + * describes channel I inside + * the decoder output buffer. + * Valid channel mapping + * values are to be present at + * the beginning of the array. + * All remaining elements of + * the array are to be filled + * with PCM_CHANNEL_NULL. + */ +}; + +struct asm_encode_cfg_blk { + u32 frames_per_buf; + u32 format_id; + u32 cfg_size; + union { + struct asm_pcm_cfg pcm; + struct asm_aac_read_cfg aac; + struct asm_amrnb_read_cfg amrnb; + struct asm_evrc_read_cfg evrc; + struct asm_qcelp13_read_cfg qcelp13; + struct asm_sbc_read_cfg sbc; + struct asm_amrwb_read_cfg amrwb; + struct asm_multi_channel_pcm_fmt_blk mpcm; + struct asm_dts_enc_cfg dts; + } __packed cfg; +}; + +struct asm_frame_meta_info { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +}; + +/* Stream level commands */ +#define ASM_STREAM_CMD_OPEN_READ 0x00010BCB +#define ASM_STREAM_CMD_OPEN_READ_V2_1 0x00010DB2 +struct asm_stream_cmd_open_read { + struct apr_hdr hdr; + u32 uMode; + u32 src_endpoint; + u32 pre_proc_top; + u32 format; +} __packed; + +struct asm_stream_cmd_open_read_v2_1 { + struct apr_hdr hdr; + u32 uMode; + u32 src_endpoint; + u32 pre_proc_top; + u32 format; + u16 bits_per_sample; + u16 reserved; +} __packed; + +/* Supported formats */ +#define LINEAR_PCM 0x00010BE5 +#define DTMF 0x00010BE6 +#define ADPCM 0x00010BE7 +#define YADPCM 0x00010BE8 +#define MP3 0x00010BE9 +#define MPEG4_AAC 0x00010BEA +#define AMRNB_FS 0x00010BEB +#define AMRWB_FS 0x00010BEC +#define V13K_FS 0x00010BED +#define EVRC_FS 0x00010BEE +#define EVRCB_FS 0x00010BEF +#define EVRCWB_FS 0x00010BF0 +#define MIDI 0x00010BF1 +#define SBC 0x00010BF2 +#define WMA_V10PRO 0x00010BF3 +#define WMA_V9 0x00010BF4 +#define AMR_WB_PLUS 0x00010BF5 +#define AC3_DECODER 0x00010BF6 +#define EAC3_DECODER 0x00010C3C +#define DTS 0x00010D88 +#define DTS_LBR 0x00010DBB +#define MP2 0x00010DBE +#define ATRAC 0x00010D89 +#define MAT 0x00010D8A +#define G711_ALAW_FS 0x00010BF7 +#define G711_MLAW_FS 0x00010BF8 +#define G711_PCM_FS 0x00010BF9 +#define MPEG4_MULTI_AAC 0x00010D86 +#define US_POINT_EPOS_FORMAT 0x00012310 +#define US_RAW_FORMAT 0x0001127C +#define US_PROX_FORMAT 0x0001272B +#define MULTI_CHANNEL_PCM 0x00010C66 + +#define ASM_ENCDEC_SBCRATE 0x00010C13 +#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14 +#define ASM_ENCDEC_CFG_BLK 0x00010C2C + +#define ASM_ENCDEC_SBCRATE 0x00010C13 +#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14 +#define ASM_ENCDEC_CFG_BLK 0x00010C2C + +#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED 0x00010D95 +struct asm_stream_cmd_open_read_compressed { + struct apr_hdr hdr; + u32 uMode; + u32 frame_per_buf; +} __packed; + +#define ASM_STREAM_CMD_OPEN_WRITE 0x00010BCA +#define ASM_STREAM_CMD_OPEN_WRITE_V2_1 0x00010DB1 +struct asm_stream_cmd_open_write { + struct apr_hdr hdr; + u32 uMode; + u16 sink_endpoint; + u16 stream_handle; + u32 post_proc_top; + u32 format; +} __packed; + +#define IEC_61937_MASK 0x00000001 +#define IEC_60958_MASK 0x00000002 + +#define ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED 0x00010D84 +struct asm_stream_cmd_open_write_compressed { + struct apr_hdr hdr; + u32 flags; + u32 format; +} __packed; +#define ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK 0x00010DBA +struct asm_stream_cmd_open_transcode_loopback { + struct apr_hdr hdr; + uint32_t mode_flags; + /* + * All bits are reserved. Clients must set them to zero. + */ + + uint32_t src_format_id; + /* + * Specifies the media format of the input audio stream. + * + * Supported values: + * - #ASM_MEDIA_FMT_LINEAR_PCM + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM + */ + + uint32_t sink_format_id; + /* + * Specifies the media format of the output stream. + * + * Supported values: + * - #ASM_MEDIA_FMT_LINEAR_PCM + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM + * - #ASM_MEDIA_FMT_DTS + */ + + uint32_t audproc_topo_id; + /* + * Postprocessing topology ID, which specifies the topology (order of + * processing) of postprocessing algorithms. + * + * Supported values: + * - #ASM_STREAM_POSTPROC_TOPO_ID_DEFAULT + * - #ASM_STREAM_POSTPROC_TOPO_ID_PEAKMETER + * - #ASM_STREAM_POSTPROC_TOPO_ID_NONE + * - #ASM_STREAM_POSTPROC_TOPO_ID_MCH_PEAK_VOL + */ + + uint16_t src_endpoint_type; + /* + * Specifies the source endpoint that provides the input samples. + * + * Supported values: + * - 0 -- Tx device matrix or stream router + * (gateway to the hardware ports) + * - All other values are reserved + * + * Clients must set this field to zero. Otherwise, an error is returned. + */ + + uint16_t sink_endpoint_type; + /* + * Specifies the sink endpoint type. + * + * Supported values: + * - 0 -- Rx device matrix or stream router + * (gateway to the hardware ports) + * - All other values are reserved + * + * Clients must set this field to zero. Otherwise, an error is returned. + */ + + uint16_t bits_per_sample; + /* + * Number of bits per sample processed by the ASM modules. + * Supported values: 16, 24 + */ + + uint16_t reserved; + /* + * This field must be set to zero. + */ +} __packed; + +/* + * ID of the DTS mix LFE channel to front channels parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + * asm_dts_generic_param_t + * ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT + */ +#define ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT 0x00010DB6 + +/* + * ID of the DTS DRC ratio parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + * asm_dts_generic_param_t + * ASM_PARAM_ID_DTS_DRC_RATIO + */ +#define ASM_PARAM_ID_DTS_DRC_RATIO 0x00010DB7 + +/* + * ID of the DTS enable dialog normalization parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + * + * asm_dts_generic_param_t + * ASM_PARAM_ID_DTS_ENABLE_DIALNORM + */ +#define ASM_PARAM_ID_DTS_ENABLE_DIALNORM 0x00010DB8 + +/* + * ID of the DTS enable parse REV2AUX parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + * asm_dts_generic_param_t + * ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX + */ +#define ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX 0x00010DB9 + +struct asm_dts_generic_param { + int32_t generic_parameter; + /* + * #ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT: + * - if enabled, mixes LFE channel to front + * while downmixing (if necessary) + * - Supported values: 1-> enable, 0-> disable + * - Default: disabled + * + * #ASM_PARAM_ID_DTS_DRC_RATIO: + * - percentage of DRC ratio. + * - Supported values: 0-100 + * - Default: 0, DRC is disabled. + * + * #ASM_PARAM_ID_DTS_ENABLE_DIALNORM: + * - flag to enable dialog normalization post processing. + * - Supported values: 1-> enable, 0-> disable. + * - Default: enabled. + * + * #ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX: + * - flag to enable parsing of rev2aux chunk in the bitstream. + * This chunk contains broadcast metadata. + * - Supported values: 1-> enable, 0-> disable. + * - Default: disabled. + */ +}; + +struct asm_stream_cmd_dts_dec_param { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_dts_generic_param generic_param; +} __packed; + + +#define ASM_STREAM_CMD_OPEN_READWRITE 0x00010BCC + +struct asm_stream_cmd_open_read_write { + struct apr_hdr hdr; + u32 uMode; + u32 post_proc_top; + u32 write_format; + u32 read_format; +} __packed; + +#define ASM_STREAM_CMD_OPEN_LOOPBACK 0x00010D6E +struct asm_stream_cmd_open_loopback { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags. + * Bit 0-31: reserved; client should set these bits to 0 + */ + u16 src_endpointype; + /* Endpoint type. 0 = Tx Matrix */ + u16 sink_endpointype; + /* Endpoint type. 0 = Rx Matrix */ + u32 postprocopo_id; +/* Postprocessor topology ID. Specifies the topology of + * postprocessing algorithms. + */ +} __packed; + +#define ADM_CMD_CONNECT_AFE_PORT 0x00010320 +#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321 + +struct adm_cmd_connect_afe_port { + struct apr_hdr hdr; + u8 mode; /*mode represent the interface is for RX or TX*/ + u8 session_id; /*ASM session ID*/ + u16 afe_port_id; +} __packed; + +#define ADM_CMD_CONNECT_AFE_PORT_V2 0x00010332 + +struct adm_cmd_connect_afe_port_v2 { + struct apr_hdr hdr; + u8 mode; /*mode represent the interface is for RX or TX*/ + u8 session_id; /*ASM session ID*/ + u16 afe_port_id; + u32 num_channels; + u32 sampling_rate; +} __packed; + +#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10 +#define ASM_STREAM_CMD_GET_ENCDEC_PARAM 0x00010C11 +#define ASM_ENCDEC_CFG_BLK_ID 0x00010C2C +#define ASM_ENABLE_SBR_PS 0x00010C63 +#define ASM_CONFIGURE_DUAL_MONO 0x00010C64 +struct asm_stream_cmd_encdec_cfg_blk { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_encode_cfg_blk enc_blk; +} __packed; + +struct asm_stream_cmd_encdec_sbc_bitrate { + struct apr_hdr hdr; + u32 param_id; + struct asm_sbc_bitrate sbc_bitrate; +} __packed; + +struct asm_stream_cmd_encdec_immed_decode { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_immed_decode dec; +} __packed; + +struct asm_stream_cmd_encdec_sbr { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_sbr_ps sbr_ps; +} __packed; + +struct asm_stream_cmd_encdec_dualmono { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_dual_mono channel_map; +} __packed; + +#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG 0x00010DD8 + +/* Structure for AAC decoder stereo coefficient setting. */ + +struct asm_aac_stereo_mix_coeff_selection_param { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + u32 aac_stereo_mix_coeff_flag; +} __packed; + +#define ASM_ENCDEC_DEC_CHAN_MAP 0x00010D82 +struct asm_stream_cmd_encdec_channelmap { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_dec_chan_map chan_map; +} __packed; + +#define ASM_STREAM_CMD_ADJUST_SAMPLES 0x00010C0A +struct asm_stream_cmd_adjust_samples { + struct apr_hdr hdr; + u16 nsamples; + u16 reserved; +} __packed; + +#define ASM_STREAM_CMD_TAP_POPP_PCM 0x00010BF9 +struct asm_stream_cmd_tap_popp_pcm { + struct apr_hdr hdr; + u16 enable; + u16 reserved; + u32 module_id; +} __packed; + +/* Session Level commands */ +#define ASM_SESSION_CMD_MEMORY_MAP 0x00010C32 +struct asm_stream_cmd_memory_map { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u16 mempool_id; + u16 reserved; +} __packed; + +#define ASM_SESSION_CMD_MEMORY_UNMAP 0x00010C33 +struct asm_stream_cmd_memory_unmap { + struct apr_hdr hdr; + u32 buf_add; +} __packed; + +#define ASM_SESSION_CMD_MEMORY_MAP_REGIONS 0x00010C45 +struct asm_memory_map_regions { + u32 phys; + u32 buf_size; +} __packed; + +struct asm_stream_cmd_memory_map_regions { + struct apr_hdr hdr; + u16 mempool_id; + u16 nregions; +} __packed; + +#define ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS 0x00010C46 +struct asm_memory_unmap_regions { + u32 phys; +} __packed; + +struct asm_stream_cmd_memory_unmap_regions { + struct apr_hdr hdr; + u16 nregions; + u16 reserved; +} __packed; + +#define ASM_SESSION_CMD_RUN 0x00010BD2 +struct asm_stream_cmd_run { + struct apr_hdr hdr; + u32 flags; + u32 msw_ts; + u32 lsw_ts; +} __packed; + +/* Session level events */ +#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5 +struct asm_stream_cmd_reg_rx_underflow_event { + struct apr_hdr hdr; + u16 enable; + u16 reserved; +} __packed; + +#define ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS 0x00010BD6 +struct asm_stream_cmd_reg_tx_overflow_event { + struct apr_hdr hdr; + u16 enable; + u16 reserved; +} __packed; + +/* Data Path commands */ +#define ASM_DATA_CMD_WRITE 0x00010BD9 +struct asm_stream_cmd_write { + struct apr_hdr hdr; + u32 buf_add; + u32 avail_bytes; + u32 uid; + u32 msw_ts; + u32 lsw_ts; + u32 uflags; +} __packed; + +#define ASM_DATA_CMD_READ 0x00010BDA +struct asm_stream_cmd_read { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u32 uid; +} __packed; + +#define ASM_DATA_CMD_READ_COMPRESSED 0x00010DBF +struct asm_stream_cmd_read_compressed { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u32 uid; +} __packed; + +#define ASM_DATA_CMD_MEDIA_FORMAT_UPDATE 0x00010BDC +#define ASM_DATA_EVENT_ENC_SR_CM_NOTIFY 0x00010BDE +struct asm_stream_media_format_update { + struct apr_hdr hdr; + u32 format; + u32 cfg_size; + union { + struct asm_pcm_cfg pcm_cfg; + struct asm_adpcm_cfg adpcm_cfg; + struct asm_yadpcm_cfg yadpcm_cfg; + struct asm_midi_cfg midi_cfg; + struct asm_wma_cfg wma_cfg; + struct asm_wmapro_cfg wmapro_cfg; + struct asm_aac_cfg aac_cfg; + struct asm_flac_cfg flac_cfg; + struct asm_vorbis_cfg vorbis_cfg; + struct asm_multi_channel_pcm_fmt_blk multi_ch_pcm_cfg; + struct asm_amrwbplus_cfg amrwbplus_cfg; + } __packed write_cfg; +} __packed; + + +/* Command Responses */ +#define ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM 0x00010C12 +struct asm_stream_cmdrsp_get_readwrite_param { + struct apr_hdr hdr; + u32 status; + u32 param_id; + u16 param_size; + u16 padding; + union { + struct asm_sbc_bitrate sbc_bitrate; + struct asm_immed_decode aac_dec; + } __packed read_write_cfg; +} __packed; + + +#define ASM_SESSION_CMDRSP_GET_SESSION_TIME 0x00010BD8 +struct asm_stream_cmdrsp_get_session_time { + struct apr_hdr hdr; + u32 status; + u32 msw_ts; + u32 lsw_ts; +} __packed; + +#define ASM_DATA_EVENT_WRITE_DONE 0x00010BDF +struct asm_data_event_write_done { + u32 buf_add; + u32 status; +} __packed; + +#define ASM_DATA_EVENT_READ_DONE 0x00010BE0 +struct asm_data_event_read_done { + u32 status; + u32 buffer_add; + u32 enc_frame_size; + u32 offset; + u32 msw_ts; + u32 lsw_ts; + u32 flags; + u32 num_frames; + u32 id; +} __packed; + +#define ASM_DATA_EVENT_READ_COMPRESSED_DONE 0x00010DC0 +struct asm_data_event_read_compressed_done { + u32 status; + u32 buffer_add; + u32 enc_frame_size; + u32 offset; + u32 msw_ts; + u32 lsw_ts; + u32 flags; + u32 num_frames; + u32 id; +} __packed; + +#define ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY 0x00010C65 +struct asm_data_event_sr_cm_change_notify { + u32 sample_rate; + u16 no_of_channels; + u16 reserved; + u8 channel_map[8]; +} __packed; + +/* service level events */ + +#define ASM_SERVICE_CMDRSP_GET_STREAM_HANDLES 0x00010C1B +struct asm_svc_cmdrsp_get_strm_handles { + struct apr_hdr hdr; + u32 num_handles; + u32 stream_handles; +} __packed; + + +#define ASM_SERVICE_CMDRSP_GET_WALLCLOCK_TIME 0x00010C1A +struct asm_svc_cmdrsp_get_wallclock_time { + struct apr_hdr hdr; + u32 status; + u32 msw_ts; + u32 lsw_ts; +} __packed; + +/* Error code */ +#define ADSP_EOK 0x00000000 /* Success / completed / no errors. */ +#define ADSP_EFAILED 0x00000001 /* General failure. */ +#define ADSP_EBADPARAM 0x00000002 /* Bad operation parameter(s). */ +#define ADSP_EUNSUPPORTED 0x00000003 /* Unsupported routine/operation. */ +#define ADSP_EVERSION 0x00000004 /* Unsupported version. */ +#define ADSP_EUNEXPECTED 0x00000005 /* Unexpected problem encountered. */ +#define ADSP_EPANIC 0x00000006 /* Unhandled problem occurred. */ +#define ADSP_ENORESOURCE 0x00000007 /* Unable to allocate resource(s). */ +#define ADSP_EHANDLE 0x00000008 /* Invalid handle. */ +#define ADSP_EALREADY 0x00000009 /* Operation is already processed. */ +#define ADSP_ENOTREADY 0x0000000A /* Operation not ready to be processed*/ +#define ADSP_EPENDING 0x0000000B /* Operation is pending completion*/ +#define ADSP_EBUSY 0x0000000C /* Operation could not be accepted or + * processed. + */ +#define ADSP_EABORTED 0x0000000D /* Operation aborted due to an error. */ +#define ADSP_EPREEMPTED 0x0000000E /* Operation preempted by higher priority*/ +#define ADSP_ECONTINUE 0x0000000F /* Operation requests intervention + * to complete. + */ +#define ADSP_EIMMEDIATE 0x00000010 /* Operation requests immediate + * intervention to complete. + */ +#define ADSP_ENOTIMPL 0x00000011 /* Operation is not implemented. */ +#define ADSP_ENEEDMORE 0x00000012 /* Operation needs more data or resources*/ + +/* SRS TRUMEDIA GUIDS */ +#define SRS_TRUMEDIA_TOPOLOGY_ID 0x00010D90 +#define SRS_TRUMEDIA_MODULE_ID 0x10005010 +#define SRS_TRUMEDIA_PARAMS 0x10005011 +#define SRS_TRUMEDIA_PARAMS_WOWHD 0x10005012 +#define SRS_TRUMEDIA_PARAMS_CSHP 0x10005013 +#define SRS_TRUMEDIA_PARAMS_HPF 0x10005014 +#define SRS_TRUMEDIA_PARAMS_PEQ 0x10005015 +#define SRS_TRUMEDIA_PARAMS_HL 0x10005016 + +/* SRS STUDIO SOUND 3D GUIDS */ +#define SRS_SS3D_TOPOLOGY_ID 0x00010720 +#define SRS_SS3D_MODULE_ID 0x10005020 +#define SRS_SS3D_PARAMS 0x10005021 +#define SRS_SS3D_PARAMS_CTRL 0x10005022 +#define SRS_SS3D_PARAMS_FILTER 0x10005023 + +/* SRS ALSA CMD MASKS */ +#define SRS_CMD_UPLOAD 0x7FFF0000 +#define SRS_PARAM_INDEX_MASK 0x80000000 +#define SRS_PARAM_OFFSET_MASK 0x3FFF0000 +#define SRS_PARAM_VALUE_MASK 0x0000FFFF + +/* SRS TRUMEDIA start */ +#define SRS_ID_GLOBAL 0x00000001 +#define SRS_ID_WOWHD 0x00000002 +#define SRS_ID_CSHP 0x00000003 +#define SRS_ID_HPF 0x00000004 +#define SRS_ID_PEQ 0x00000005 +#define SRS_ID_HL 0x00000006 + +struct srs_trumedia_params_GLOBAL { + uint8_t v1; + uint8_t v2; + uint8_t v3; + uint8_t v4; + uint8_t v5; + uint8_t v6; + uint8_t v7; + uint8_t v8; +} __packed; + +struct srs_trumedia_params_WOWHD { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v4; + uint16_t v5; + uint16_t v6; + uint16_t v7; + uint16_t v8; + uint16_t v____A1; + uint32_t v9; + uint16_t v10; + uint16_t v11; + uint32_t v12[16]; +} __packed; + +struct srs_trumedia_params_CSHP { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v4; + uint16_t v5; + uint16_t v6; + uint16_t v____A1; + uint32_t v7; + uint16_t v8; + uint16_t v9; + uint32_t v10[16]; +} __packed; + +struct srs_trumedia_params_HPF { + uint32_t v1; + uint32_t v2[26]; +} __packed; + +struct srs_trumedia_params_PEQ { + uint32_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v4; + uint16_t v____A1; + uint32_t v5[26]; + uint32_t v6[26]; +} __packed; + +struct srs_trumedia_params_HL { + uint16_t v1; + uint16_t v2; + uint16_t v3; + uint16_t v____A1; + int32_t v4; + uint32_t v5; + uint16_t v6; + uint16_t v____A2; + uint32_t v7; +} __packed; + +struct srs_trumedia_params { + struct srs_trumedia_params_GLOBAL global; + struct srs_trumedia_params_WOWHD wowhd; + struct srs_trumedia_params_CSHP cshp; + struct srs_trumedia_params_HPF hpf; + struct srs_trumedia_params_PEQ peq; + struct srs_trumedia_params_HL hl; +} __packed; + +int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params); +/* SRS TruMedia end */ + +/* SRS Studio Sound 3D start */ +#define SRS_ID_SS3D_GLOBAL 0x00000001 +#define SRS_ID_SS3D_CTRL 0x00000002 +#define SRS_ID_SS3D_FILTER 0x00000003 + +struct srs_SS3D_params_GLOBAL { + uint8_t v1; + uint8_t v2; + uint8_t v3; + uint8_t v4; + uint8_t v5; + uint8_t v6; + uint8_t v7; + uint8_t v8; +} __packed; + +struct srs_SS3D_ctrl_params { + uint8_t v[236]; +} __packed; + +struct srs_SS3D_filter_params { + uint8_t v[28 + 2752]; +} __packed; + +struct srs_SS3D_params { + struct srs_SS3D_params_GLOBAL global; + struct srs_SS3D_ctrl_params ss3d; + struct srs_SS3D_filter_params ss3d_f; +} __packed; + +int srs_ss3d_open(int port_id, int srs_tech_id, void *srs_params); +/* SRS Studio Sound 3D end */ +#endif /*_APR_AUDIO_H_*/ diff --git a/include/sound/audio_cal_utils.h b/include/sound/audio_cal_utils.h new file mode 100644 index 000000000000..b28b3bdf4e83 --- /dev/null +++ b/include/sound/audio_cal_utils.h @@ -0,0 +1,102 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _AUDIO_CAL_UTILS_H +#define _AUDIO_CAL_UTILS_H + +#include +#include +#include +#include "audio_calibration.h" + +struct cal_data { + size_t size; + void *kvaddr; + phys_addr_t paddr; +}; + +struct mem_map_data { + size_t map_size; + int32_t q6map_handle; + int32_t ion_map_handle; + struct ion_client *ion_client; + struct ion_handle *ion_handle; +}; + +struct cal_block_data { + size_t client_info_size; + void *client_info; + void *cal_info; + struct list_head list; + struct cal_data cal_data; + struct mem_map_data map_data; + int32_t buffer_number; +}; + +struct cal_util_callbacks { + int (*map_cal) + (int32_t cal_type, struct cal_block_data *cal_block); + int (*unmap_cal) + (int32_t cal_type, struct cal_block_data *cal_block); + bool (*match_block) + (struct cal_block_data *cal_block, void *user_data); +}; + +struct cal_type_info { + struct audio_cal_reg reg; + struct cal_util_callbacks cal_util_callbacks; +}; + +struct cal_type_data { + struct cal_type_info info; + struct mutex lock; + struct list_head cal_blocks; +}; + + +/* to register & degregister with cal util driver */ +int cal_utils_create_cal_types(int num_cal_types, + struct cal_type_data **cal_type, + struct cal_type_info *info); +void cal_utils_destroy_cal_types(int num_cal_types, + struct cal_type_data **cal_type); + +/* common functions for callbacks */ +int cal_utils_alloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info); +int cal_utils_dealloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type); +int cal_utils_set_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info); + +/* use for SSR */ +void cal_utils_clear_cal_block_q6maps(int num_cal_types, + struct cal_type_data **cal_type); + + +/* common matching functions used to add blocks */ +bool cal_utils_match_buf_num(struct cal_block_data *cal_block, + void *user_data); + +/* common matching functions to find cal blocks */ +struct cal_block_data *cal_utils_get_only_cal_block( + struct cal_type_data *cal_type); + +/* Size of calibration specific data */ +size_t get_cal_info_size(int32_t cal_type); +size_t get_user_cal_type_size(int32_t cal_type); + +/* Version of the cal type*/ +int32_t cal_utils_get_cal_type_version(void *cal_type_data); +#endif diff --git a/include/sound/audio_calibration.h b/include/sound/audio_calibration.h new file mode 100644 index 000000000000..5decff913493 --- /dev/null +++ b/include/sound/audio_calibration.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _AUDIO_CALIBRATION_H +#define _AUDIO_CALIBRATION_H + +#include + +/* Used by driver in buffer_number field to notify client + * To update all blocks, for example: freeing all memory + */ +#define ALL_CAL_BLOCKS -1 + + +struct audio_cal_callbacks { + int (*alloc)(int32_t cal_type, size_t data_size, void *data); + int (*dealloc)(int32_t cal_type, size_t data_size, void *data); + int (*pre_cal)(int32_t cal_type, size_t data_size, void *data); + int (*set_cal)(int32_t cal_type, size_t data_size, void *data); + int (*get_cal)(int32_t cal_type, size_t data_size, void *data); + int (*post_cal)(int32_t cal_type, size_t data_size, void *data); +}; + +struct audio_cal_reg { + int32_t cal_type; + struct audio_cal_callbacks callbacks; +}; + +int audio_cal_register(int num_cal_types, struct audio_cal_reg *reg_data); +int audio_cal_deregister(int num_cal_types, struct audio_cal_reg *reg_data); + +#endif diff --git a/include/sound/audio_slimslave.h b/include/sound/audio_slimslave.h new file mode 100644 index 000000000000..316a5573f5b4 --- /dev/null +++ b/include/sound/audio_slimslave.h @@ -0,0 +1,18 @@ +#ifndef __AUDIO_SLIMSLAVE_H__ +#define __AUDIO_SLIMSLAVE_H__ + +#include +#include + +#define AUDIO_SLIMSLAVE_IOCTL_NAME "audio_slimslave" +#define AUDIO_SLIMSLAVE_MAGIC 'S' + +#define AUDIO_SLIMSLAVE_IOCTL_UNVOTE _IO(AUDIO_SLIMSLAVE_MAGIC, 0x00) +#define AUDIO_SLIMSLAVE_IOCTL_VOTE _IO(AUDIO_SLIMSLAVE_MAGIC, 0x01) + +enum { + AUDIO_SLIMSLAVE_UNVOTE, + AUDIO_SLIMSLAVE_VOTE +}; + +#endif diff --git a/include/sound/cpe_cmi.h b/include/sound/cpe_cmi.h new file mode 100644 index 000000000000..cbf83e7a7e91 --- /dev/null +++ b/include/sound/cpe_cmi.h @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2014-2016, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CPE_CMI_H__ +#define __CPE_CMI_H__ + +#include + +#define CPE_AFE_PORT_1_TX 1 +#define CPE_AFE_PORT_3_TX 3 +#define CPE_AFE_PORT_ID_2_OUT 0x02 +#define CMI_INBAND_MESSAGE_SIZE 127 + +/* + * Multiple mad types can be supported at once. + * these values can be OR'ed to form the set of + * supported mad types + */ +#define MAD_TYPE_AUDIO (1 << 0) +#define MAD_TYPE_BEACON (1 << 1) +#define MAD_TYPE_ULTRASND (1 << 2) + +/* Core service command opcodes */ +#define CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC (0x3001) +#define CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC (0x3002) +#define CPE_CORE_SVC_CMD_SHARED_MEM_DEALLOC (0x3003) +#define CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ (0x3004) +#define CPE_CORE_SVC_EVENT_SYSTEM_BOOT (0x3005) +/* core service command opcodes for WCD9335 */ +#define CPE_CORE_SVC_CMD_CFG_CLK_PLAN (0x3006) +#define CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST (0x3007) + +#define CPE_BOOT_SUCCESS 0x00 +#define CPE_BOOT_FAILED 0x01 + +#define CPE_CORE_VERSION_SYSTEM_BOOT_EVENT 0x01 + +/* LSM Service command opcodes */ +#define CPE_LSM_SESSION_CMD_OPEN_TX (0x2000) +#define CPE_LSM_SESSION_CMD_SET_PARAMS (0x2001) +#define CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL (0x2002) +#define CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL (0x2003) +#define CPE_LSM_SESSION_CMD_START (0x2004) +#define CPE_LSM_SESSION_CMD_STOP (0x2005) +#define CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2 (0x2006) +#define CPE_LSM_SESSION_CMD_CLOSE_TX (0x2007) +#define CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC (0x2008) +#define CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC (0x2009) +#define CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC (0x200A) +#define CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG (0x200f) +#define CPE_LSM_SESSION_CMD_OPEN_TX_V2 (0x200D) +#define CPE_LSM_SESSION_CMD_SET_PARAMS_V2 (0x200E) + +/* LSM Service module and param IDs */ +#define CPE_LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00) +#define CPE_LSM_MODULE_ID_VOICE_WAKEUP_V2 (0x00012C0D) +#define CPE_LSM_MODULE_FRAMEWORK (0x00012C0E) + +#define CPE_LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01) +#define CPE_LSM_PARAM_ID_OPERATION_MODE (0x00012C02) +#define CPE_LSM_PARAM_ID_GAIN (0x00012C03) +#define CPE_LSM_PARAM_ID_CONNECT_TO_PORT (0x00012C04) +#define CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS (0x00012C07) + +/* LSM LAB command opcodes */ +#define CPE_LSM_SESSION_CMD_EOB 0x0000200B +#define CPE_LSM_MODULE_ID_LAB 0x00012C08 +/* used for enable/disable lab*/ +#define CPE_LSM_PARAM_ID_LAB_ENABLE 0x00012C09 +/* used for T in LAB config DSP internal buffer*/ +#define CPE_LSM_PARAM_ID_LAB_CONFIG 0x00012C0A +#define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL (0x00012C14) +#define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL (0x00012C15) +#define CPE_LSM_PARAM_ID_MEDIA_FMT (0x00012C1E) + +/* AFE Service command opcodes */ +#define CPE_AFE_PORT_CMD_START (0x1001) +#define CPE_AFE_PORT_CMD_STOP (0x1002) +#define CPE_AFE_PORT_CMD_SUSPEND (0x1003) +#define CPE_AFE_PORT_CMD_RESUME (0x1004) +#define CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC (0x1005) +#define CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC (0x1006) +#define CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC (0x1007) +#define CPE_AFE_PORT_CMD_GENERIC_CONFIG (0x1008) +#define CPE_AFE_SVC_CMD_LAB_MODE (0x1009) + +/* AFE Service module and param IDs */ +#define CPE_AFE_CMD_SET_PARAM (0x1000) +#define CPE_AFE_MODULE_ID_SW_MAD (0x0001022D) +#define CPE_AFE_PARAM_ID_SW_MAD_CFG (0x0001022E) +#define CPE_AFE_PARAM_ID_SVM_MODEL (0x0001022F) + +#define CPE_AFE_MODULE_HW_MAD (0x00010230) +#define CPE_AFE_PARAM_ID_HW_MAD_CTL (0x00010232) +#define CPE_AFE_PARAM_ID_HW_MAD_CFG (0x00010231) + +#define CPE_AFE_MODULE_AUDIO_DEV_INTERFACE (0x0001020C) +#define CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG (0x00010253) + +#define CPE_CMI_BASIC_RSP_OPCODE (0x0001) +#define CPE_HDR_MAX_PLD_SIZE (0x7F) + +#define CMI_OBM_FLAG_IN_BAND 0 +#define CMI_OBM_FLAG_OUT_BAND 1 + +#define CMI_SHMEM_ALLOC_FAILED 0xff + +/* + * Future Service ID's can be added one line + * before the CMI_CPE_SERVICE_ID_MAX + */ +enum { + CMI_CPE_SERVICE_ID_MIN = 0, + CMI_CPE_CORE_SERVICE_ID, + CMI_CPE_AFE_SERVICE_ID, + CMI_CPE_LSM_SERVICE_ID, + CMI_CPE_SERVICE_ID_MAX, +}; + +#define CPE_LSM_SESSION_ID_MAX 2 + +#define IS_VALID_SESSION_ID(s_id) \ + (s_id <= CPE_LSM_SESSION_ID_MAX) + +#define IS_VALID_SERVICE_ID(s_id) \ + (s_id > CMI_CPE_SERVICE_ID_MIN && \ + s_id < CMI_CPE_SERVICE_ID_MAX) + +#define IS_VALID_PLD_SIZE(p_size) \ + (p_size <= CPE_HDR_MAX_PLD_SIZE) + +#define CMI_HDR_SET_OPCODE(hdr, cmd) (hdr->opcode = cmd) + + +#define CMI_HDR_SET(hdr_info, mask, shift, value) \ + (hdr_info = (((hdr_info) & ~(mask)) | \ + ((value << shift) & mask))) + +#define SVC_ID_SHIFT 4 +#define SVC_ID_MASK (0x07 << SVC_ID_SHIFT) + +#define SESSION_ID_SHIFT 0 +#define SESSION_ID_MASK (0x0F << SESSION_ID_SHIFT) + +#define PAYLD_SIZE_SHIFT 0 +#define PAYLD_SIZE_MASK (0x7F << PAYLD_SIZE_SHIFT) + +#define OBM_FLAG_SHIFT 7 +#define OBM_FLAG_MASK (1 << OBM_FLAG_SHIFT) + +#define VERSION_SHIFT 7 +#define VERSION_MASK (1 << VERSION_SHIFT) + +#define CMI_HDR_SET_SERVICE(hdr, s_id) \ + CMI_HDR_SET(hdr->hdr_info, SVC_ID_MASK,\ + SVC_ID_SHIFT, s_id) +#define CMI_HDR_GET_SERVICE(hdr) \ + ((hdr->hdr_info >> SVC_ID_SHIFT) & \ + (SVC_ID_MASK >> SVC_ID_SHIFT)) + + +#define CMI_HDR_SET_SESSION(hdr, s_id) \ + CMI_HDR_SET(hdr->hdr_info, SESSION_ID_MASK,\ + SESSION_ID_SHIFT, s_id) + +#define CMI_HDR_GET_SESSION_ID(hdr) \ + ((hdr->hdr_info >> SESSION_ID_SHIFT) & \ + (SESSION_ID_MASK >> SESSION_ID_SHIFT)) + +#define CMI_GET_HEADER(msg) ((struct cmi_hdr *)(msg)) +#define CMI_GET_PAYLOAD(msg) ((void *)(CMI_GET_HEADER(msg) + 1)) +#define CMI_GET_OPCODE(msg) (CMI_GET_HEADER(msg)->opcode) + +#define CMI_HDR_SET_VERSION(hdr, ver) \ + CMI_HDR_SET(hdr->hdr_info, VERSION_MASK, \ + VERSION_SHIFT, ver) + +#define CMI_HDR_SET_PAYLOAD_SIZE(hdr, p_size) \ + CMI_HDR_SET(hdr->pld_info, PAYLD_SIZE_MASK, \ + PAYLD_SIZE_SHIFT, p_size) + +#define CMI_HDR_GET_PAYLOAD_SIZE(hdr) \ + ((hdr->pld_info >> PAYLD_SIZE_SHIFT) & \ + (PAYLD_SIZE_MASK >> PAYLD_SIZE_SHIFT)) + +#define CMI_HDR_SET_OBM(hdr, obm_flag) \ + CMI_HDR_SET(hdr->pld_info, OBM_FLAG_MASK, \ + OBM_FLAG_SHIFT, obm_flag) + +#define CMI_HDR_GET_OBM_FLAG(hdr) \ + ((hdr->pld_info >> OBM_FLAG_SHIFT) & \ + (OBM_FLAG_MASK >> OBM_FLAG_SHIFT)) + +struct cmi_hdr { + /* + * bits 0:3 is session id + * bits 4:6 is service id + * bit 7 is the version flag + */ + u8 hdr_info; + + /* + * bits 0:6 is payload size in case of in-band message + * bits 0:6 is size (OBM message size) + * bit 7 is the OBM flag + */ + u8 pld_info; + + /* 16 bit command opcode */ + u16 opcode; +} __packed; + +union cpe_addr { + u64 msw_lsw; + void *kvaddr; +} __packed; + +struct cmi_obm { + u32 version; + u32 size; + union cpe_addr data_ptr; + u32 mem_handle; +} __packed; + +struct cmi_obm_msg { + struct cmi_hdr hdr; + struct cmi_obm pld; +} __packed; + +struct cmi_core_svc_event_system_boot { + u8 status; + u8 version; + u16 sfr_buff_size; + u32 sfr_buff_address; +} __packed; + +struct cmi_core_svc_cmd_shared_mem_alloc { + u32 size; +} __packed; + +struct cmi_core_svc_cmdrsp_shared_mem_alloc { + u32 addr; +} __packed; + +struct cmi_core_svc_cmd_clk_freq_request { + u32 clk_freq; +} __packed; + +struct cmi_msg_transport { + u32 size; + u32 addr; +} __packed; + +struct cmi_basic_rsp_result { + u8 status; +} __packed; + +struct cpe_lsm_cmd_open_tx { + struct cmi_hdr hdr; + u16 app_id; + u16 reserved; + u32 sampling_rate; +} __packed; + +struct cpe_lsm_cmd_open_tx_v2 { + struct cmi_hdr hdr; + u32 topology_id; +} __packed; + +struct cpe_cmd_shmem_alloc { + struct cmi_hdr hdr; + u32 size; +} __packed; + +struct cpe_cmdrsp_shmem_alloc { + struct cmi_hdr hdr; + u32 addr; +} __packed; + +struct cpe_cmd_shmem_dealloc { + struct cmi_hdr hdr; + u32 addr; +} __packed; + +struct cpe_lsm_event_detect_v2 { + struct cmi_hdr hdr; + u8 detection_status; + u8 size; + u8 payload[0]; +} __packed; + +struct cpe_lsm_psize_res { + u16 param_size; + u16 reserved; +} __packed; + +union cpe_lsm_param_size { + u32 param_size; + struct cpe_lsm_psize_res sr; +} __packed; + +struct cpe_param_data { + u32 module_id; + u32 param_id; + union cpe_lsm_param_size p_size; +} __packed; + +struct cpe_lsm_param_epd_thres { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u32 epd_begin; + u32 epd_end; +} __packed; + +struct cpe_lsm_param_gain { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u16 gain; + u16 reserved; +} __packed; + +struct cpe_afe_hw_mad_ctrl { + struct cpe_param_data param; + u32 minor_version; + u16 mad_type; + u16 mad_enable; +} __packed; + +struct cpe_afe_port_cfg { + struct cpe_param_data param; + u32 minor_version; + u16 bit_width; + u16 num_channels; + u32 sample_rate; +} __packed; + +struct cpe_afe_cmd_port_cfg { + struct cmi_hdr hdr; + u8 bit_width; + u8 num_channels; + u16 buffer_size; + u32 sample_rate; +} __packed; + +struct cpe_afe_params { + struct cmi_hdr hdr; + struct cpe_afe_hw_mad_ctrl hw_mad_ctrl; + struct cpe_afe_port_cfg port_cfg; +} __packed; + +struct cpe_afe_svc_cmd_mode { + struct cmi_hdr hdr; + u8 mode; +} __packed; + +struct cpe_lsm_param_opmode { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u16 mode; + u16 reserved; +} __packed; + +struct cpe_lsm_param_connectport { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u16 afe_port_id; + u16 reserved; +} __packed; + +/* + * This cannot be sent to CPE as is, + * need to append the conf_levels dynamically + */ +struct cpe_lsm_conf_level { + struct cmi_hdr hdr; + struct cpe_param_data param; + u8 num_active_models; +} __packed; + +struct cpe_lsm_output_format_cfg { + struct cmi_hdr hdr; + u8 format; + u8 packing; + u8 data_path_events; +} __packed; + +struct cpe_lsm_lab_enable { + struct cpe_param_data param; + u16 enable; + u16 reserved; +} __packed; + +struct cpe_lsm_control_lab { + struct cmi_hdr hdr; + struct cpe_lsm_lab_enable lab_enable; +} __packed; + +struct cpe_lsm_lab_config { + struct cpe_param_data param; + u32 minor_ver; + u32 latency; +} __packed; + +struct cpe_lsm_lab_latency_config { + struct cmi_hdr hdr; + struct cpe_lsm_lab_config latency_cfg; +} __packed; + +struct cpe_lsm_media_fmt_param { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u32 sample_rate; + u16 num_channels; + u16 bit_width; +} __packed; + + +#define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\ + sizeof(struct cpe_lsm_lab_latency_config) - \ + sizeof(struct cmi_hdr)) +#define PARAM_SIZE_LSM_LATENCY_SIZE (\ + sizeof(struct cpe_lsm_lab_config) - \ + sizeof(struct cpe_param_data)) +#define CPE_PARAM_SIZE_LSM_LAB_CONTROL (\ + sizeof(struct cpe_lsm_control_lab) - \ + sizeof(struct cmi_hdr)) +#define PARAM_SIZE_LSM_CONTROL_SIZE (sizeof(struct cpe_lsm_lab_enable) - \ + sizeof(struct cpe_param_data)) +#define PARAM_SIZE_AFE_HW_MAD_CTRL (sizeof(struct cpe_afe_hw_mad_ctrl) - \ + sizeof(struct cpe_param_data)) +#define PARAM_SIZE_AFE_PORT_CFG (sizeof(struct cpe_afe_port_cfg) - \ + sizeof(struct cpe_param_data)) +#define CPE_AFE_PARAM_PAYLOAD_SIZE (sizeof(struct cpe_afe_params) - \ + sizeof(struct cmi_hdr)) + +#define OPEN_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx) - \ + sizeof(struct cmi_hdr)) +#define OPEN_V2_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx_v2) - \ + sizeof(struct cmi_hdr)) +#define SHMEM_ALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_alloc) - \ + sizeof(struct cmi_hdr)) + +#define SHMEM_DEALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_dealloc) - \ + sizeof(struct cmi_hdr)) +#define OUT_FMT_CFG_CMD_PAYLOAD_SIZE ( \ + sizeof(struct cpe_lsm_output_format_cfg) - \ + sizeof(struct cmi_hdr)) + +#define CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE \ + (sizeof(struct cpe_afe_cmd_port_cfg) - \ + sizeof(struct cmi_hdr)) + +#define CPE_AFE_CMD_MODE_PAYLOAD_SIZE \ + (sizeof(struct cpe_afe_svc_cmd_mode) - \ + sizeof(struct cmi_hdr)) +#define CPE_CMD_EPD_THRES_PLD_SIZE (sizeof(struct cpe_lsm_param_epd_thres) - \ + sizeof(struct cmi_hdr)) +#define CPE_EPD_THRES_PARAM_SIZE ((CPE_CMD_EPD_THRES_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#define CPE_CMD_OPMODE_PLD_SIZE (sizeof(struct cpe_lsm_param_opmode) - \ + sizeof(struct cmi_hdr)) +#define CPE_OPMODE_PARAM_SIZE ((CPE_CMD_OPMODE_PLD_SIZE) -\ + sizeof(struct cpe_param_data)) +#define CPE_CMD_CONNECTPORT_PLD_SIZE \ + (sizeof(struct cpe_lsm_param_connectport) - \ + sizeof(struct cmi_hdr)) +#define CPE_CONNECTPORT_PARAM_SIZE ((CPE_CMD_CONNECTPORT_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#define CPE_CMD_GAIN_PLD_SIZE (sizeof(struct cpe_lsm_param_gain) - \ + sizeof(struct cmi_hdr)) +#define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \ + sizeof(struct cmi_hdr)) +#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#endif /* __CPE_CMI_H__ */ diff --git a/include/sound/cpe_core.h b/include/sound/cpe_core.h new file mode 100644 index 000000000000..f4af56240322 --- /dev/null +++ b/include/sound/cpe_core.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2013-2017, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CPE_CORE_H__ +#define __CPE_CORE_H__ + +#include +#include +#include +#include + +enum { + CMD_INIT_STATE = 0, + CMD_SENT, + CMD_RESP_RCVD, +}; + +enum wcd_cpe_event { + WCD_CPE_PRE_ENABLE = 1, + WCD_CPE_POST_ENABLE, + WCD_CPE_PRE_DISABLE, + WCD_CPE_POST_DISABLE, +}; + +struct wcd_cpe_afe_port_cfg { + u8 port_id; + u16 bit_width; + u16 num_channels; + u32 sample_rate; +}; + +struct lsm_out_fmt_cfg { + u8 format; + u8 pack_mode; + u8 data_path_events; + u8 transfer_mode; +}; + +struct lsm_hw_params { + u32 sample_rate; + u16 num_chs; + u16 bit_width; +}; + +struct cpe_lsm_session { + /* sound model related */ + void *snd_model_data; + u8 *conf_levels; + void *cmi_reg_handle; + + /* Clients private data */ + void *priv_d; + + void (*event_cb)(void *priv_data, + u8 detect_status, + u8 size, u8 *payload); + + struct completion cmd_comp; + struct wcd_cpe_afe_port_cfg afe_port_cfg; + struct wcd_cpe_afe_port_cfg afe_out_port_cfg; + struct mutex lsm_lock; + + u32 snd_model_size; + u32 lsm_mem_handle; + u16 cmd_err_code; + u8 id; + u8 num_confidence_levels; + u16 afe_out_port_id; + struct task_struct *lsm_lab_thread; + bool started; + + u32 lab_enable; + struct lsm_out_fmt_cfg out_fmt_cfg; + + bool is_topology_used; +}; + +struct wcd_cpe_afe_ops { + int (*afe_set_params)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg, + bool afe_mad_ctl); + + int (*afe_port_start)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_stop)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_suspend)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_resume)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_cmd_cfg)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); +}; + +struct wcd_cpe_lsm_ops { + + struct cpe_lsm_session *(*lsm_alloc_session) + (void *core_handle, void *lsm_priv_d, + void (*event_cb)(void *priv_data, + u8 detect_status, + u8 size, u8 *payload)); + + int (*lsm_dealloc_session) + (void *core_handle, struct cpe_lsm_session *); + + int (*lsm_open_tx)(void *core_handle, + struct cpe_lsm_session *, u16, u16); + + int (*lsm_close_tx)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_shmem_alloc)(void *core_handle, + struct cpe_lsm_session *, u32 size); + + int (*lsm_shmem_dealloc)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_register_snd_model)(void *core_handle, + struct cpe_lsm_session *, + enum lsm_detection_mode, bool); + + int (*lsm_deregister_snd_model)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_get_afe_out_port_id)(void *core_handle, + struct cpe_lsm_session *session); + + int (*lsm_start)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_stop)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_lab_control)(void *core_handle, + struct cpe_lsm_session *session, + bool enable); + + int (*lab_ch_setup)(void *core_handle, + struct cpe_lsm_session *session, + enum wcd_cpe_event event); + + int (*lsm_set_data)(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure); + int (*lsm_set_fmt_cfg)(void *core_handle, + struct cpe_lsm_session *session); + int (*lsm_set_one_param)(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_params_info *p_info, + void *data, enum LSM_PARAM_TYPE param_type); + void (*lsm_get_snd_model_offset) + (void *core_handle, struct cpe_lsm_session *, + size_t *offset); + int (*lsm_set_media_fmt_params)(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param); + int (*lsm_set_port)(void *core_handle, + struct cpe_lsm_session *session, void *data); +}; + +int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops); +int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops); +void *wcd_cpe_get_core_handle(struct snd_soc_codec *codec); +#endif diff --git a/include/sound/cpe_err.h b/include/sound/cpe_err.h new file mode 100644 index 000000000000..d35580326bac --- /dev/null +++ b/include/sound/cpe_err.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CPE_ERR__ +#define __CPE_ERR__ + +#include + +/* ERROR CODES */ +/* Success. The operation completed with no errors. */ +#define CPE_EOK 0x00000000 +/* General failure. */ +#define CPE_EFAILED 0x00000001 +/* Bad operation parameter. */ +#define CPE_EBADPARAM 0x00000002 +/* Unsupported routine or operation. */ +#define CPE_EUNSUPPORTED 0x00000003 +/* Unsupported version. */ +#define CPE_EVERSION 0x00000004 +/* Unexpected problem encountered. */ +#define CPE_EUNEXPECTED 0x00000005 +/* Unhandled problem occurred. */ +#define CPE_EPANIC 0x00000006 +/* Unable to allocate resource. */ +#define CPE_ENORESOURCE 0x00000007 +/* Invalid handle. */ +#define CPE_EHANDLE 0x00000008 +/* Operation is already processed. */ +#define CPE_EALREADY 0x00000009 +/* Operation is not ready to be processed. */ +#define CPE_ENOTREADY 0x0000000A +/* Operation is pending completion. */ +#define CPE_EPENDING 0x0000000B +/* Operation could not be accepted or processed. */ +#define CPE_EBUSY 0x0000000C +/* Operation aborted due to an error. */ +#define CPE_EABORTED 0x0000000D +/* Operation preempted by a higher priority. */ +#define CPE_EPREEMPTED 0x0000000E +/* Operation requests intervention to complete. */ +#define CPE_ECONTINUE 0x0000000F +/* Operation requests immediate intervention to complete. */ +#define CPE_EIMMEDIATE 0x00000010 +/* Operation is not implemented. */ +#define CPE_ENOTIMPL 0x00000011 +/* Operation needs more data or resources. */ +#define CPE_ENEEDMORE 0x00000012 +/* Operation does not have memory. */ +#define CPE_ENOMEMORY 0x00000014 +/* Item does not exist. */ +#define CPE_ENOTEXIST 0x00000015 +/* Operation is finished. */ +#define CPE_ETERMINATED 0x00000016 +/* Max count for adsp error code sent to HLOS*/ +#define CPE_ERR_MAX (CPE_ETERMINATED + 1) + + +/* ERROR STRING */ +/* Success. The operation completed with no errors. */ +#define CPE_EOK_STR "CPE_EOK" +/* General failure. */ +#define CPE_EFAILED_STR "CPE_EFAILED" +/* Bad operation parameter. */ +#define CPE_EBADPARAM_STR "CPE_EBADPARAM" +/* Unsupported routine or operation. */ +#define CPE_EUNSUPPORTED_STR "CPE_EUNSUPPORTED" +/* Unsupported version. */ +#define CPE_EVERSION_STR "CPE_EVERSION" +/* Unexpected problem encountered. */ +#define CPE_EUNEXPECTED_STR "CPE_EUNEXPECTED" +/* Unhandled problem occurred. */ +#define CPE_EPANIC_STR "CPE_EPANIC" +/* Unable to allocate resource. */ +#define CPE_ENORESOURCE_STR "CPE_ENORESOURCE" +/* Invalid handle. */ +#define CPE_EHANDLE_STR "CPE_EHANDLE" +/* Operation is already processed. */ +#define CPE_EALREADY_STR "CPE_EALREADY" +/* Operation is not ready to be processed. */ +#define CPE_ENOTREADY_STR "CPE_ENOTREADY" +/* Operation is pending completion. */ +#define CPE_EPENDING_STR "CPE_EPENDING" +/* Operation could not be accepted or processed. */ +#define CPE_EBUSY_STR "CPE_EBUSY" +/* Operation aborted due to an error. */ +#define CPE_EABORTED_STR "CPE_EABORTED" +/* Operation preempted by a higher priority. */ +#define CPE_EPREEMPTED_STR "CPE_EPREEMPTED" +/* Operation requests intervention to complete. */ +#define CPE_ECONTINUE_STR "CPE_ECONTINUE" +/* Operation requests immediate intervention to complete. */ +#define CPE_EIMMEDIATE_STR "CPE_EIMMEDIATE" +/* Operation is not implemented. */ +#define CPE_ENOTIMPL_STR "CPE_ENOTIMPL" +/* Operation needs more data or resources. */ +#define CPE_ENEEDMORE_STR "CPE_ENEEDMORE" +/* Operation does not have memory. */ +#define CPE_ENOMEMORY_STR "CPE_ENOMEMORY" +/* Item does not exist. */ +#define CPE_ENOTEXIST_STR "CPE_ENOTEXIST" +/* Operation is finished. */ +#define CPE_ETERMINATED_STR "CPE_ETERMINATED" +/* Unexpected error code. */ +#define CPE_ERR_MAX_STR "CPE_ERR_MAX" + + +struct cpe_err_code { + int lnx_err_code; + char *cpe_err_str; +}; + + +static struct cpe_err_code cpe_err_code_info[CPE_ERR_MAX+1] = { + { 0, CPE_EOK_STR}, + { -ENOTRECOVERABLE, CPE_EFAILED_STR}, + { -EINVAL, CPE_EBADPARAM_STR}, + { -EOPNOTSUPP, CPE_EUNSUPPORTED_STR}, + { -ENOPROTOOPT, CPE_EVERSION_STR}, + { -ENOTRECOVERABLE, CPE_EUNEXPECTED_STR}, + { -ENOTRECOVERABLE, CPE_EPANIC_STR}, + { -ENOSPC, CPE_ENORESOURCE_STR}, + { -EBADR, CPE_EHANDLE_STR}, + { -EALREADY, CPE_EALREADY_STR}, + { -EPERM, CPE_ENOTREADY_STR}, + { -EINPROGRESS, CPE_EPENDING_STR}, + { -EBUSY, CPE_EBUSY_STR}, + { -ECANCELED, CPE_EABORTED_STR}, + { -EAGAIN, CPE_EPREEMPTED_STR}, + { -EAGAIN, CPE_ECONTINUE_STR}, + { -EAGAIN, CPE_EIMMEDIATE_STR}, + { -EAGAIN, CPE_ENOTIMPL_STR}, + { -ENODATA, CPE_ENEEDMORE_STR}, + { -EADV, CPE_ERR_MAX_STR}, + { -ENOMEM, CPE_ENOMEMORY_STR}, + { -ENODEV, CPE_ENOTEXIST_STR}, + { -EADV, CPE_ETERMINATED_STR}, + { -EADV, CPE_ERR_MAX_STR}, +}; + +static inline int cpe_err_get_lnx_err_code(u32 cpe_error) +{ + if (cpe_error > CPE_ERR_MAX) + return cpe_err_code_info[CPE_ERR_MAX].lnx_err_code; + else + return cpe_err_code_info[cpe_error].lnx_err_code; +} + +static inline char *cpe_err_get_err_str(u32 cpe_error) +{ + if (cpe_error > CPE_ERR_MAX) + return cpe_err_code_info[CPE_ERR_MAX].cpe_err_str; + else + return cpe_err_code_info[cpe_error].cpe_err_str; +} + +#endif diff --git a/include/sound/msm-audio-effects-q6-v2.h b/include/sound/msm-audio-effects-q6-v2.h new file mode 100644 index 000000000000..6bc2338bcf55 --- /dev/null +++ b/include/sound/msm-audio-effects-q6-v2.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_AUDIO_EFFECTS_H +#define _MSM_AUDIO_EFFECTS_H + +#include + +#define MAX_PP_PARAMS_SZ 128 + +bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module, + int topology); + +int msm_audio_effects_enable_extn(struct audio_client *ac, + struct msm_nt_eff_all_config *effects, + bool flag); + +int msm_audio_effects_reverb_handler(struct audio_client *ac, + struct reverb_params *reverb, + long *values); + +int msm_audio_effects_bass_boost_handler(struct audio_client *ac, + struct bass_boost_params *bass_boost, + long *values); + +int msm_audio_effects_pbe_handler(struct audio_client *ac, + struct pbe_params *pbe, + long *values); + +int msm_audio_effects_virtualizer_handler(struct audio_client *ac, + struct virtualizer_params *virtualizer, + long *values); + +int msm_audio_effects_popless_eq_handler(struct audio_client *ac, + struct eq_params *eq, + long *values); + +int msm_audio_effects_volume_handler(struct audio_client *ac, + struct soft_volume_params *vol, + long *values); + +int msm_audio_effects_volume_handler_v2(struct audio_client *ac, + struct soft_volume_params *vol, + long *values, int instance); +#endif /*_MSM_AUDIO_EFFECTS_H*/ diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h new file mode 100644 index 000000000000..b1d76bf73f51 --- /dev/null +++ b/include/sound/msm-dai-q6-v2.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_DAI_Q6_PDATA_H__ + +#define __MSM_DAI_Q6_PDATA_H__ + +#define MSM_MI2S_SD0 (1 << 0) +#define MSM_MI2S_SD1 (1 << 1) +#define MSM_MI2S_SD2 (1 << 2) +#define MSM_MI2S_SD3 (1 << 3) +#define MSM_MI2S_CAP_RX 0 +#define MSM_MI2S_CAP_TX 1 + +#define MSM_PRIM_MI2S 0 +#define MSM_SEC_MI2S 1 +#define MSM_TERT_MI2S 2 +#define MSM_QUAT_MI2S 3 +#define MSM_SEC_MI2S_SD1 4 +#define MSM_QUIN_MI2S 5 +#define MSM_SENARY_MI2S 6 +#define MSM_INT0_MI2S 7 +#define MSM_INT1_MI2S 8 +#define MSM_INT2_MI2S 9 +#define MSM_INT3_MI2S 10 +#define MSM_INT4_MI2S 11 +#define MSM_INT5_MI2S 12 +#define MSM_INT6_MI2S 13 +#define MSM_MI2S_MIN MSM_PRIM_MI2S +#define MSM_MI2S_MAX MSM_INT6_MI2S + +struct msm_dai_auxpcm_config { + u16 mode; + u16 sync; + u16 frame; + u16 quant; + u16 num_slots; + u16 *slot_mapping; + u16 data; + u32 pcm_clk_rate; +}; + +struct msm_dai_auxpcm_pdata { + struct msm_dai_auxpcm_config mode_8k; + struct msm_dai_auxpcm_config mode_16k; +}; + +struct msm_mi2s_pdata { + u16 rx_sd_lines; + u16 tx_sd_lines; + u16 intf_id; +}; + +struct msm_i2s_data { + u32 capability; /* RX or TX */ + u16 sd_lines; +}; + +struct msm_dai_tdm_group_config { + u16 group_id; + u16 num_ports; + u16 *port_id; + u32 clk_rate; +}; + +struct msm_dai_tdm_config { + u16 sync_mode; + u16 sync_src; + u16 data_out; + u16 invert_sync; + u16 data_delay; + u32 data_align; + u16 header_start_offset; + u16 header_width; + u16 header_num_frame_repeat; +}; + +struct msm_dai_tdm_pdata { + struct msm_dai_tdm_group_config group_config; + struct msm_dai_tdm_config config; +}; + +#endif diff --git a/include/sound/msm-dts-eagle.h b/include/sound/msm-dts-eagle.h new file mode 100644 index 000000000000..2ef01136b7ce --- /dev/null +++ b/include/sound/msm-dts-eagle.h @@ -0,0 +1,148 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_DTS_EAGLE_H__ +#define __MSM_DTS_EAGLE_H__ + +#include +#include +#include +#include + +#ifdef CONFIG_COMPAT +enum { + DTS_EAGLE_IOCTL_GET_CACHE_SIZE32 = _IOR(0xF2, 0, __s32), + DTS_EAGLE_IOCTL_SET_CACHE_SIZE32 = _IOW(0xF2, 1, __s32), + DTS_EAGLE_IOCTL_GET_PARAM32 = _IOR(0xF2, 2, compat_uptr_t), + DTS_EAGLE_IOCTL_SET_PARAM32 = _IOW(0xF2, 3, compat_uptr_t), + DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32 = + _IOW(0xF2, 4, compat_uptr_t), + DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32 = + _IOW(0xF2, 5, compat_uptr_t), + DTS_EAGLE_IOCTL_GET_LICENSE32 = + _IOR(0xF2, 6, compat_uptr_t), + DTS_EAGLE_IOCTL_SET_LICENSE32 = + _IOW(0xF2, 7, compat_uptr_t), + DTS_EAGLE_IOCTL_SEND_LICENSE32 = _IOW(0xF2, 8, __s32), + DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32 = _IOW(0xF2, 9, + compat_uptr_t), +}; +#endif + +#ifdef CONFIG_DTS_EAGLE +void msm_dts_ion_memmap(struct param_outband *po_); +int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module); +int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable); +void msm_dts_eagle_add_controls(struct snd_soc_platform *platform); +int msm_dts_eagle_set_stream_gain(struct audio_client *ac, + int lgain, int rgain); +int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf, + bool for_pre, bool get, struct audio_client *ac, + struct param_outband *po); +int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf, + bool for_pre, bool get); +int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg); +int msm_dts_eagle_is_hpx_on(void); +int msm_dts_eagle_init_pre(struct audio_client *ac); +int msm_dts_eagle_deinit_pre(struct audio_client *ac); +int msm_dts_eagle_init_post(int port_id, int copp_id); +int msm_dts_eagle_deinit_post(int port_id, int topology); +int msm_dts_eagle_init_master_module(struct audio_client *ac); +int msm_dts_eagle_deinit_master_module(struct audio_client *ac); +int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime); +void msm_dts_eagle_pcm_free(struct snd_pcm *pcm); +int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg); +#else +static inline void msm_dts_ion_memmap(struct param_outband *po_) +{ + pr_debug("%s\n", __func__); +} +static inline int msm_dts_eagle_enable_asm(struct audio_client *ac, + u32 enable, int module) +{ + return 0; +} +static inline int msm_dts_eagle_enable_adm(int port_id, int copp_idx, + u32 enable) +{ + return 0; +} +static inline void msm_dts_eagle_add_controls(struct snd_soc_platform *platform) +{ +} +static inline int msm_dts_eagle_set_stream_gain(struct audio_client *ac, + int lgain, int rgain) +{ + pr_debug("%s\n", __func__); + return 0; +} +static inline int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, + char *buf, bool for_pre, bool get, + struct audio_client *ac, + struct param_outband *po) +{ + return 0; +} +static inline int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, + char *buf, bool for_pre, bool get) +{ + return 0; +} +static inline int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg) +{ + return -EPERM; +} +static inline int msm_dts_eagle_is_hpx_on(void) +{ + return 0; +} +static inline int msm_dts_eagle_init_pre(struct audio_client *ac) +{ + return 0; +} +static inline int msm_dts_eagle_deinit_pre(struct audio_client *ac) +{ + return 0; +} +static inline int msm_dts_eagle_init_post(int port_id, int coppid) +{ + return 0; +} +static inline int msm_dts_eagle_deinit_post(int port_id, int topology) +{ + return 0; +} +static inline int msm_dts_eagle_init_master_module(struct audio_client *ac) +{ + return 0; +} +static inline int msm_dts_eagle_deinit_master_module(struct audio_client *ac) +{ + return 0; +} +static inline int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime) +{ + pr_debug("%s\n", __func__); + return 0; +} +static inline void msm_dts_eagle_pcm_free(struct snd_pcm *pcm) +{ + pr_debug("%s\n", __func__); +} +static inline int msm_dts_eagle_compat_ioctl(unsigned int cmd, + unsigned long arg) +{ + return 0; +} +#endif + +#endif diff --git a/include/sound/msm-slim-dma.h b/include/sound/msm-slim-dma.h new file mode 100644 index 000000000000..daf394bffaff --- /dev/null +++ b/include/sound/msm-slim-dma.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MSM_SLIMBUS_DMA_H +#define _MSM_SLIMBUS_DMA_H + +#include + +/* + * struct msm_slim_dma_data - DMA data for slimbus data transfer + * + * @sdev: Handle to the slim_device instance associated with the + * data transfer. + * @ph: Port handle for the slimbus ports. + * @dai_channel_ctl: callback function into the CPU dai driver + * to setup the data path. + * + * This structure is used to share the slimbus port handles and + * other data path setup related handles with other drivers. + */ +struct msm_slim_dma_data { + + /* Handle to slimbus device */ + struct slim_device *sdev; + + /* Port Handle */ + u32 ph; + + /* Callback for data channel control */ + int (*dai_channel_ctl)(struct msm_slim_dma_data *dma_data, + struct snd_soc_dai *dai, bool enable); +}; + +#endif diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h new file mode 100644 index 000000000000..c9a429d8607d --- /dev/null +++ b/include/sound/q6adm-v2.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __Q6_ADM_V2_H__ +#define __Q6_ADM_V2_H__ + + +#define ADM_PATH_PLAYBACK 0x1 +#define ADM_PATH_LIVE_REC 0x2 +#define ADM_PATH_NONLIVE_REC 0x3 +#define ADM_PATH_COMPRESSED_RX 0x5 +#include +#include +#include + +#define MAX_MODULES_IN_TOPO 16 +#define ADM_GET_TOPO_MODULE_LIST_LENGTH\ + ((MAX_MODULES_IN_TOPO + 1) * sizeof(uint32_t)) +#define AUD_PROC_BLOCK_SIZE 4096 +#define AUD_VOL_BLOCK_SIZE 4096 +#define AUDIO_RX_CALIBRATION_SIZE (AUD_PROC_BLOCK_SIZE + \ + AUD_VOL_BLOCK_SIZE) +enum { + ADM_CUSTOM_TOP_CAL = 0, + ADM_AUDPROC_CAL, + ADM_AUDVOL_CAL, + ADM_RTAC_INFO_CAL, + ADM_RTAC_APR_CAL, + ADM_DTS_EAGLE, + ADM_SRS_TRUMEDIA, + ADM_RTAC_AUDVOL_CAL, + ADM_MAX_CAL_TYPES +}; + +enum { + ADM_MEM_MAP_INDEX_SOURCE_TRACKING = ADM_MAX_CAL_TYPES, + ADM_MEM_MAP_INDEX_MAX +}; + +enum { + ADM_CLIENT_ID_DEFAULT = 0, + ADM_CLIENT_ID_SOURCE_TRACKING, + ADM_CLIENT_ID_MAX, +}; + +#define MAX_COPPS_PER_PORT 0x8 +#define ADM_MAX_CHANNELS 8 + +/* multiple copp per stream. */ +struct route_payload { + unsigned int copp_idx[MAX_COPPS_PER_PORT]; + unsigned int port_id[MAX_COPPS_PER_PORT]; + int app_type; + int acdb_dev_id; + int sample_rate; + unsigned short num_copps; + unsigned int session_id; +}; + +int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, + void *srs_params); + +int adm_dts_eagle_set(int port_id, int copp_idx, int param_id, + void *data, uint32_t size); + +int adm_dts_eagle_get(int port_id, int copp_idx, int param_id, + void *data, uint32_t size); + +void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate); + +int adm_get_params(int port_id, int copp_idx, uint32_t module_id, + uint32_t param_id, uint32_t params_length, char *params); + +int adm_send_params_v5(int port_id, int copp_idx, char *params, + uint32_t params_length); + +int adm_dolby_dap_send_params(int port_id, int copp_idx, char *params, + uint32_t params_length); + +int adm_open(int port, int path, int rate, int mode, int topology, + int perf_mode, uint16_t bits_per_sample, + int app_type, int acdbdev_id); + +int adm_map_rtac_block(struct rtac_cal_block_data *cal_block); + +int adm_unmap_rtac_block(uint32_t *mem_map_handle); + +int adm_close(int port, int topology, int perf_mode); + +int adm_matrix_map(int path, struct route_payload payload_map, + int perf_mode); + +int adm_connect_afe_port(int mode, int session_id, int port_id); + +void adm_ec_ref_rx_id(int port_id); + +int adm_get_lowlatency_copp_id(int port_id); + +int adm_set_multi_ch_map(char *channel_map, int path); + +int adm_get_multi_ch_map(char *channel_map, int path); + +int adm_validate_and_get_port_index(int port_id); + +int adm_get_default_copp_idx(int port_id); + +int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id); + +int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx); + +int adm_get_indexes_from_copp_id(int copp_id, int *port_idx, int *copp_idx); + +int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx, + unsigned int session_id, + char *params, uint32_t params_length); + +int adm_get_pp_topo_module_list(int port_id, int copp_idx, int32_t param_length, + char *params); + +int adm_set_volume(int port_id, int copp_idx, int volume); + +int adm_set_softvolume(int port_id, int copp_idx, + struct audproc_softvolume_params *softvol_param); + +int adm_param_enable(int port_id, int copp_idx, int module_id, int enable); + +int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode, + int cal_type, char *params, int size); + +int adm_set_wait_parameters(int port_id, int copp_idx); + +int adm_reset_wait_parameters(int port_id, int copp_idx); + +int adm_wait_timeout(int port_id, int copp_idx, int wait_time); + +int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode, + int cal_type, char *params, int *size); + +int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on); + +int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency); +int adm_set_sound_focus(int port_id, int copp_idx, + struct sound_focus_param soundFocusData); +int adm_get_sound_focus(int port_id, int copp_idx, + struct sound_focus_param *soundFocusData); +int adm_get_source_tracking(int port_id, int copp_idx, + struct source_tracking_param *sourceTrackingData); +#endif /* __Q6_ADM_V2_H__ */ diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h new file mode 100644 index 000000000000..367e75d381de --- /dev/null +++ b/include/sound/q6afe-v2.h @@ -0,0 +1,366 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __Q6AFE_V2_H__ +#define __Q6AFE_V2_H__ +#include +#include + +#define IN 0x000 +#define OUT 0x001 +#define MSM_AFE_MONO 0 +#define MSM_AFE_CH_STEREO 1 +#define MSM_AFE_MONO_RIGHT 1 +#define MSM_AFE_MONO_LEFT 2 +#define MSM_AFE_STEREO 3 +#define MSM_AFE_4CHANNELS 4 +#define MSM_AFE_6CHANNELS 6 +#define MSM_AFE_8CHANNELS 8 + +#define MSM_AFE_I2S_FORMAT_LPCM 0 +#define MSM_AFE_I2S_FORMAT_COMPR 1 +#define MSM_AFE_I2S_FORMAT_IEC60958_LPCM 2 +#define MSM_AFE_I2S_FORMAT_IEC60958_COMPR 3 + +#define MSM_AFE_PORT_TYPE_RX 0 +#define MSM_AFE_PORT_TYPE_TX 1 + +#define RT_PROXY_DAI_001_RX 0xE0 +#define RT_PROXY_DAI_001_TX 0xF0 +#define RT_PROXY_DAI_002_RX 0xF1 +#define RT_PROXY_DAI_002_TX 0xE1 +#define VIRTUAL_ID_TO_PORTID(val) ((val & 0xF) | 0x2000) + +#define AFE_CLK_VERSION_V1 1 +#define AFE_CLK_VERSION_V2 2 + +enum { + /* IDX 0->4 */ + IDX_PRIMARY_I2S_RX, + IDX_PRIMARY_I2S_TX, + IDX_AFE_PORT_ID_PRIMARY_PCM_RX, + IDX_AFE_PORT_ID_PRIMARY_PCM_TX, + IDX_SECONDARY_I2S_RX, + /* IDX 5->9 */ + IDX_SECONDARY_I2S_TX, + IDX_MI2S_RX, + IDX_MI2S_TX, + IDX_HDMI_RX, + IDX_RSVD_2, + /* IDX 10->14 */ + IDX_RSVD_3, + IDX_DIGI_MIC_TX, + IDX_VOICE_RECORD_RX, + IDX_VOICE_RECORD_TX, + IDX_VOICE_PLAYBACK_TX, + /* IDX 15->19 */ + IDX_SLIMBUS_0_RX, + IDX_SLIMBUS_0_TX, + IDX_SLIMBUS_1_RX, + IDX_SLIMBUS_1_TX, + IDX_SLIMBUS_2_RX, + /* IDX 20->24 */ + IDX_SLIMBUS_2_TX, + IDX_SLIMBUS_3_RX, + IDX_SLIMBUS_3_TX, + IDX_SLIMBUS_4_RX, + IDX_SLIMBUS_4_TX, + /* IDX 25->29 */ + IDX_SLIMBUS_5_RX, + IDX_SLIMBUS_5_TX, + IDX_INT_BT_SCO_RX, + IDX_INT_BT_SCO_TX, + IDX_INT_BT_A2DP_RX, + /* IDX 30->34 */ + IDX_INT_FM_RX, + IDX_INT_FM_TX, + IDX_RT_PROXY_PORT_001_RX, + IDX_RT_PROXY_PORT_001_TX, + IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX, + /* IDX 35->39 */ + IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX, + IDX_AFE_PORT_ID_SECONDARY_MI2S_RX, + IDX_AFE_PORT_ID_SECONDARY_MI2S_TX, + IDX_AFE_PORT_ID_TERTIARY_MI2S_RX, + IDX_AFE_PORT_ID_TERTIARY_MI2S_TX, + /* IDX 40->44 */ + IDX_AFE_PORT_ID_PRIMARY_MI2S_RX, + IDX_AFE_PORT_ID_PRIMARY_MI2S_TX, + IDX_AFE_PORT_ID_SECONDARY_PCM_RX, + IDX_AFE_PORT_ID_SECONDARY_PCM_TX, + IDX_VOICE2_PLAYBACK_TX, + /* IDX 45->49 */ + IDX_SLIMBUS_6_RX, + IDX_SLIMBUS_6_TX, + IDX_SPDIF_RX, + IDX_GLOBAL_CFG, + IDX_AUDIO_PORT_ID_I2S_RX, + /* IDX 50->53 */ + IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1, + IDX_AFE_PORT_ID_QUINARY_MI2S_RX, + IDX_AFE_PORT_ID_QUINARY_MI2S_TX, + IDX_AFE_PORT_ID_SENARY_MI2S_TX, + /* IDX 54->117 */ + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7, + /* IDX 118->121 */ + IDX_SLIMBUS_7_RX, + IDX_SLIMBUS_7_TX, + IDX_SLIMBUS_8_RX, + IDX_SLIMBUS_8_TX, + /* IDX 122-> 123 */ + IDX_AFE_PORT_ID_USB_RX, + IDX_AFE_PORT_ID_USB_TX, + /* IDX 124 */ + IDX_DISPLAY_PORT_RX, + /* IDX 125-> 128 */ + IDX_AFE_PORT_ID_TERTIARY_PCM_RX, + IDX_AFE_PORT_ID_TERTIARY_PCM_TX, + IDX_AFE_PORT_ID_QUATERNARY_PCM_RX, + IDX_AFE_PORT_ID_QUATERNARY_PCM_TX, + /* IDX 129-> 142 */ + IDX_AFE_PORT_ID_INT0_MI2S_RX, + IDX_AFE_PORT_ID_INT0_MI2S_TX, + IDX_AFE_PORT_ID_INT1_MI2S_RX, + IDX_AFE_PORT_ID_INT1_MI2S_TX, + IDX_AFE_PORT_ID_INT2_MI2S_RX, + IDX_AFE_PORT_ID_INT2_MI2S_TX, + IDX_AFE_PORT_ID_INT3_MI2S_RX, + IDX_AFE_PORT_ID_INT3_MI2S_TX, + IDX_AFE_PORT_ID_INT4_MI2S_RX, + IDX_AFE_PORT_ID_INT4_MI2S_TX, + IDX_AFE_PORT_ID_INT5_MI2S_RX, + IDX_AFE_PORT_ID_INT5_MI2S_TX, + IDX_AFE_PORT_ID_INT6_MI2S_RX, + IDX_AFE_PORT_ID_INT6_MI2S_TX, + AFE_MAX_PORTS +}; + +enum afe_mad_type { + MAD_HW_NONE = 0x00, + MAD_HW_AUDIO = 0x01, + MAD_HW_BEACON = 0x02, + MAD_HW_ULTRASOUND = 0x04, + MAD_SW_AUDIO = 0x05, +}; + +enum afe_cal_mode { + AFE_CAL_MODE_DEFAULT = 0x00, + AFE_CAL_MODE_NONE, +}; + +struct afe_audio_buffer { + dma_addr_t phys; + void *data; + uint32_t used; + uint32_t size;/* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ + struct ion_handle *handle; + struct ion_client *client; +}; + +struct afe_audio_port_data { + struct afe_audio_buffer *buf; + uint32_t max_buf_cnt; + uint32_t dsp_buf; + uint32_t cpu_buf; + struct list_head mem_map_handle; + uint32_t tmp_hdl; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; +}; + +struct afe_audio_client { + atomic_t cmd_state; + /* Relative or absolute TS */ + uint32_t time_flag; + void *priv; + uint64_t time_stamp; + struct mutex cmd_lock; + /* idx:1 out port, 0: in port*/ + struct afe_audio_port_data port[2]; + wait_queue_head_t cmd_wait; + uint32_t mem_map_handle; +}; + +struct aanc_data { + bool aanc_active; + uint16_t aanc_rx_port; + uint16_t aanc_tx_port; + uint32_t aanc_rx_port_sample_rate; + uint32_t aanc_tx_port_sample_rate; +}; + +int afe_open(u16 port_id, union afe_port_config *afe_config, int rate); +int afe_close(int port_id); +int afe_loopback(u16 enable, u16 rx_port, u16 tx_port); +int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain); +int afe_loopback_gain(u16 port_id, u16 volume); +int afe_validate_port(u16 port_id); +int afe_get_port_index(u16 port_id); +int afe_get_topology(int port_id); +int afe_start_pseudo_port(u16 port_id); +int afe_stop_pseudo_port(u16 port_id); +uint32_t afe_req_mmap_handle(struct afe_audio_client *ac); +int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz, + struct afe_audio_client *ac); +int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz); +int afe_cmd_memory_map_nowait(int port_id, phys_addr_t dma_addr_p, + u32 dma_buf_sz); +int afe_cmd_memory_unmap(u32 dma_addr_p); +int afe_cmd_memory_unmap_nowait(u32 dma_addr_p); +void afe_set_dtmf_gen_rx_portid(u16 rx_port_id, int set); +int afe_dtmf_generate_rx(int64_t duration_in_ms, + uint16_t high_freq, + uint16_t low_freq, uint16_t gain); +int afe_register_get_events(u16 port_id, + void (*cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv), + void *private_data); +int afe_unregister_get_events(u16 port_id); +int afe_rt_proxy_port_write(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes); +int afe_rt_proxy_port_read(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes); +void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode); +int afe_port_start(u16 port_id, union afe_port_config *afe_config, + u32 rate); +int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config, + u32 rate, u16 afe_in_channels, u16 afe_in_bit_width, + struct afe_enc_config *enc_config); +int afe_spk_prot_feed_back_cfg(int src_port, int dst_port, + int l_ch, int r_ch, u32 enable); +int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib); +int afe_port_stop_nowait(int port_id); +int afe_apply_gain(u16 port_id, u16 gain); +int afe_q6_interface_prepare(void); +int afe_get_port_type(u16 port_id); +int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir, + struct afe_audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt); +struct afe_audio_client *q6afe_audio_client_alloc(void *priv); +int q6afe_audio_client_buf_free_contiguous(unsigned int dir, + struct afe_audio_client *ac); +void q6afe_audio_client_free(struct afe_audio_client *ac); +/* if port_id is virtual, convert to physical.. + * if port_id is already physical, return physical + */ +int afe_convert_virtual_to_portid(u16 port_id); + +int afe_pseudo_port_start_nowait(u16 port_id); +int afe_pseudo_port_stop_nowait(u16 port_id); +int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg); +int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg); +int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg); +int afe_set_digital_codec_core_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg); +int afe_set_lpass_internal_digital_codec_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg); +int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable); + +int q6afe_check_osr_clk_freq(u32 freq); + +int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg, + u16 port_id); +int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg + *ch_status_cfg, u16 port_id); + +int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port, + u32 rate); + +int afe_turn_onoff_hw_mad(u16 mad_type, u16 mad_enable); +int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type); +enum afe_mad_type afe_port_get_mad_type(u16 port_id); +int afe_set_config(enum afe_config_type config_type, void *config_data, + int arg); +void afe_clear_config(enum afe_config_type config); +bool afe_has_config(enum afe_config_type config); + +void afe_set_aanc_info(struct aanc_data *aanc_info); +int afe_port_group_set_param(u16 group_id, + union afe_port_group_config *afe_group_config); +int afe_port_group_enable(u16 group_id, + union afe_port_group_config *afe_group_config, u16 enable); +int afe_unmap_rtac_block(uint32_t *mem_map_handle); +int afe_map_rtac_block(struct rtac_cal_block_data *cal_block); +int afe_send_slot_mapping_cfg( + struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg, + u16 port_id); +int afe_send_custom_tdm_header_cfg( + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg, + u16 port_id); +int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, + u32 rate); +#endif /* __Q6AFE_V2_H__ */ diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h new file mode 100644 index 000000000000..732148164473 --- /dev/null +++ b/include/sound/q6asm-v2.h @@ -0,0 +1,637 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __Q6_ASM_V2_H__ +#define __Q6_ASM_V2_H__ + +#include +#include +#include +#include +#include + +#define IN 0x000 +#define OUT 0x001 +#define CH_MODE_MONO 0x001 +#define CH_MODE_STEREO 0x002 + +#define FORMAT_LINEAR_PCM 0x0000 +#define FORMAT_DTMF 0x0001 +#define FORMAT_ADPCM 0x0002 +#define FORMAT_YADPCM 0x0003 +#define FORMAT_MP3 0x0004 +#define FORMAT_MPEG4_AAC 0x0005 +#define FORMAT_AMRNB 0x0006 +#define FORMAT_AMRWB 0x0007 +#define FORMAT_V13K 0x0008 +#define FORMAT_EVRC 0x0009 +#define FORMAT_EVRCB 0x000a +#define FORMAT_EVRCWB 0x000b +#define FORMAT_MIDI 0x000c +#define FORMAT_SBC 0x000d +#define FORMAT_WMA_V10PRO 0x000e +#define FORMAT_WMA_V9 0x000f +#define FORMAT_AMR_WB_PLUS 0x0010 +#define FORMAT_MPEG4_MULTI_AAC 0x0011 +#define FORMAT_MULTI_CHANNEL_LINEAR_PCM 0x0012 +#define FORMAT_AC3 0x0013 +#define FORMAT_EAC3 0x0014 +#define FORMAT_MP2 0x0015 +#define FORMAT_FLAC 0x0016 +#define FORMAT_ALAC 0x0017 +#define FORMAT_VORBIS 0x0018 +#define FORMAT_APE 0x0019 +#define FORMAT_G711_ALAW_FS 0x001a +#define FORMAT_G711_MLAW_FS 0x001b +#define FORMAT_DTS 0x001c +#define FORMAT_DSD 0x001d + +#define ENCDEC_SBCBITRATE 0x0001 +#define ENCDEC_IMMEDIATE_DECODE 0x0002 +#define ENCDEC_CFG_BLK 0x0003 + +#define CMD_PAUSE 0x0001 +#define CMD_FLUSH 0x0002 +#define CMD_EOS 0x0003 +#define CMD_CLOSE 0x0004 +#define CMD_OUT_FLUSH 0x0005 +#define CMD_SUSPEND 0x0006 + +/* bit 0:1 represents priority of stream */ +#define STREAM_PRIORITY_NORMAL 0x0000 +#define STREAM_PRIORITY_LOW 0x0001 +#define STREAM_PRIORITY_HIGH 0x0002 + +/* bit 4 represents META enable of encoded data buffer */ +#define BUFFER_META_ENABLE 0x0010 + +/* bit 5 represents timestamp */ +/* bit 5 - 0 -- ASM_DATA_EVENT_READ_DONE will have relative time-stamp*/ +/* bit 5 - 1 -- ASM_DATA_EVENT_READ_DONE will have absolute time-stamp*/ +#define ABSOLUTE_TIMESTAMP_ENABLE 0x0020 + +/* Enable Sample_Rate/Channel_Mode notification event from Decoder */ +#define SR_CM_NOTIFY_ENABLE 0x0004 + +#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ +#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ +#define SYNC_IO_MODE 0x0001 +#define ASYNC_IO_MODE 0x0002 +#define COMPRESSED_IO 0x0040 +#define COMPRESSED_STREAM_IO 0x0080 +#define NT_MODE 0x0400 + +#define NO_TIMESTAMP 0xFF00 +#define SET_TIMESTAMP 0x0000 + +#define SOFT_PAUSE_ENABLE 1 +#define SOFT_PAUSE_DISABLE 0 + +#define ASM_ACTIVE_STREAMS_ALLOWED 0x8 +/* Control session is used for mapping calibration memory */ +#define ASM_CONTROL_SESSION (ASM_ACTIVE_STREAMS_ALLOWED + 1) + +#define ASM_SHIFT_GAPLESS_MODE_FLAG 31 +#define ASM_SHIFT_LAST_BUFFER_FLAG 30 + +#define ASM_LITTLE_ENDIAN 0 +#define ASM_BIG_ENDIAN 1 + +/* PCM_MEDIA_FORMAT_Version */ +enum { + PCM_MEDIA_FORMAT_V2 = 0, + PCM_MEDIA_FORMAT_V3, + PCM_MEDIA_FORMAT_V4, +}; + +/* PCM format modes in DSP */ +enum { + DEFAULT_QF = 0, + Q15 = 15, + Q23 = 23, + Q31 = 31, +}; + +/* payload structure bytes */ +#define READDONE_IDX_STATUS 0 +#define READDONE_IDX_BUFADD_LSW 1 +#define READDONE_IDX_BUFADD_MSW 2 +#define READDONE_IDX_MEMMAP_HDL 3 +#define READDONE_IDX_SIZE 4 +#define READDONE_IDX_OFFSET 5 +#define READDONE_IDX_LSW_TS 6 +#define READDONE_IDX_MSW_TS 7 +#define READDONE_IDX_FLAGS 8 +#define READDONE_IDX_NUMFRAMES 9 +#define READDONE_IDX_SEQ_ID 10 + +#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */ +#define SOFT_PAUSE_STEP 0 /* Step value 0ms or 0us */ +enum { + SOFT_PAUSE_CURVE_LINEAR = 0, + SOFT_PAUSE_CURVE_EXP, + SOFT_PAUSE_CURVE_LOG, +}; + +#define SOFT_VOLUME_PERIOD 30 /* ramp up/down for 30ms */ +#define SOFT_VOLUME_STEP 0 /* Step value 0ms or 0us */ +enum { + SOFT_VOLUME_CURVE_LINEAR = 0, + SOFT_VOLUME_CURVE_EXP, + SOFT_VOLUME_CURVE_LOG, +}; + +#define SOFT_VOLUME_INSTANCE_1 1 +#define SOFT_VOLUME_INSTANCE_2 2 + +typedef void (*app_cb)(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t used; + uint32_t size;/* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ + struct ion_handle *handle; + struct ion_client *client; +}; + +struct audio_aio_write_param { + phys_addr_t paddr; + uint32_t len; + uint32_t uid; + uint32_t lsw_ts; + uint32_t msw_ts; + uint32_t flags; + uint32_t metadata_len; + uint32_t last_buffer; +}; + +struct audio_aio_read_param { + phys_addr_t paddr; + uint32_t len; + uint32_t uid; + uint32_t flags;/*meta data flags*/ +}; + +struct audio_port_data { + struct audio_buffer *buf; + uint32_t max_buf_cnt; + uint32_t dsp_buf; + uint32_t cpu_buf; + struct list_head mem_map_handle; + uint32_t tmp_hdl; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; +}; + +struct shared_io_config { + uint32_t format; + uint16_t bits_per_sample; + uint32_t rate; + uint32_t channels; + uint16_t sample_word_size; + uint32_t bufsz; + uint32_t bufcnt; +}; + +struct audio_client { + int session; + app_cb cb; + atomic_t cmd_state; + /* Relative or absolute TS */ + atomic_t time_flag; + atomic_t nowait_cmd_cnt; + atomic_t mem_state; + void *priv; + uint32_t io_mode; + uint64_t time_stamp; + struct apr_svc *apr; + struct apr_svc *mmap_apr; + struct apr_svc *apr2; + struct mutex cmd_lock; + /* idx:1 out port, 0: in port*/ + struct audio_port_data port[2]; + wait_queue_head_t cmd_wait; + wait_queue_head_t time_wait; + wait_queue_head_t mem_wait; + int perf_mode; + int stream_id; + struct device *dev; + int topology; + int app_type; + /* audio cache operations fptr*/ + int (*fptr_cache_ops)(struct audio_buffer *abuff, int cache_op); + atomic_t unmap_cb_success; + atomic_t reset; + /* holds latest DSP pipeline delay */ + uint32_t path_delay; + /* shared io */ + struct audio_buffer shared_pos_buf; + struct shared_io_config config; +}; + +void q6asm_audio_client_free(struct audio_client *ac); + +struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv); + +struct audio_client *q6asm_get_audio_client(int session_id); + +int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */, + struct audio_client *ac, + unsigned int bufsz, + uint32_t bufcnt); +int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir + /* 1:Out,0:In */, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt); + +int q6asm_audio_client_buf_free_contiguous(unsigned int dir, + struct audio_client *ac); + +int q6asm_open_read(struct audio_client *ac, uint32_t format + /*, uint16_t bits_per_sample*/); + +int q6asm_open_read_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_read_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_write(struct audio_client *ac, uint32_t format + /*, uint16_t bits_per_sample*/); + +int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_shared_io(struct audio_client *ac, + struct shared_io_config *c, int dir); + +int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, + uint32_t passthrough_flag); + +int q6asm_open_read_write(struct audio_client *ac, + uint32_t rd_format, + uint32_t wr_format); + +int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format, bool is_meta_data_mode, + uint32_t bits_per_sample, bool overwrite_topology, + int topology); + +int q6asm_open_loopback_v2(struct audio_client *ac, + uint16_t bits_per_sample); + +int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); + +int q6asm_async_write(struct audio_client *ac, + struct audio_aio_write_param *param); + +int q6asm_async_read(struct audio_client *ac, + struct audio_aio_read_param *param); + +int q6asm_read(struct audio_client *ac); +int q6asm_read_v2(struct audio_client *ac, uint32_t len); +int q6asm_read_nolock(struct audio_client *ac); + +int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add, + int dir, uint32_t bufsz, uint32_t bufcnt); + +int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, + int dir); + +struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac, int dir); + +int q6asm_shared_io_free(struct audio_client *ac, int dir); + +int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *si, uint32_t *msw, + uint32_t *lsw); + +int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block); + +int q6asm_unmap_rtac_block(uint32_t *mem_map_handle); + +int q6asm_send_cal(struct audio_client *ac); + +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); + +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); + +int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id); + +int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable); + +int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable); + +int q6asm_cmd(struct audio_client *ac, int cmd); + +int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id); + +int q6asm_cmd_nowait(struct audio_client *ac, int cmd); + +int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd, + uint32_t stream_id); + +void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *idx); + +int q6asm_cpu_buf_release(int dir, struct audio_client *ac); + +void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *idx); + +int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac); + +/* File format specific configurations to be added below */ + +int q6asm_enc_cfg_blk_aac(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, + uint32_t mode, uint32_t format); + +int q6asm_enc_cfg_blk_g711(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate); + +int q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + bool use_default_chmap, bool use_back_flavor, + u8 *channel_map); + +int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size); + +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode); + +int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size); + +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_set_encdec_chan_map(struct audio_client *ac, + uint32_t num_channels); + +int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_enable_sbrps(struct audio_client *ac, + uint32_t sbr_ps); + +int q6asm_cfg_dual_mono_aac(struct audio_client *ac, + uint16_t sce_left, uint16_t sce_right); + +int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff); + +int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t reduced_rate_level, uint16_t rate_modulation_cmd); + +int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t rate_modulation_cmd); + +int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable); + +int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable); + +int q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_media_format_block_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, int stream_id, + bool use_default_chmap, char *channel_map); + +int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size); + +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map); + +int q6asm_media_format_block_multi_ch_pcm_v2( + struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample); + +int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size); + +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg); + +int q6asm_stream_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg, int stream_id); + +int q6asm_media_format_block_multi_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg); + +int q6asm_media_format_block_wma(struct audio_client *ac, + void *cfg, int stream_id); + +int q6asm_media_format_block_wmapro(struct audio_client *ac, + void *cfg, int stream_id); + +int q6asm_media_format_block_amrwbplus(struct audio_client *ac, + struct asm_amrwbplus_cfg *cfg); + +int q6asm_stream_media_format_block_flac(struct audio_client *ac, + struct asm_flac_cfg *cfg, int stream_id); + +int q6asm_media_format_block_alac(struct audio_client *ac, + struct asm_alac_cfg *cfg, int stream_id); + +int q6asm_media_format_block_g711(struct audio_client *ac, + struct asm_g711_dec_cfg *cfg, int stream_id); + +int q6asm_stream_media_format_block_vorbis(struct audio_client *ac, + struct asm_vorbis_cfg *cfg, int stream_id); + +int q6asm_media_format_block_ape(struct audio_client *ac, + struct asm_ape_cfg *cfg, int stream_id); + +int q6asm_media_format_block_dsd(struct audio_client *ac, + struct asm_dsd_cfg *cfg, int stream_id); + +int q6asm_ds1_set_endp_params(struct audio_client *ac, + int param_id, int param_value); + +/* Send stream based end params */ +int q6asm_ds1_set_stream_endp_params(struct audio_client *ac, int param_id, + int param_value, int stream_id); + +/* PP specific */ +int q6asm_equalizer(struct audio_client *ac, void *eq); + +/* Send Volume Command */ +int q6asm_set_volume(struct audio_client *ac, int volume); + +/* Send Volume Command */ +int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance); + +/* DTS Eagle Params */ +int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, + void *data, struct param_outband *po, int m_id); +int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size, + void *data, struct param_outband *po, int m_id); + +/* Set SoftPause Params */ +int q6asm_set_softpause(struct audio_client *ac, + struct asm_softpause_params *param); + +/* Set Softvolume Params */ +int q6asm_set_softvolume(struct audio_client *ac, + struct asm_softvolume_params *param); + +/* Set Softvolume Params */ +int q6asm_set_softvolume_v2(struct audio_client *ac, + struct asm_softvolume_params *param, int instance); + +/* Send left-right channel gain */ +int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain); + +/* Send multi channel gain */ +int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, + uint32_t *gains, uint8_t *ch_map, bool use_default); + +/* Enable Mute/unmute flag */ +int q6asm_set_mute(struct audio_client *ac, int muteflag); + +int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp); + +int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp); + +int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, + uint32_t params_length); + +/* Client can set the IO mode to either AIO/SIO mode */ +int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode); + +/* Get Service ID for APR communication */ +int q6asm_get_apr_service_id(int session_id); + +/* Common format block without any payload */ +int q6asm_media_format_block(struct audio_client *ac, uint32_t format); + +/* Send the meta data to remove initial and trailing silence */ +int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples, + uint32_t trailing_samples); + +/* Send the stream meta data to remove initial and trailing silence */ +int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id, + uint32_t initial_samples, uint32_t trailing_samples); + +int q6asm_get_asm_topology(int session_id); +int q6asm_get_asm_app_type(int session_id); + +int q6asm_send_mtmx_strtr_window(struct audio_client *ac, + struct asm_session_mtmx_strtr_param_window_v2_t *window_param, + uint32_t param_id); + +/* Retrieve the current DSP path delay */ +int q6asm_get_path_delay(struct audio_client *ac); + +/* Helper functions to retrieve data from token */ +uint8_t q6asm_get_buf_index_from_token(uint32_t token); +uint8_t q6asm_get_stream_id_from_token(uint32_t token); + +#endif /* __Q6_ASM_H__ */ diff --git a/include/sound/q6audio-v2.h b/include/sound/q6audio-v2.h new file mode 100644 index 000000000000..fd14f330d1d5 --- /dev/null +++ b/include/sound/q6audio-v2.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2012-2013, 2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _Q6_AUDIO_H_ +#define _Q6_AUDIO_H_ + +#include + +enum { + LEGACY_PCM_MODE = 0, + LOW_LATENCY_PCM_MODE, + ULTRA_LOW_LATENCY_PCM_MODE, + ULL_POST_PROCESSING_PCM_MODE, +}; + + +int q6audio_get_port_index(u16 port_id); + +int q6audio_convert_virtual_to_portid(u16 port_id); + +int q6audio_validate_port(u16 port_id); + +int q6audio_is_digital_pcm_interface(u16 port_id); + +int q6audio_get_port_id(u16 port_id); + +#endif diff --git a/include/sound/q6core.h b/include/sound/q6core.h new file mode 100644 index 000000000000..0b8309a10a66 --- /dev/null +++ b/include/sound/q6core.h @@ -0,0 +1,159 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __Q6CORE_H__ +#define __Q6CORE_H__ +#include + + + +#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C +#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D + +bool q6core_is_adsp_ready(void); + +#define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 +#define DTS_EAGLE_LICENSE_ID 0x00028346 +struct adsp_dts_eagle { + struct apr_hdr hdr; + uint32_t id; + uint32_t overwrite; + uint32_t size; + char data[]; +}; +int core_dts_eagle_set(int size, char *data); +int core_dts_eagle_get(int id, int size, char *data); + +#define ADSP_CMD_SET_DOLBY_MANUFACTURER_ID 0x00012918 + +struct adsp_dolby_manufacturer_id { + struct apr_hdr hdr; + int manufacturer_id; +}; + +uint32_t core_set_dolby_manufacturer_id(int manufacturer_id); + +/* Dolby Surround1 Module License ID. This ID is used as an identifier + * for DS1 license via ADSP generic license mechanism. + * Please refer AVCS_CMD_SET_LICENSE for more details. + */ +#define DOLBY_DS1_LICENSE_ID 0x00000001 + +#define AVCS_CMD_SET_LICENSE 0x00012919 +struct avcs_cmd_set_license { + struct apr_hdr hdr; + uint32_t id; /**< A unique ID used to refer to this license */ + uint32_t overwrite; + /* 0 = do not overwrite an existing license with this id. + * 1 = overwrite an existing license with this id. + */ + uint32_t size; + /**< Size in bytes of the license data following this header. */ + /* uint8_t* data , data and padding follows this structure + * total packet size needs to be multiple of 4 Bytes + */ + +}; + +#define AVCS_CMD_GET_LICENSE_VALIDATION_RESULT 0x0001291A +struct avcs_cmd_get_license_validation_result { + struct apr_hdr hdr; + uint32_t id; /**< A unique ID used to refer to this license */ +}; + +#define AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT 0x0001291B +struct avcs_cmdrsp_get_license_validation_result { + uint32_t result; + /* ADSP_EOK if the license validation result was successfully retrieved. + * ADSP_ENOTEXIST if there is no license with the given id. + * ADSP_ENOTIMPL if there is no validation function for a license + * with this id. + */ + uint32_t size; + /* Length in bytes of the result that follows this structure*/ +}; + +/* Set Q6 topologies */ +/* + * Registers custom topologies in the aDSP for + * use in audio, voice, AFE and LSM. + */ + + +#define AVCS_CMD_SHARED_MEM_MAP_REGIONS 0x00012924 +#define AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00012925 +#define AVCS_CMD_SHARED_MEM_UNMAP_REGIONS 0x00012926 + + +#define AVCS_CMD_REGISTER_TOPOLOGIES 0x00012923 + +/* The payload for the AVCS_CMD_REGISTER_TOPOLOGIES command */ +struct avcs_cmd_register_topologies { + struct apr_hdr hdr; + uint32_t payload_addr_lsw; + /* Lower 32 bits of the topology buffer address. */ + + uint32_t payload_addr_msw; + /* Upper 32 bits of the topology buffer address. */ + + uint32_t mem_map_handle; + /* Unique identifier for an address. + * -This memory map handle is returned by the aDSP through the + * memory map command. + * -NULL mem_map_handle is interpreted as in-band parameter + * passing. + * -Client has the flexibility to choose in-band or out-of-band. + * -Out-of-band is recommended in this case. + */ + + uint32_t payload_size; + /* Size in bytes of the valid data in the topology buffer. */ +} __packed; + + +#define AVCS_CMD_DEREGISTER_TOPOLOGIES 0x0001292a + +/* The payload for the AVCS_CMD_DEREGISTER_TOPOLOGIES command */ +struct avcs_cmd_deregister_topologies { + struct apr_hdr hdr; + uint32_t payload_addr_lsw; + /* Lower 32 bits of the topology buffer address. */ + + uint32_t payload_addr_msw; + /* Upper 32 bits of the topology buffer address. */ + + uint32_t mem_map_handle; + /* Unique identifier for an address. + * -This memory map handle is returned by the aDSP through the + * memory map command. + * -NULL mem_map_handle is interpreted as in-band parameter + * passing. + * -Client has the flexibility to choose in-band or out-of-band. + * -Out-of-band is recommended in this case. + */ + + uint32_t payload_size; + /* Size in bytes of the valid data in the topology buffer. */ + + uint32_t mode; + /* 1: Deregister selected topologies + * 2: Deregister all topologies + */ +} __packed; + +#define AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES 2 + + +int32_t core_set_license(uint32_t key, uint32_t module_id); +int32_t core_get_license_status(uint32_t module_id); + +#endif /* __Q6CORE_H__ */ diff --git a/include/sound/q6lsm.h b/include/sound/q6lsm.h new file mode 100644 index 000000000000..22a62da39c4f --- /dev/null +++ b/include/sound/q6lsm.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __Q6LSM_H__ +#define __Q6LSM_H__ + +#include +#include +#include +#include +#include + +#define MAX_NUM_CONFIDENCE 20 + +typedef void (*lsm_app_cb)(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +struct lsm_sound_model { + dma_addr_t phys; + void *data; + size_t size; /* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ + struct ion_handle *handle; + struct ion_client *client; + uint32_t mem_map_handle; +}; + +struct snd_lsm_event_status_v2 { + uint16_t status; + uint16_t payload_size; + uint8_t confidence_value[0]; +}; + +struct lsm_lab_buffer { + dma_addr_t phys; + void *data; + size_t size; + struct ion_handle *handle; + struct ion_client *client; + uint32_t mem_map_handle; +}; + +struct lsm_lab_hw_params { + u16 sample_rate; + u16 sample_size; + u32 buf_sz; + u32 period_count; +}; + +struct lsm_client { + int session; + lsm_app_cb cb; + atomic_t cmd_state; + void *priv; + struct apr_svc *apr; + struct apr_svc *mmap_apr; + struct mutex cmd_lock; + struct lsm_sound_model sound_model; + wait_queue_head_t cmd_wait; + uint32_t cmd_err_code; + uint16_t mode; + uint16_t connect_to_port; + uint8_t num_confidence_levels; + uint8_t *confidence_levels; + bool opened; + bool started; + dma_addr_t lsm_cal_phy_addr; + uint32_t lsm_cal_size; + uint32_t app_id; + bool lab_enable; + bool lab_started; + struct lsm_lab_buffer *lab_buffer; + struct lsm_lab_hw_params hw_params; + bool use_topology; +}; + +struct lsm_stream_cmd_open_tx { + struct apr_hdr hdr; + uint16_t app_id; + uint16_t reserved; + uint32_t sampling_rate; +} __packed; + +struct lsm_stream_cmd_open_tx_v2 { + struct apr_hdr hdr; + uint32_t topology_id; +} __packed; + +struct lsm_custom_topologies { + struct apr_hdr hdr; + uint32_t data_payload_addr_lsw; + uint32_t data_payload_addr_msw; + uint32_t mem_map_handle; + uint32_t buffer_size; +} __packed; + +struct lsm_param_size_reserved { + uint16_t param_size; + uint16_t reserved; +} __packed; + +union lsm_param_size { + uint32_t param_size; + struct lsm_param_size_reserved sr; +} __packed; + +struct lsm_param_payload_common { + uint32_t module_id; + uint32_t param_id; + union lsm_param_size p_size; +} __packed; + +struct lsm_param_op_mode { + struct lsm_param_payload_common common; + uint32_t minor_version; + uint16_t mode; + uint16_t reserved; +} __packed; + +struct lsm_param_connect_to_port { + struct lsm_param_payload_common common; + uint32_t minor_version; + /* AFE port id that receives voice wake up data */ + uint16_t port_id; + uint16_t reserved; +} __packed; + + +/* + * This param cannot be sent in this format. + * The actual number of confidence level values + * need to appended to this param payload. + */ +struct lsm_param_min_confidence_levels { + struct lsm_param_payload_common common; + uint8_t num_confidence_levels; +} __packed; + +struct lsm_set_params_hdr { + uint32_t data_payload_size; + uint32_t data_payload_addr_lsw; + uint32_t data_payload_addr_msw; + uint32_t mem_map_handle; +} __packed; + +struct lsm_cmd_set_params { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr param_hdr; +} __packed; + +struct lsm_cmd_set_params_conf { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_min_confidence_levels conf_payload; +} __packed; + +struct lsm_cmd_set_opmode_connectport { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_connect_to_port connect_to_port; + struct lsm_param_op_mode op_mode; +} __packed; + +struct lsm_param_epd_thres { + struct lsm_param_payload_common common; + uint32_t minor_version; + uint32_t epd_begin; + uint32_t epd_end; +} __packed; + +struct lsm_cmd_set_epd_threshold { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr param_hdr; + struct lsm_param_epd_thres epd_thres; +} __packed; + +struct lsm_param_gain { + struct lsm_param_payload_common common; + uint32_t minor_version; + uint16_t gain; + uint16_t reserved; +} __packed; + +struct lsm_cmd_set_gain { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr param_hdr; + struct lsm_param_gain lsm_gain; +} __packed; + +struct lsm_cmd_reg_snd_model { + struct apr_hdr hdr; + uint32_t model_size; + uint32_t model_addr_lsw; + uint32_t model_addr_msw; + uint32_t mem_map_handle; +} __packed; + +struct lsm_lab_enable { + struct lsm_param_payload_common common; + uint16_t enable; + uint16_t reserved; +} __packed; + +struct lsm_params_lab_enable { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_lab_enable lab_enable; +} __packed; + +struct lsm_lab_config { + struct lsm_param_payload_common common; + uint32_t minor_version; + uint32_t wake_up_latency_ms; +} __packed; + + +struct lsm_params_lab_config { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_lab_config lab_config; +} __packed; + +struct lsm_cmd_read { + struct apr_hdr hdr; + uint32_t buf_addr_lsw; + uint32_t buf_addr_msw; + uint32_t mem_map_handle; + uint32_t buf_size; +} __packed; + +struct lsm_cmd_read_done { + struct apr_hdr hdr; + uint32_t status; + uint32_t buf_addr_lsw; + uint32_t buf_addr_msw; + uint32_t mem_map_handle; + uint32_t total_size; + uint32_t offset; + uint32_t timestamp_lsw; + uint32_t timestamp_msw; + uint32_t flags; +} __packed; + +struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv); +void q6lsm_client_free(struct lsm_client *client); +int q6lsm_open(struct lsm_client *client, uint16_t app_id); +int q6lsm_start(struct lsm_client *client, bool wait); +int q6lsm_stop(struct lsm_client *client, bool wait); +int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, + bool allocate_module_data); +int q6lsm_snd_model_buf_free(struct lsm_client *client); +int q6lsm_close(struct lsm_client *client); +int q6lsm_register_sound_model(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure); +int q6lsm_set_data(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure); +int q6lsm_deregister_sound_model(struct lsm_client *client); +void set_lsm_port(int lsm_port); +int get_lsm_port(void); +int q6lsm_lab_control(struct lsm_client *client, u32 enable); +int q6lsm_stop_lab(struct lsm_client *client); +int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read); +int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc); +int q6lsm_set_one_param(struct lsm_client *client, + struct lsm_params_info *p_info, void *data, + enum LSM_PARAM_TYPE param_type); +void q6lsm_sm_set_param_data(struct lsm_client *client, + struct lsm_params_info *p_info, + size_t *offset); +#endif /* __Q6LSM_H__ */ diff --git a/include/sound/voice_params.h b/include/sound/voice_params.h new file mode 100644 index 000000000000..43e3b9d0aa49 --- /dev/null +++ b/include/sound/voice_params.h @@ -0,0 +1,14 @@ +#ifndef __VOICE_PARAMS_H__ +#define __VOICE_PARAMS_H__ + +#include +#include + +enum voice_lch_mode { + VOICE_LCH_START = 1, + VOICE_LCH_STOP +}; + +#define SNDRV_VOICE_IOCTL_LCH _IOW('U', 0x00, enum voice_lch_mode) + +#endif diff --git a/include/sound/voice_svc.h b/include/sound/voice_svc.h new file mode 100644 index 000000000000..035053f091ef --- /dev/null +++ b/include/sound/voice_svc.h @@ -0,0 +1,47 @@ +#ifndef __VOICE_SVC_H__ +#define __VOICE_SVC_H__ + +#include +#include + +#define VOICE_SVC_DRIVER_NAME "voice_svc" + +#define VOICE_SVC_MVM_STR "MVM" +#define VOICE_SVC_CVS_STR "CVS" +#define MAX_APR_SERVICE_NAME_LEN 64 + +#define MSG_REGISTER 0x1 +#define MSG_REQUEST 0x2 +#define MSG_RESPONSE 0x3 + +struct voice_svc_write_msg { + __u32 msg_type; + __u8 payload[0]; +}; + +struct voice_svc_register { + char svc_name[MAX_APR_SERVICE_NAME_LEN]; + __u32 src_port; + __u8 reg_flag; +}; + +struct voice_svc_cmd_response { + __u32 src_port; + __u32 dest_port; + __u32 token; + __u32 opcode; + __u32 payload_size; + __u8 payload[0]; +}; + +struct voice_svc_cmd_request { + char svc_name[MAX_APR_SERVICE_NAME_LEN]; + __u32 src_port; + __u32 dest_port; + __u32 token; + __u32 opcode; + __u32 payload_size; + __u8 payload[0]; +}; + +#endif diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index ae6fa2f8c593..6ce3d167c942 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -62,6 +62,7 @@ header-y += audit.h header-y += auto_fs4.h header-y += auto_fs.h header-y += auxvec.h +header-y += avtimer.h header-y += ax25.h header-y += b1lli.h header-y += baycom.h @@ -281,6 +282,23 @@ header-y += mroute6.h header-y += mroute.h header-y += msdos_fs.h header-y += msg.h +header-y += msm_audio.h +header-y += msm_audio_aac.h +header-y += msm_audio_ac3.h +header-y += msm_audio_amrnb.h +header-y += msm_audio_amrwb.h +header-y += msm_audio_amrwbplus.h +header-y += msm_audio_calibration.h +header-y += msm_audio_mvs.h +header-y += msm_audio_qcp.h +header-y += msm_audio_sbc.h +header-y += msm_audio_voicememo.h +header-y += msm_audio_wma.h +header-y += msm_audio_wmapro.h +header-y += msm_audio_alac.h +header-y += msm_audio_ape.h +header-y += msm_audio_g711.h +header-y += msm_audio_g711_dec.h header-y += msm_ion.h header-y += msm_ipc.h header-y += msm_kgsl.h diff --git a/include/uapi/linux/avtimer.h b/include/uapi/linux/avtimer.h new file mode 100644 index 000000000000..96b5483fbf2e --- /dev/null +++ b/include/uapi/linux/avtimer.h @@ -0,0 +1,10 @@ +#ifndef _UAPI_AVTIMER_H +#define _UAPI_AVTIMER_H + +#include + +#define MAJOR_NUM 100 + +#define IOCTL_GET_AVTIMER_TICK _IOR(MAJOR_NUM, 0, uint64_t) + +#endif diff --git a/include/uapi/linux/msm_audio.h b/include/uapi/linux/msm_audio.h new file mode 100644 index 000000000000..bde27d17e7f5 --- /dev/null +++ b/include/uapi/linux/msm_audio.h @@ -0,0 +1,464 @@ +/* include/linux/msm_audio.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2012, 2014, 2017 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _UAPI_LINUX_MSM_AUDIO_H +#define _UAPI_LINUX_MSM_AUDIO_H + +#include +#include + +/* PCM Audio */ + +#define AUDIO_IOCTL_MAGIC 'a' + +#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int) +#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned int) +#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned int) +#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, \ + struct msm_audio_config) +#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, \ + struct msm_audio_config) +#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, \ + struct msm_audio_stats) +#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned int) +#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned int) +#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned int) +#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned int) +#define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned int) +#define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned int) +#define AUDIO_PLAY_DTMF _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned int) +#define AUDIO_GET_EVENT _IOR(AUDIO_IOCTL_MAGIC, 13, \ + struct msm_audio_event) +#define AUDIO_ABORT_GET_EVENT _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned int) +#define AUDIO_REGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned int) +#define AUDIO_DEREGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 16, unsigned int) +#define AUDIO_ASYNC_WRITE _IOW(AUDIO_IOCTL_MAGIC, 17, \ + struct msm_audio_aio_buf) +#define AUDIO_ASYNC_READ _IOW(AUDIO_IOCTL_MAGIC, 18, \ + struct msm_audio_aio_buf) +#define AUDIO_SET_INCALL _IOW(AUDIO_IOCTL_MAGIC, 19, struct msm_voicerec_mode) +#define AUDIO_GET_NUM_SND_DEVICE _IOR(AUDIO_IOCTL_MAGIC, 20, unsigned int) +#define AUDIO_GET_SND_DEVICES _IOWR(AUDIO_IOCTL_MAGIC, 21, \ + struct msm_snd_device_list) +#define AUDIO_ENABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 22, unsigned int) +#define AUDIO_DISABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 23, unsigned int) +#define AUDIO_ROUTE_STREAM _IOW(AUDIO_IOCTL_MAGIC, 24, \ + struct msm_audio_route_config) +#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned int) +#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned int) +#define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned int) +#define AUDIO_SET_MUTE _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned int) +#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned int) +#define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned int) +#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned int) +#define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned int) +#define AUDIO_OUTPORT_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 40, unsigned short) +#define AUDIO_SET_ERR_THRESHOLD_VALUE _IOW(AUDIO_IOCTL_MAGIC, 41, \ + unsigned short) +#define AUDIO_GET_BITSTREAM_ERROR_INFO _IOR(AUDIO_IOCTL_MAGIC, 42, \ + struct msm_audio_bitstream_error_info) + +#define AUDIO_SET_SRS_TRUMEDIA_PARAM _IOW(AUDIO_IOCTL_MAGIC, 43, unsigned int) + +/* Qualcomm technologies inc extensions */ +#define AUDIO_SET_STREAM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 80, \ + struct msm_audio_stream_config) +#define AUDIO_GET_STREAM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 81, \ + struct msm_audio_stream_config) +#define AUDIO_GET_SESSION_ID _IOR(AUDIO_IOCTL_MAGIC, 82, unsigned short) +#define AUDIO_GET_STREAM_INFO _IOR(AUDIO_IOCTL_MAGIC, 83, \ + struct msm_audio_bitstream_info) +#define AUDIO_SET_PAN _IOW(AUDIO_IOCTL_MAGIC, 84, unsigned int) +#define AUDIO_SET_QCONCERT_PLUS _IOW(AUDIO_IOCTL_MAGIC, 85, unsigned int) +#define AUDIO_SET_MBADRC _IOW(AUDIO_IOCTL_MAGIC, 86, unsigned int) +#define AUDIO_SET_VOLUME_PATH _IOW(AUDIO_IOCTL_MAGIC, 87, \ + struct msm_vol_info) +#define AUDIO_SET_MAX_VOL_ALL _IOW(AUDIO_IOCTL_MAGIC, 88, unsigned int) +#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 89, unsigned int) +#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 90, unsigned int) +#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 91, unsigned int) +#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 92, unsigned int) +#define AUDIO_GET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 93, \ + struct msm_audio_buf_cfg) +#define AUDIO_SET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 94, \ + struct msm_audio_buf_cfg) +#define AUDIO_SET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 95, \ + struct msm_acdb_cmd_device) +#define AUDIO_GET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 96, \ + struct msm_acdb_cmd_device) + +#define AUDIO_REGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 97, \ + struct msm_audio_ion_info) +#define AUDIO_DEREGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 98, \ + struct msm_audio_ion_info) +#define AUDIO_SET_EFFECTS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 99, \ + struct msm_hwacc_effects_config) +#define AUDIO_EFFECTS_SET_BUF_LEN _IOW(AUDIO_IOCTL_MAGIC, 100, \ + struct msm_hwacc_buf_cfg) +#define AUDIO_EFFECTS_GET_BUF_AVAIL _IOW(AUDIO_IOCTL_MAGIC, 101, \ + struct msm_hwacc_buf_avail) +#define AUDIO_EFFECTS_WRITE _IOW(AUDIO_IOCTL_MAGIC, 102, void *) +#define AUDIO_EFFECTS_READ _IOWR(AUDIO_IOCTL_MAGIC, 103, void *) +#define AUDIO_EFFECTS_SET_PP_PARAMS _IOW(AUDIO_IOCTL_MAGIC, 104, void *) + +#define AUDIO_PM_AWAKE _IOW(AUDIO_IOCTL_MAGIC, 105, unsigned int) +#define AUDIO_PM_RELAX _IOW(AUDIO_IOCTL_MAGIC, 106, unsigned int) + +#define AUDIO_MAX_COMMON_IOCTL_NUM 107 + + +#define HANDSET_MIC 0x01 +#define HANDSET_SPKR 0x02 +#define HEADSET_MIC 0x03 +#define HEADSET_SPKR_MONO 0x04 +#define HEADSET_SPKR_STEREO 0x05 +#define SPKR_PHONE_MIC 0x06 +#define SPKR_PHONE_MONO 0x07 +#define SPKR_PHONE_STEREO 0x08 +#define BT_SCO_MIC 0x09 +#define BT_SCO_SPKR 0x0A +#define BT_A2DP_SPKR 0x0B +#define TTY_HEADSET_MIC 0x0C +#define TTY_HEADSET_SPKR 0x0D + +/* Default devices are not supported in a */ +/* device switching context. Only supported */ +/* for stream devices. */ +/* DO NOT USE */ +#define DEFAULT_TX 0x0E +#define DEFAULT_RX 0x0F + +#define BT_A2DP_TX 0x10 + +#define HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12 +#define HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13 +#define HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 + +#define I2S_RX 0x20 +#define I2S_TX 0x21 + +#define ADRC_ENABLE 0x0001 +#define EQUALIZER_ENABLE 0x0002 +#define IIR_ENABLE 0x0004 +#define QCONCERT_PLUS_ENABLE 0x0008 +#define MBADRC_ENABLE 0x0010 +#define SRS_ENABLE 0x0020 +#define SRS_DISABLE 0x0040 + +#define AGC_ENABLE 0x0001 +#define NS_ENABLE 0x0002 +#define TX_IIR_ENABLE 0x0004 +#define FLUENCE_ENABLE 0x0008 + +#define VOC_REC_UPLINK 0x00 +#define VOC_REC_DOWNLINK 0x01 +#define VOC_REC_BOTH 0x02 + +struct msm_audio_config { + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t channel_count; + uint32_t sample_rate; + uint32_t type; + uint32_t meta_field; + uint32_t bits; + uint32_t unused[3]; +}; + +struct msm_audio_stream_config { + uint32_t buffer_size; + uint32_t buffer_count; +}; + +struct msm_audio_buf_cfg { + uint32_t meta_info_enable; + uint32_t frames_per_buf; +}; + +struct msm_audio_stats { + uint32_t byte_count; + uint32_t sample_count; + uint32_t unused[2]; +}; + +struct msm_audio_ion_info { + int fd; + void *vaddr; +}; + +struct msm_audio_pmem_info { + int fd; + void *vaddr; +}; + +struct msm_audio_aio_buf { + void *buf_addr; + uint32_t buf_len; + uint32_t data_len; + void *private_data; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +/* Audio routing */ + +#define SND_IOCTL_MAGIC 's' + +#define SND_MUTE_UNMUTED 0 +#define SND_MUTE_MUTED 1 + +struct msm_mute_info { + uint32_t mute; + uint32_t path; +}; + +struct msm_vol_info { + uint32_t vol; + uint32_t path; +}; + +struct msm_voicerec_mode { + uint32_t rec_mode; +}; + +struct msm_snd_device_config { + uint32_t device; + uint32_t ear_mute; + uint32_t mic_mute; +}; + +#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *) + +enum cad_device_path_type { + CAD_DEVICE_PATH_RX, /*For Decoding session*/ + CAD_DEVICE_PATH_TX, /* For Encoding session*/ + CAD_DEVICE_PATH_RX_TX, /* For Voice call */ + CAD_DEVICE_PATH_LB, /* For loopback (FM Analog)*/ + CAD_DEVICE_PATH_MAX +}; + +struct cad_devices_type { + uint32_t rx_device; + uint32_t tx_device; + enum cad_device_path_type pathtype; +}; + +struct msm_cad_device_config { + struct cad_devices_type device; + uint32_t ear_mute; + uint32_t mic_mute; +}; + +#define CAD_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_cad_device_config *) + +#define SND_METHOD_VOICE 0 +#define SND_METHOD_MIDI 4 + +struct msm_snd_volume_config { + uint32_t device; + uint32_t method; + uint32_t volume; +}; + +#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *) + +struct msm_cad_volume_config { + struct cad_devices_type device; + uint32_t method; + uint32_t volume; +}; + +#define CAD_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_cad_volume_config *) + +/* Returns the number of SND endpoints supported. */ + +#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned int *) + +struct msm_snd_endpoint { + int id; /* input and output */ + char name[64]; /* output only */ +}; + +/* Takes an index between 0 and one less than the number returned by + * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a + * SND endpoint. On input, the .id field contains the number of the + * endpoint, and on exit it contains the SND index, while .name contains + * the description of the endpoint. + */ + +#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *) + + +#define SND_AVC_CTL _IOW(SND_IOCTL_MAGIC, 6, unsigned int *) +#define SND_AGC_CTL _IOW(SND_IOCTL_MAGIC, 7, unsigned int *) + +/*return the number of CAD endpoints supported. */ + +#define CAD_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned int *) + +struct msm_cad_endpoint { + int id; /* input and output */ + char name[64]; /* output only */ +}; + +/* Takes an index between 0 and one less than the number returned by + * SND_GET_NUM_ENDPOINTS, and returns the CAD index and name of a + * CAD endpoint. On input, the .id field contains the number of the + * endpoint, and on exit it contains the SND index, while .name contains + * the description of the endpoint. + */ + +#define CAD_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_cad_endpoint *) + +struct msm_audio_pcm_config { + uint32_t pcm_feedback; /* 0 - disable > 0 - enable */ + uint32_t buffer_count; /* Number of buffers to allocate */ + uint32_t buffer_size; /* Size of buffer for capturing of + * PCM samples + */ +}; + +#define AUDIO_EVENT_SUSPEND 0 +#define AUDIO_EVENT_RESUME 1 +#define AUDIO_EVENT_WRITE_DONE 2 +#define AUDIO_EVENT_READ_DONE 3 +#define AUDIO_EVENT_STREAM_INFO 4 +#define AUDIO_EVENT_BITSTREAM_ERROR_INFO 5 + +#define AUDIO_CODEC_TYPE_MP3 0 +#define AUDIO_CODEC_TYPE_AAC 1 + +struct msm_audio_bitstream_info { + uint32_t codec_type; + uint32_t chan_info; + uint32_t sample_rate; + uint32_t bit_stream_info; + uint32_t bit_rate; + uint32_t unused[3]; +}; + +struct msm_audio_bitstream_error_info { + uint32_t dec_id; + uint32_t err_msg_indicator; + uint32_t err_type; +}; + +union msm_audio_event_payload { + struct msm_audio_aio_buf aio_buf; + struct msm_audio_bitstream_info stream_info; + struct msm_audio_bitstream_error_info error_info; + int reserved; +}; + +struct msm_audio_event { + int event_type; + int timeout_ms; + union msm_audio_event_payload event_payload; +}; + +#define MSM_SNDDEV_CAP_RX 0x1 +#define MSM_SNDDEV_CAP_TX 0x2 +#define MSM_SNDDEV_CAP_VOICE 0x4 + +struct msm_snd_device_info { + uint32_t dev_id; + uint32_t dev_cap; /* bitmask describe capability of device */ + char dev_name[64]; +}; + +struct msm_snd_device_list { + uint32_t num_dev; /* Indicate number of device info to be retrieved */ + struct msm_snd_device_info *list; +}; + +struct msm_dtmf_config { + uint16_t path; + uint16_t dtmf_hi; + uint16_t dtmf_low; + uint16_t duration; + uint16_t tx_gain; + uint16_t rx_gain; + uint16_t mixing; +}; + +#define AUDIO_ROUTE_STREAM_VOICE_RX 0 +#define AUDIO_ROUTE_STREAM_VOICE_TX 1 +#define AUDIO_ROUTE_STREAM_PLAYBACK 2 +#define AUDIO_ROUTE_STREAM_REC 3 + +struct msm_audio_route_config { + uint32_t stream_type; + uint32_t stream_id; + uint32_t dev_id; +}; + +#define AUDIO_MAX_EQ_BANDS 12 + +struct msm_audio_eq_band { + uint16_t band_idx; /* The band index, 0 .. 11 */ + uint32_t filter_type; /* Filter band type */ + uint32_t center_freq_hz; /* Filter band center frequency */ + uint32_t filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + uint32_t q_factor; +} __attribute__ ((packed)); + +struct msm_audio_eq_stream_config { + uint32_t enable; /* Number of consequtive bands specified */ + uint32_t num_bands; + struct msm_audio_eq_band eq_bands[AUDIO_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +struct msm_acdb_cmd_device { + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; /* Actual sample rate value */ + uint32_t interface_id; /* See interface id's above */ + uint32_t algorithm_block_id; /* See enumerations above */ + uint32_t total_bytes; /* Length in bytes used by buffer */ + uint32_t *phys_buf; /* Physical Address of data */ +}; + +struct msm_hwacc_data_config { + __u32 buf_size; + __u32 num_buf; + __u32 num_channels; + __u8 channel_map[8]; + __u32 sample_rate; + __u32 bits_per_sample; +}; + +struct msm_hwacc_buf_cfg { + __u32 input_len; + __u32 output_len; +}; + +struct msm_hwacc_buf_avail { + __u32 input_num_avail; + __u32 output_num_avail; +}; + +struct msm_hwacc_effects_config { + struct msm_hwacc_data_config input; + struct msm_hwacc_data_config output; + struct msm_hwacc_buf_cfg buf_cfg; + __u32 meta_mode_enabled; + __u32 overwrite_topology; + __s32 topology; +}; + +#endif diff --git a/include/uapi/linux/msm_audio_aac.h b/include/uapi/linux/msm_audio_aac.h new file mode 100644 index 000000000000..7e1e1b72424a --- /dev/null +++ b/include/uapi/linux/msm_audio_aac.h @@ -0,0 +1,76 @@ +#ifndef _UAPI_MSM_AUDIO_AAC_H +#define _UAPI_MSM_AUDIO_AAC_H + +#include + +#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config) +#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config) + +#define AUDIO_SET_AAC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config) + +#define AUDIO_GET_AAC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config) + +#define AUDIO_SET_AAC_MIX_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+5), uint32_t) + +#define AUDIO_AAC_FORMAT_ADTS -1 +#define AUDIO_AAC_FORMAT_RAW 0x0000 +#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDIO_AAC_FORMAT_LOAS 0x0002 +#define AUDIO_AAC_FORMAT_ADIF 0x0003 + +#define AUDIO_AAC_OBJECT_LC 0x0002 +#define AUDIO_AAC_OBJECT_LTP 0x0004 +#define AUDIO_AAC_OBJECT_ERLC 0x0011 +#define AUDIO_AAC_OBJECT_BSAC 0x0016 + +#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +/* Primary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_PL_PR 0 +/* Secondary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_SL_SR 1 +/* Primary channel on right channel and 2nd on left channel */ +#define AUDIO_AAC_DUAL_MONO_SL_PR 2 +/* 2nd channel on right channel and primary on left channel */ +#define AUDIO_AAC_DUAL_MONO_PL_SR 3 + +struct msm_audio_aac_config { + signed short format; + unsigned short audio_object; + unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */ + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; + unsigned short sample_rate; +}; + +struct msm_audio_aac_enc_config { + uint32_t channels; + uint32_t sample_rate; + uint32_t bit_rate; + uint32_t stream_format; +}; + +#endif /* _UAPI_MSM_AUDIO_AAC_H */ diff --git a/include/uapi/linux/msm_audio_ac3.h b/include/uapi/linux/msm_audio_ac3.h new file mode 100644 index 000000000000..1df6e6949ccf --- /dev/null +++ b/include/uapi/linux/msm_audio_ac3.h @@ -0,0 +1,41 @@ +#ifndef _UAPI_MSM_AUDIO_AC3_H +#define _UAPI_MSM_AUDIO_AC3_H + +#include + +#define AUDIO_SET_AC3_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_GET_AC3_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) + +#define AUDAC3_DEF_WORDSIZE 0 +#define AUDAC3_DEF_USER_DOWNMIX_FLAG 0x0 +#define AUDAC3_DEF_USER_KARAOKE_FLAG 0x0 +#define AUDAC3_DEF_ERROR_CONCEALMENT 0 +#define AUDAC3_DEF_MAX_REPEAT_COUNT 0 + +struct msm_audio_ac3_config { + unsigned short numChans; + unsigned short wordSize; + unsigned short kCapableMode; + unsigned short compMode; + unsigned short outLfeOn; + unsigned short outputMode; + unsigned short stereoMode; + unsigned short dualMonoMode; + unsigned short fsCod; + unsigned short pcmScaleFac; + unsigned short dynRngScaleHi; + unsigned short dynRngScaleLow; + unsigned short user_downmix_flag; + unsigned short user_karaoke_flag; + unsigned short dm_address_high; + unsigned short dm_address_low; + unsigned short ko_address_high; + unsigned short ko_address_low; + unsigned short error_concealment; + unsigned short max_rep_count; + unsigned short channel_routing_mode[6]; +}; + +#endif /* _UAPI_MSM_AUDIO_AC3_H */ diff --git a/include/uapi/linux/msm_audio_alac.h b/include/uapi/linux/msm_audio_alac.h new file mode 100644 index 000000000000..5476e96d06fc --- /dev/null +++ b/include/uapi/linux/msm_audio_alac.h @@ -0,0 +1,24 @@ +#ifndef _UAPI_MSM_AUDIO_ALAC_H +#define _UAPI_MSM_AUDIO_ALAC_H + +#define AUDIO_GET_ALAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config) +#define AUDIO_SET_ALAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config) + +struct msm_audio_alac_config { + uint32_t frameLength; + uint8_t compatVersion; + uint8_t bitDepth; + uint8_t pb; /* currently unused */ + uint8_t mb; /* currently unused */ + uint8_t kb; /* currently unused */ + uint8_t channelCount; + uint16_t maxRun; /* currently unused */ + uint32_t maxSize; + uint32_t averageBitRate; + uint32_t sampleRate; + uint32_t channelLayout; +}; + +#endif /* _UAPI_MSM_AUDIO_ALAC_H */ diff --git a/include/uapi/linux/msm_audio_amrnb.h b/include/uapi/linux/msm_audio_amrnb.h new file mode 100644 index 000000000000..619f928f7175 --- /dev/null +++ b/include/uapi/linux/msm_audio_amrnb.h @@ -0,0 +1,34 @@ +#ifndef _UAPI_MSM_AUDIO_AMRNB_H +#define _UAPI_MSM_AUDIO_AMRNB_H + +#include + +#define AUDIO_GET_AMRNB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_SET_AMRNB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) +#define AUDIO_GET_AMRNB_ENC_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), \ + struct msm_audio_amrnb_enc_config_v2) +#define AUDIO_SET_AMRNB_ENC_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), \ + struct msm_audio_amrnb_enc_config_v2) + +struct msm_audio_amrnb_enc_config { + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short dtx_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short test_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short enc_mode; /* 0-MR475,1-MR515,2-MR59,3-MR67,4-MR74 + * 5-MR795, 6- MR102, 7- MR122(default) + */ +}; + +struct msm_audio_amrnb_enc_config_v2 { + uint32_t band_mode; + uint32_t dtx_enable; + uint32_t frame_format; +}; +#endif /* _UAPI_MSM_AUDIO_AMRNB_H */ diff --git a/include/uapi/linux/msm_audio_amrwb.h b/include/uapi/linux/msm_audio_amrwb.h new file mode 100644 index 000000000000..51240389988f --- /dev/null +++ b/include/uapi/linux/msm_audio_amrwb.h @@ -0,0 +1,18 @@ +#ifndef _UAPI_MSM_AUDIO_AMRWB_H +#define _UAPI_MSM_AUDIO_AMRWB_H + +#include + +#define AUDIO_GET_AMRWB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), \ + struct msm_audio_amrwb_enc_config) +#define AUDIO_SET_AMRWB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), \ + struct msm_audio_amrwb_enc_config) + +struct msm_audio_amrwb_enc_config { + uint32_t band_mode; + uint32_t dtx_enable; + uint32_t frame_format; +}; +#endif /* _UAPI_MSM_AUDIO_AMRWB_H */ diff --git a/include/uapi/linux/msm_audio_amrwbplus.h b/include/uapi/linux/msm_audio_amrwbplus.h new file mode 100644 index 000000000000..ba2d06e99aa1 --- /dev/null +++ b/include/uapi/linux/msm_audio_amrwbplus.h @@ -0,0 +1,18 @@ +#ifndef _UAPI_MSM_AUDIO_AMR_WB_PLUS_H +#define _UAPI_MSM_AUDIO_AMR_WB_PLUS_H + +#define AUDIO_GET_AMRWBPLUS_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_amrwbplus_config_v2) +#define AUDIO_SET_AMRWBPLUS_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_amrwbplus_config_v2) + +struct msm_audio_amrwbplus_config_v2 { + unsigned int size_bytes; + unsigned int version; + unsigned int num_channels; + unsigned int amr_band_mode; + unsigned int amr_dtx_mode; + unsigned int amr_frame_fmt; + unsigned int amr_lsf_idx; +}; +#endif /* _UAPI_MSM_AUDIO_AMR_WB_PLUS_H */ diff --git a/include/uapi/linux/msm_audio_ape.h b/include/uapi/linux/msm_audio_ape.h new file mode 100644 index 000000000000..587d3bc1832d --- /dev/null +++ b/include/uapi/linux/msm_audio_ape.h @@ -0,0 +1,26 @@ +/* The following structure has been taken + * from Monkey's Audio SDK with permission + */ + +#ifndef _UAPI_MSM_AUDIO_APE_H +#define _UAPI_MSM_AUDIO_APE_H + +#define AUDIO_GET_APE_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config) +#define AUDIO_SET_APE_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config) + +struct msm_audio_ape_config { + uint16_t compatibleVersion; + uint16_t compressionLevel; + uint32_t formatFlags; + uint32_t blocksPerFrame; + uint32_t finalFrameBlocks; + uint32_t totalFrames; + uint16_t bitsPerSample; + uint16_t numChannels; + uint32_t sampleRate; + uint32_t seekTablePresent; +}; + +#endif /* _UAPI_MSM_AUDIO_APE_H */ diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h new file mode 100644 index 000000000000..11af32ecc4fa --- /dev/null +++ b/include/uapi/linux/msm_audio_calibration.h @@ -0,0 +1,692 @@ +#ifndef _UAPI_MSM_AUDIO_CALIBRATION_H +#define _UAPI_MSM_AUDIO_CALIBRATION_H + +#include +#include + +#define CAL_IOCTL_MAGIC 'a' + +#define AUDIO_ALLOCATE_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 200, void *) +#define AUDIO_DEALLOCATE_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 201, void *) +#define AUDIO_PREPARE_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 202, void *) +#define AUDIO_SET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 203, void *) +#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 204, void *) +#define AUDIO_POST_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 205, void *) + +/* For Real-Time Audio Calibration */ +#define AUDIO_GET_RTAC_ADM_INFO _IOR(CAL_IOCTL_MAGIC, \ + 207, void *) +#define AUDIO_GET_RTAC_VOICE_INFO _IOR(CAL_IOCTL_MAGIC, \ + 208, void *) +#define AUDIO_GET_RTAC_ADM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 209, void *) +#define AUDIO_SET_RTAC_ADM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 210, void *) +#define AUDIO_GET_RTAC_ASM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 211, void *) +#define AUDIO_SET_RTAC_ASM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 212, void *) +#define AUDIO_GET_RTAC_CVS_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 213, void *) +#define AUDIO_SET_RTAC_CVS_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 214, void *) +#define AUDIO_GET_RTAC_CVP_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 215, void *) +#define AUDIO_SET_RTAC_CVP_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 216, void *) +#define AUDIO_GET_RTAC_AFE_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 217, void *) +#define AUDIO_SET_RTAC_AFE_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 218, void *) +enum { + CVP_VOC_RX_TOPOLOGY_CAL_TYPE = 0, + CVP_VOC_TX_TOPOLOGY_CAL_TYPE, + CVP_VOCPROC_STATIC_CAL_TYPE, + CVP_VOCPROC_DYNAMIC_CAL_TYPE, + CVS_VOCSTRM_STATIC_CAL_TYPE, + CVP_VOCDEV_CFG_CAL_TYPE, + CVP_VOCPROC_STATIC_COL_CAL_TYPE, + CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE, + CVS_VOCSTRM_STATIC_COL_CAL_TYPE, + + ADM_TOPOLOGY_CAL_TYPE, + ADM_CUST_TOPOLOGY_CAL_TYPE, + ADM_AUDPROC_CAL_TYPE, + ADM_AUDVOL_CAL_TYPE, + + ASM_TOPOLOGY_CAL_TYPE, + ASM_CUST_TOPOLOGY_CAL_TYPE, + ASM_AUDSTRM_CAL_TYPE, + + AFE_COMMON_RX_CAL_TYPE, + AFE_COMMON_TX_CAL_TYPE, + AFE_ANC_CAL_TYPE, + AFE_AANC_CAL_TYPE, + AFE_FB_SPKR_PROT_CAL_TYPE, + AFE_HW_DELAY_CAL_TYPE, + AFE_SIDETONE_CAL_TYPE, + AFE_TOPOLOGY_CAL_TYPE, + AFE_CUST_TOPOLOGY_CAL_TYPE, + + LSM_CUST_TOPOLOGY_CAL_TYPE, + LSM_TOPOLOGY_CAL_TYPE, + LSM_CAL_TYPE, + + ADM_RTAC_INFO_CAL_TYPE, + VOICE_RTAC_INFO_CAL_TYPE, + ADM_RTAC_APR_CAL_TYPE, + ASM_RTAC_APR_CAL_TYPE, + VOICE_RTAC_APR_CAL_TYPE, + + MAD_CAL_TYPE, + ULP_AFE_CAL_TYPE, + ULP_LSM_CAL_TYPE, + + DTS_EAGLE_CAL_TYPE, + AUDIO_CORE_METAINFO_CAL_TYPE, + SRS_TRUMEDIA_CAL_TYPE, + + CORE_CUSTOM_TOPOLOGIES_CAL_TYPE, + ADM_RTAC_AUDVOL_CAL_TYPE, + + ULP_LSM_TOPOLOGY_ID_CAL_TYPE, + AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE, + AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE, + MAX_CAL_TYPES, +}; + +#define AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE +#define AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE + +enum { + VERSION_0_0, +}; + +enum { + PER_VOCODER_CAL_BIT_MASK = 0x10000, +}; + +#define MAX_IOCTL_CMD_SIZE 512 + +/* common structures */ + +struct audio_cal_header { + int32_t data_size; + int32_t version; + int32_t cal_type; + int32_t cal_type_size; +}; + +struct audio_cal_type_header { + int32_t version; + int32_t buffer_number; +}; + +struct audio_cal_data { + /* Size of cal data at mem_handle allocation or at vaddr */ + int32_t cal_size; + /* If mem_handle if shared memory is used*/ + int32_t mem_handle; + /* size of virtual memory if shared memory not used */ +}; + + +/* AUDIO_ALLOCATE_CALIBRATION */ +struct audio_cal_type_alloc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_alloc { + struct audio_cal_header hdr; + struct audio_cal_type_alloc cal_type; +}; + + +/* AUDIO_DEALLOCATE_CALIBRATION */ +struct audio_cal_type_dealloc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_dealloc { + struct audio_cal_header hdr; + struct audio_cal_type_dealloc cal_type; +}; + + +/* AUDIO_PREPARE_CALIBRATION */ +struct audio_cal_type_prepare { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_prepare { + struct audio_cal_header hdr; + struct audio_cal_type_prepare cal_type; +}; + + +/* AUDIO_POST_CALIBRATION */ +struct audio_cal_type_post { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_post { + struct audio_cal_header hdr; + struct audio_cal_type_post cal_type; +}; + +/*AUDIO_CORE_META_INFO */ + +struct audio_cal_info_metainfo { + uint32_t nKey; +}; + +/* Cal info types */ +enum { + RX_DEVICE, + TX_DEVICE, + MAX_PATH_TYPE +}; + +struct audio_cal_info_adm_top { + int32_t topology; + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; + int32_t sample_rate; +}; + +struct audio_cal_info_audproc { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; + int32_t sample_rate; +}; + +struct audio_cal_info_audvol { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; + int32_t vol_index; +}; + +struct audio_cal_info_afe { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t sample_rate; +}; + +struct audio_cal_info_afe_top { + int32_t topology; + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t sample_rate; +}; + +struct audio_cal_info_asm_top { + int32_t topology; + int32_t app_type; +}; + +struct audio_cal_info_audstrm { + int32_t app_type; +}; + +struct audio_cal_info_aanc { + int32_t acdb_id; +}; + +#define MAX_HW_DELAY_ENTRIES 25 + +struct audio_cal_hw_delay_entry { + uint32_t sample_rate; + uint32_t delay_usec; +}; + +struct audio_cal_hw_delay_data { + uint32_t num_entries; + struct audio_cal_hw_delay_entry entry[MAX_HW_DELAY_ENTRIES]; +}; + +struct audio_cal_info_hw_delay { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t property_type; + struct audio_cal_hw_delay_data data; +}; + +enum msm_spkr_prot_states { + MSM_SPKR_PROT_CALIBRATED, + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS, + MSM_SPKR_PROT_DISABLED, + MSM_SPKR_PROT_NOT_CALIBRATED, + MSM_SPKR_PROT_PRE_CALIBRATED, + MSM_SPKR_PROT_IN_FTM_MODE +}; +#define MSM_SPKR_PROT_IN_FTM_MODE MSM_SPKR_PROT_IN_FTM_MODE + +enum msm_spkr_count { + SP_V2_SPKR_1, + SP_V2_SPKR_2, + SP_V2_NUM_MAX_SPKRS +}; + +struct audio_cal_info_spk_prot_cfg { + int32_t r0[SP_V2_NUM_MAX_SPKRS]; + int32_t t0[SP_V2_NUM_MAX_SPKRS]; + uint32_t quick_calib_flag; + uint32_t mode; + /* + * 0 - Start spk prot + * 1 - Start calib + * 2 - Disable spk prot + */ +}; + +struct audio_cal_info_sp_th_vi_ftm_cfg { + uint32_t wait_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t ftm_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t mode; + /* + * 0 - normal running mode + * 1 - Calibration + * 2 - FTM mode + */ +}; + +struct audio_cal_info_sp_ex_vi_ftm_cfg { + uint32_t wait_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t ftm_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t mode; + /* + * 0 - normal running mode + * 2 - FTM mode + */ +}; + +struct audio_cal_info_sp_ex_vi_param { + int32_t freq_q20[SP_V2_NUM_MAX_SPKRS]; + int32_t resis_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t qmct_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t status[SP_V2_NUM_MAX_SPKRS]; +}; + +struct audio_cal_info_sp_th_vi_param { + int32_t r_dc_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t temp_q22[SP_V2_NUM_MAX_SPKRS]; + int32_t status[SP_V2_NUM_MAX_SPKRS]; +}; + +struct audio_cal_info_msm_spk_prot_status { + int32_t r0[SP_V2_NUM_MAX_SPKRS]; + int32_t status; +}; + +struct audio_cal_info_sidetone { + uint16_t enable; + uint16_t gain; + int32_t tx_acdb_id; + int32_t rx_acdb_id; + int32_t mid; + int32_t pid; +}; + +struct audio_cal_info_lsm_top { + int32_t topology; + int32_t acdb_id; + int32_t app_type; +}; + + +struct audio_cal_info_lsm { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; +}; + +struct audio_cal_info_voc_top { + int32_t topology; + int32_t acdb_id; +}; + +struct audio_cal_info_vocproc { + int32_t tx_acdb_id; + int32_t rx_acdb_id; + int32_t tx_sample_rate; + int32_t rx_sample_rate; +}; + +enum { + DEFAULT_FEATURE_SET, + VOL_BOOST_FEATURE_SET, +}; + +struct audio_cal_info_vocvol { + int32_t tx_acdb_id; + int32_t rx_acdb_id; + /* DEFAULT_ or VOL_BOOST_FEATURE_SET */ + int32_t feature_set; +}; + +struct audio_cal_info_vocdev_cfg { + int32_t tx_acdb_id; + int32_t rx_acdb_id; +}; + +#define MAX_VOICE_COLUMNS 20 + +union audio_cal_col_na { + uint8_t val8; + uint16_t val16; + uint32_t val32; + uint64_t val64; +} __packed; + +struct audio_cal_col { + uint32_t id; + uint32_t type; + union audio_cal_col_na na_value; +} __packed; + +struct audio_cal_col_data { + uint32_t num_columns; + struct audio_cal_col column[MAX_VOICE_COLUMNS]; +} __packed; + +struct audio_cal_info_voc_col { + int32_t table_id; + int32_t tx_acdb_id; + int32_t rx_acdb_id; + struct audio_cal_col_data data; +}; + +/* AUDIO_SET_CALIBRATION & */ +struct audio_cal_type_basic { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_basic { + struct audio_cal_header hdr; + struct audio_cal_type_basic cal_type; +}; + +struct audio_cal_type_adm_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_adm_top cal_info; +}; + +struct audio_cal_adm_top { + struct audio_cal_header hdr; + struct audio_cal_type_adm_top cal_type; +}; + +struct audio_cal_type_metainfo { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_metainfo cal_info; +}; + +struct audio_core_metainfo { + struct audio_cal_header hdr; + struct audio_cal_type_metainfo cal_type; +}; + +struct audio_cal_type_audproc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_audproc cal_info; +}; + +struct audio_cal_audproc { + struct audio_cal_header hdr; + struct audio_cal_type_audproc cal_type; +}; + +struct audio_cal_type_audvol { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_audvol cal_info; +}; + +struct audio_cal_audvol { + struct audio_cal_header hdr; + struct audio_cal_type_audvol cal_type; +}; + +struct audio_cal_type_asm_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_asm_top cal_info; +}; + +struct audio_cal_asm_top { + struct audio_cal_header hdr; + struct audio_cal_type_asm_top cal_type; +}; + +struct audio_cal_type_audstrm { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_audstrm cal_info; +}; + +struct audio_cal_audstrm { + struct audio_cal_header hdr; + struct audio_cal_type_audstrm cal_type; +}; + +struct audio_cal_type_afe { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_afe cal_info; +}; + +struct audio_cal_afe { + struct audio_cal_header hdr; + struct audio_cal_type_afe cal_type; +}; + +struct audio_cal_type_afe_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_afe_top cal_info; +}; + +struct audio_cal_afe_top { + struct audio_cal_header hdr; + struct audio_cal_type_afe_top cal_type; +}; + +struct audio_cal_type_aanc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_aanc cal_info; +}; + +struct audio_cal_aanc { + struct audio_cal_header hdr; + struct audio_cal_type_aanc cal_type; +}; + +struct audio_cal_type_fb_spk_prot_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_spk_prot_cfg cal_info; +}; + +struct audio_cal_fb_spk_prot_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_fb_spk_prot_cfg cal_type; +}; + +struct audio_cal_type_sp_th_vi_ftm_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_th_vi_ftm_cfg cal_info; +}; + +struct audio_cal_sp_th_vi_ftm_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_sp_th_vi_ftm_cfg cal_type; +}; + +struct audio_cal_type_sp_ex_vi_ftm_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_ex_vi_ftm_cfg cal_info; +}; + +struct audio_cal_sp_ex_vi_ftm_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_sp_ex_vi_ftm_cfg cal_type; +}; +struct audio_cal_type_hw_delay { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_hw_delay cal_info; +}; + +struct audio_cal_hw_delay { + struct audio_cal_header hdr; + struct audio_cal_type_hw_delay cal_type; +}; + +struct audio_cal_type_sidetone { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sidetone cal_info; +}; + +struct audio_cal_sidetone { + struct audio_cal_header hdr; + struct audio_cal_type_sidetone cal_type; +}; + +struct audio_cal_type_lsm_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_lsm_top cal_info; +}; + +struct audio_cal_lsm_top { + struct audio_cal_header hdr; + struct audio_cal_type_lsm_top cal_type; +}; + +struct audio_cal_type_lsm { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_lsm cal_info; +}; + +struct audio_cal_lsm { + struct audio_cal_header hdr; + struct audio_cal_type_lsm cal_type; +}; + +struct audio_cal_type_voc_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_voc_top cal_info; +}; + +struct audio_cal_voc_top { + struct audio_cal_header hdr; + struct audio_cal_type_voc_top cal_type; +}; + +struct audio_cal_type_vocproc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_vocproc cal_info; +}; + +struct audio_cal_vocproc { + struct audio_cal_header hdr; + struct audio_cal_type_vocproc cal_type; +}; + +struct audio_cal_type_vocvol { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_vocvol cal_info; +}; + +struct audio_cal_vocvol { + struct audio_cal_header hdr; + struct audio_cal_type_vocvol cal_type; +}; + +struct audio_cal_type_vocdev_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_vocdev_cfg cal_info; +}; + +struct audio_cal_vocdev_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_vocdev_cfg cal_type; +}; + +struct audio_cal_type_voc_col { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_voc_col cal_info; +}; + +struct audio_cal_voc_col { + struct audio_cal_header hdr; + struct audio_cal_type_voc_col cal_type; +}; + +/* AUDIO_GET_CALIBRATION */ +struct audio_cal_type_fb_spk_prot_status { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_msm_spk_prot_status cal_info; +}; + +struct audio_cal_fb_spk_prot_status { + struct audio_cal_header hdr; + struct audio_cal_type_fb_spk_prot_status cal_type; +}; + +struct audio_cal_type_sp_th_vi_param { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_th_vi_param cal_info; +}; + +struct audio_cal_sp_th_vi_param { + struct audio_cal_header hdr; + struct audio_cal_type_sp_th_vi_param cal_type; +}; +struct audio_cal_type_sp_ex_vi_param { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_ex_vi_param cal_info; +}; + +struct audio_cal_sp_ex_vi_param { + struct audio_cal_header hdr; + struct audio_cal_type_sp_ex_vi_param cal_type; +}; +#endif /* _UAPI_MSM_AUDIO_CALIBRATION_H */ diff --git a/include/uapi/linux/msm_audio_g711.h b/include/uapi/linux/msm_audio_g711.h new file mode 100644 index 000000000000..48ebd6a1131e --- /dev/null +++ b/include/uapi/linux/msm_audio_g711.h @@ -0,0 +1,17 @@ +#ifndef _UAPI_MSM_AUDIO_G711_H +#define _UAPI_MSM_AUDIO_G711_H + +#include + +struct msm_audio_g711_enc_config { + uint32_t sample_rate; +}; + +#define AUDIO_SET_G711_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config) + +#define AUDIO_GET_G711_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config) + + +#endif /* _UAPI_MSM_AUDIO_G711_H */ diff --git a/include/uapi/linux/msm_audio_g711_dec.h b/include/uapi/linux/msm_audio_g711_dec.h new file mode 100644 index 000000000000..ff7e4ce39fd5 --- /dev/null +++ b/include/uapi/linux/msm_audio_g711_dec.h @@ -0,0 +1,16 @@ +#ifndef _UAPI_MSM_AUDIO_G711_H +#define _UAPI_MSM_AUDIO_G711_H + +#include + +struct msm_audio_g711_dec_config { + uint32_t sample_rate; +}; + +#define AUDIO_SET_G711_DEC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config) + +#define AUDIO_GET_G711_DEC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config) + +#endif /* _UAPI_MSM_AUDIO_G711_H */ diff --git a/include/uapi/linux/msm_audio_mvs.h b/include/uapi/linux/msm_audio_mvs.h new file mode 100644 index 000000000000..5b76bf99a701 --- /dev/null +++ b/include/uapi/linux/msm_audio_mvs.h @@ -0,0 +1,155 @@ +#ifndef _UAPI_MSM_AUDIO_MVS_H +#define _UAPI_MSM_AUDIO_MVS_H + +#include + +#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned int) +#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned int) + +/* MVS modes */ +#define MVS_MODE_IS733 0x1 /*QCELP 13K*/ +#define MVS_MODE_IS127 0x2 /*EVRC-8k*/ +#define MVS_MODE_4GV_NB 0x3 /*EVRC-B*/ +#define MVS_MODE_4GV_WB 0x4 /*EVRC-WB*/ +#define MVS_MODE_AMR 0x5 +#define MVS_MODE_EFR 0x6 +#define MVS_MODE_FR 0x7 +#define MVS_MODE_HR 0x8 +#define MVS_MODE_LINEAR_PCM 0x9 +#define MVS_MODE_G711 0xA +#define MVS_MODE_PCM 0xC +#define MVS_MODE_AMR_WB 0xD +#define MVS_MODE_G729A 0xE +#define MVS_MODE_G711A 0xF +#define MVS_MODE_G722 0x10 +#define MVS_MODE_PCM_WB 0x12 + +enum msm_audio_amr_mode { + MVS_AMR_MODE_0475, /* AMR 4.75 kbps */ + MVS_AMR_MODE_0515, /* AMR 5.15 kbps */ + MVS_AMR_MODE_0590, /* AMR 5.90 kbps */ + MVS_AMR_MODE_0670, /* AMR 6.70 kbps */ + MVS_AMR_MODE_0740, /* AMR 7.40 kbps */ + MVS_AMR_MODE_0795, /* AMR 7.95 kbps */ + MVS_AMR_MODE_1020, /* AMR 10.20 kbps */ + MVS_AMR_MODE_1220, /* AMR 12.20 kbps */ + MVS_AMR_MODE_0660, /* AMR-WB 6.60 kbps */ + MVS_AMR_MODE_0885, /* AMR-WB 8.85 kbps */ + MVS_AMR_MODE_1265, /* AMR-WB 12.65 kbps */ + MVS_AMR_MODE_1425, /* AMR-WB 14.25 kbps */ + MVS_AMR_MODE_1585, /* AMR-WB 15.85 kbps */ + MVS_AMR_MODE_1825, /* AMR-WB 18.25 kbps */ + MVS_AMR_MODE_1985, /* AMR-WB 19.85 kbps */ + MVS_AMR_MODE_2305, /* AMR-WB 23.05 kbps */ + MVS_AMR_MODE_2385, /* AMR-WB 23.85 kbps */ + MVS_AMR_MODE_UNDEF +}; + +/* The MVS VOC rate type is used to identify the rate of QCELP 13K(IS733), + * EVRC(IS127), 4GV, or 4GV-WB frame. + */ +enum msm_audio_voc_rate { + MVS_VOC_0_RATE, /* Blank frame */ + MVS_VOC_8_RATE, /* 1/8 rate */ + MVS_VOC_4_RATE, /* 1/4 rate */ + MVS_VOC_2_RATE, /* 1/2 rate */ + MVS_VOC_1_RATE, /* Full rate */ + MVS_VOC_ERASURE, /* erasure frame */ + MVS_VOC_RATE_MAX, + MVS_VOC_RATE_UNDEF = MVS_VOC_RATE_MAX +}; + +enum msm_audio_amr_frame_type { + MVS_AMR_SPEECH_GOOD, /* Good speech frame */ + MVS_AMR_SPEECH_DEGRADED, /* Speech degraded */ + MVS_AMR_ONSET, /* Onset */ + MVS_AMR_SPEECH_BAD, /* Corrupt speech frame (bad CRC) */ + MVS_AMR_SID_FIRST, /* First silence descriptor */ + MVS_AMR_SID_UPDATE, /* Comfort noise frame */ + MVS_AMR_SID_BAD, /* Corrupt SID frame (bad CRC) */ + MVS_AMR_NO_DATA, /* Nothing to transmit */ + MVS_AMR_SPEECH_LOST /* Downlink speech lost */ +}; + +enum msm_audio_g711a_mode { + MVS_G711A_MODE_MULAW, + MVS_G711A_MODE_ALAW +}; + +enum msm_audio_g711_mode { + MVS_G711_MODE_MULAW, + MVS_G711_MODE_ALAW +}; + +enum mvs_g722_mode_type { + MVS_G722_MODE_01, + MVS_G722_MODE_02, + MVS_G722_MODE_03, + MVS_G722_MODE_MAX, + MVS_G722_MODE_UNDEF +}; + +enum msm_audio_g711a_frame_type { + MVS_G711A_SPEECH_GOOD, + MVS_G711A_SID, + MVS_G711A_NO_DATA, + MVS_G711A_ERASURE +}; + +enum msm_audio_g729a_frame_type { + MVS_G729A_NO_DATA, + MVS_G729A_SPEECH_GOOD, + MVS_G729A_SID, + MVS_G729A_ERASURE +}; + +struct min_max_rate { + uint32_t min_rate; + uint32_t max_rate; +}; + +struct msm_audio_mvs_config { + uint32_t mvs_mode; + uint32_t rate_type; + struct min_max_rate min_max_rate; + uint32_t dtx_mode; +}; + +#define MVS_MAX_VOC_PKT_SIZE 640 + +struct gsm_header { + uint8_t bfi; + uint8_t sid; + uint8_t taf; + uint8_t ufi; +}; + +struct q6_msm_audio_mvs_frame { + union { + uint32_t frame_type; + uint32_t packet_rate; + struct gsm_header gsm_frame_type; + } header; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +struct msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +#define Q5V2_MVS_MAX_VOC_PKT_SIZE 320 + +struct q5v2_msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t len; + uint8_t voc_pkt[Q5V2_MVS_MAX_VOC_PKT_SIZE]; + +}; +#endif /* _UAPI_MSM_AUDIO_MVS_H */ diff --git a/include/uapi/linux/msm_audio_qcp.h b/include/uapi/linux/msm_audio_qcp.h new file mode 100644 index 000000000000..fdb234e91acf --- /dev/null +++ b/include/uapi/linux/msm_audio_qcp.h @@ -0,0 +1,37 @@ +#ifndef _UAPI_MSM_AUDIO_QCP_H +#define _UAPI_MSM_AUDIO_QCP_H + +#include + +#define AUDIO_SET_QCELP_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + 0, struct msm_audio_qcelp_enc_config) + +#define AUDIO_GET_QCELP_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + 1, struct msm_audio_qcelp_enc_config) + +#define AUDIO_SET_EVRC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + 2, struct msm_audio_evrc_enc_config) + +#define AUDIO_GET_EVRC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + 3, struct msm_audio_evrc_enc_config) + +#define CDMA_RATE_BLANK 0x00 +#define CDMA_RATE_EIGHTH 0x01 +#define CDMA_RATE_QUARTER 0x02 +#define CDMA_RATE_HALF 0x03 +#define CDMA_RATE_FULL 0x04 +#define CDMA_RATE_ERASURE 0x05 + +struct msm_audio_qcelp_enc_config { + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +struct msm_audio_evrc_enc_config { + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +#endif /* _UAPI_MSM_AUDIO_QCP_H */ diff --git a/include/uapi/linux/msm_audio_sbc.h b/include/uapi/linux/msm_audio_sbc.h new file mode 100644 index 000000000000..1c7c63da3da7 --- /dev/null +++ b/include/uapi/linux/msm_audio_sbc.h @@ -0,0 +1,36 @@ +#ifndef _UAPI_MSM_AUDIO_SBC_H +#define _UAPI_MSM_AUDIO_SBC_H + +#include + +#define AUDIO_SET_SBC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_sbc_enc_config) + +#define AUDIO_GET_SBC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_sbc_enc_config) + +#define AUDIO_SBC_BA_LOUDNESS 0x0 +#define AUDIO_SBC_BA_SNR 0x1 + +#define AUDIO_SBC_MODE_MONO 0x0 +#define AUDIO_SBC_MODE_DUAL 0x1 +#define AUDIO_SBC_MODE_STEREO 0x2 +#define AUDIO_SBC_MODE_JSTEREO 0x3 + +#define AUDIO_SBC_BANDS_8 0x1 + +#define AUDIO_SBC_BLOCKS_4 0x0 +#define AUDIO_SBC_BLOCKS_8 0x1 +#define AUDIO_SBC_BLOCKS_12 0x2 +#define AUDIO_SBC_BLOCKS_16 0x3 + +struct msm_audio_sbc_enc_config { + uint32_t channels; + uint32_t sample_rate; + uint32_t bit_allocation; + uint32_t number_of_subbands; + uint32_t number_of_blocks; + uint32_t bit_rate; + uint32_t mode; +}; +#endif /* _UAPI_MSM_AUDIO_SBC_H */ diff --git a/include/uapi/linux/msm_audio_voicememo.h b/include/uapi/linux/msm_audio_voicememo.h new file mode 100644 index 000000000000..a7a7a4df17d5 --- /dev/null +++ b/include/uapi/linux/msm_audio_voicememo.h @@ -0,0 +1,66 @@ +#ifndef _UAPI_MSM_AUDIO_VOICEMEMO_H +#define _UAPI_MSM_AUDIO_VOICEMEMO_H + +#include + +#define AUDIO_GET_VOICEMEMO_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_SET_VOICEMEMO_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) + +/* rec_type */ +enum rpc_voc_rec_dir_type { + RPC_VOC_REC_NONE, + RPC_VOC_REC_FORWARD, + RPC_VOC_REC_REVERSE, + RPC_VOC_REC_BOTH, + RPC_VOC_MAX_REC_TYPE +}; + +/* capability */ +enum rpc_voc_capability_type { + RPC_VOC_CAP_IS733 = 4, + RPC_VOC_CAP_IS127 = 8, + RPC_VOC_CAP_AMR = 64, + RPC_VOC_CAP_32BIT_DUMMY = 2147483647 +}; + +/* Rate */ +enum rpc_voc_rate_type { + RPC_VOC_0_RATE = 0, + RPC_VOC_8_RATE, + RPC_VOC_4_RATE, + RPC_VOC_2_RATE, + RPC_VOC_1_RATE, + RPC_VOC_ERASURE, + RPC_VOC_ERR_RATE, + RPC_VOC_AMR_RATE_475 = 0, + RPC_VOC_AMR_RATE_515 = 1, + RPC_VOC_AMR_RATE_590 = 2, + RPC_VOC_AMR_RATE_670 = 3, + RPC_VOC_AMR_RATE_740 = 4, + RPC_VOC_AMR_RATE_795 = 5, + RPC_VOC_AMR_RATE_1020 = 6, + RPC_VOC_AMR_RATE_1220 = 7, +}; + +/* frame_format */ +enum rpc_voc_pb_len_rate_var_type { + RPC_VOC_PB_NATIVE_QCP = 3, + RPC_VOC_PB_AMR, + RPC_VOC_PB_EVB +}; + +struct msm_audio_voicememo_config { + uint32_t rec_type; + uint32_t rec_interval_ms; + uint32_t auto_stop_ms; + uint32_t capability; + uint32_t max_rate; + uint32_t min_rate; + uint32_t frame_format; + uint32_t dtx_enable; + uint32_t data_req_ms; +}; + +#endif /* _UAPI_MSM_AUDIO_VOICEMEMO_H */ diff --git a/include/uapi/linux/msm_audio_wma.h b/include/uapi/linux/msm_audio_wma.h new file mode 100644 index 000000000000..523fadef08d1 --- /dev/null +++ b/include/uapi/linux/msm_audio_wma.h @@ -0,0 +1,33 @@ +#ifndef _UAPI_MSM_AUDIO_WMA_H +#define _UAPI_MSM_AUDIO_WMA_H + +#define AUDIO_GET_WMA_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_SET_WMA_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) + +#define AUDIO_GET_WMA_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2) +#define AUDIO_SET_WMA_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2) + +struct msm_audio_wma_config { + unsigned short armdatareqthr; + unsigned short channelsdecoded; + unsigned short wmabytespersec; + unsigned short wmasamplingfreq; + unsigned short wmaencoderopts; +}; + +struct msm_audio_wma_config_v2 { + unsigned short format_tag; + unsigned short numchannels; + uint32_t samplingrate; + uint32_t avgbytespersecond; + unsigned short block_align; + unsigned short validbitspersample; + uint32_t channelmask; + unsigned short encodeopt; +}; + +#endif /* _UAPI_MSM_AUDIO_WMA_H */ diff --git a/include/uapi/linux/msm_audio_wmapro.h b/include/uapi/linux/msm_audio_wmapro.h new file mode 100644 index 000000000000..64cbf9e079d6 --- /dev/null +++ b/include/uapi/linux/msm_audio_wmapro.h @@ -0,0 +1,22 @@ +#ifndef _UAPI_MSM_AUDIO_WMAPRO_H +#define _UAPI_MSM_AUDIO_WMAPRO_H + +#define AUDIO_GET_WMAPRO_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config) +#define AUDIO_SET_WMAPRO_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config) + +struct msm_audio_wmapro_config { + unsigned short armdatareqthr; + uint8_t validbitspersample; + uint8_t numchannels; + unsigned short formattag; + uint32_t samplingrate; + uint32_t avgbytespersecond; + unsigned short asfpacketlength; + uint32_t channelmask; + unsigned short encodeopt; + unsigned short advancedencodeopt; + uint32_t advancedencodeopt2; +}; +#endif /* _UAPI_MSM_AUDIO_WMAPRO_H */ diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild index 9578d8bdbf31..5b0a9bc03b5d 100644 --- a/include/uapi/sound/Kbuild +++ b/include/uapi/sound/Kbuild @@ -14,3 +14,10 @@ header-y += sfnt_info.h header-y += tlv.h header-y += usb_stream.h header-y += snd_sst_tokens.h +header-y += lsm_params.h +header-y += audio_slimslave.h +header-y += voice_params.h +header-y += audio_effects.h +header-y += voice_svc.h +header-y += devdep_params.h +header-y += msmcal-hwdep.h diff --git a/include/uapi/sound/audio_effects.h b/include/uapi/sound/audio_effects.h new file mode 100644 index 000000000000..063b5c1a8b57 --- /dev/null +++ b/include/uapi/sound/audio_effects.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _AUDIO_EFFECTS_H +#define _AUDIO_EFFECTS_H + +/** AUDIO EFFECTS **/ + + +/* CONFIG GET/SET */ +#define CONFIG_CACHE 0 +#define CONFIG_SET 1 +#define CONFIG_GET 2 + +/* CONFIG HEADER */ +/* + * MODULE_ID, + * DEVICE, + * NUM_COMMANDS, + * COMMAND_ID_1, + * CONFIG_CACHE/SET/GET, + * OFFSET_1, + * LENGTH_1, + * VALUES_1, + * ..., + * ..., + * COMMAND_ID_2, + * CONFIG_CACHE/SET/GET, + * OFFSET_2, + * LENGTH_2, + * VALUES_2, + * ..., + * ..., + * COMMAND_ID_3, + * ... + */ + + +/* CONFIG PARAM IDs */ +#define VIRTUALIZER_MODULE 0x00001000 +#define VIRTUALIZER_ENABLE 0x00001001 +#define VIRTUALIZER_STRENGTH 0x00001002 +#define VIRTUALIZER_OUT_TYPE 0x00001003 +#define VIRTUALIZER_GAIN_ADJUST 0x00001004 +#define VIRTUALIZER_ENABLE_PARAM_LEN 1 +#define VIRTUALIZER_STRENGTH_PARAM_LEN 1 +#define VIRTUALIZER_OUT_TYPE_PARAM_LEN 1 +#define VIRTUALIZER_GAIN_ADJUST_PARAM_LEN 1 + +#define REVERB_MODULE 0x00002000 +#define REVERB_ENABLE 0x00002001 +#define REVERB_MODE 0x00002002 +#define REVERB_PRESET 0x00002003 +#define REVERB_WET_MIX 0x00002004 +#define REVERB_GAIN_ADJUST 0x00002005 +#define REVERB_ROOM_LEVEL 0x00002006 +#define REVERB_ROOM_HF_LEVEL 0x00002007 +#define REVERB_DECAY_TIME 0x00002008 +#define REVERB_DECAY_HF_RATIO 0x00002009 +#define REVERB_REFLECTIONS_LEVEL 0x0000200a +#define REVERB_REFLECTIONS_DELAY 0x0000200b +#define REVERB_LEVEL 0x0000200c +#define REVERB_DELAY 0x0000200d +#define REVERB_DIFFUSION 0x0000200e +#define REVERB_DENSITY 0x0000200f +#define REVERB_ENABLE_PARAM_LEN 1 +#define REVERB_MODE_PARAM_LEN 1 +#define REVERB_PRESET_PARAM_LEN 1 +#define REVERB_WET_MIX_PARAM_LEN 1 +#define REVERB_GAIN_ADJUST_PARAM_LEN 1 +#define REVERB_ROOM_LEVEL_PARAM_LEN 1 +#define REVERB_ROOM_HF_LEVEL_PARAM_LEN 1 +#define REVERB_DECAY_TIME_PARAM_LEN 1 +#define REVERB_DECAY_HF_RATIO_PARAM_LEN 1 +#define REVERB_REFLECTIONS_LEVEL_PARAM_LEN 1 +#define REVERB_REFLECTIONS_DELAY_PARAM_LEN 1 +#define REVERB_LEVEL_PARAM_LEN 1 +#define REVERB_DELAY_PARAM_LEN 1 +#define REVERB_DIFFUSION_PARAM_LEN 1 +#define REVERB_DENSITY_PARAM_LEN 1 + +#define BASS_BOOST_MODULE 0x00003000 +#define BASS_BOOST_ENABLE 0x00003001 +#define BASS_BOOST_MODE 0x00003002 +#define BASS_BOOST_STRENGTH 0x00003003 +#define BASS_BOOST_ENABLE_PARAM_LEN 1 +#define BASS_BOOST_MODE_PARAM_LEN 1 +#define BASS_BOOST_STRENGTH_PARAM_LEN 1 + +#define EQ_MODULE 0x00004000 +#define EQ_ENABLE 0x00004001 +#define EQ_CONFIG 0x00004002 +#define EQ_NUM_BANDS 0x00004003 +#define EQ_BAND_LEVELS 0x00004004 +#define EQ_BAND_LEVEL_RANGE 0x00004005 +#define EQ_BAND_FREQS 0x00004006 +#define EQ_SINGLE_BAND_FREQ_RANGE 0x00004007 +#define EQ_SINGLE_BAND_FREQ 0x00004008 +#define EQ_BAND_INDEX 0x00004009 +#define EQ_PRESET_ID 0x0000400a +#define EQ_NUM_PRESETS 0x0000400b +#define EQ_PRESET_NAME 0x0000400c +#define EQ_ENABLE_PARAM_LEN 1 +#define EQ_CONFIG_PARAM_LEN 3 +#define EQ_CONFIG_PER_BAND_PARAM_LEN 5 +#define EQ_NUM_BANDS_PARAM_LEN 1 +#define EQ_BAND_LEVELS_PARAM_LEN 13 +#define EQ_BAND_LEVEL_RANGE_PARAM_LEN 2 +#define EQ_BAND_FREQS_PARAM_LEN 13 +#define EQ_SINGLE_BAND_FREQ_RANGE_PARAM_LEN 2 +#define EQ_SINGLE_BAND_FREQ_PARAM_LEN 1 +#define EQ_BAND_INDEX_PARAM_LEN 1 +#define EQ_PRESET_ID_PARAM_LEN 1 +#define EQ_NUM_PRESETS_PARAM_LEN 1 +#define EQ_PRESET_NAME_PARAM_LEN 32 + +#define EQ_TYPE_NONE 0 +#define EQ_BASS_BOOST 1 +#define EQ_BASS_CUT 2 +#define EQ_TREBLE_BOOST 3 +#define EQ_TREBLE_CUT 4 +#define EQ_BAND_BOOST 5 +#define EQ_BAND_CUT 6 + +#define SOFT_VOLUME_MODULE 0x00006000 +#define SOFT_VOLUME_ENABLE 0x00006001 +#define SOFT_VOLUME_GAIN_2CH 0x00006002 +#define SOFT_VOLUME_GAIN_MASTER 0x00006003 +#define SOFT_VOLUME_ENABLE_PARAM_LEN 1 +#define SOFT_VOLUME_GAIN_2CH_PARAM_LEN 2 +#define SOFT_VOLUME_GAIN_MASTER_PARAM_LEN 1 + +#define SOFT_VOLUME2_MODULE 0x00007000 +#define SOFT_VOLUME2_ENABLE 0x00007001 +#define SOFT_VOLUME2_GAIN_2CH 0x00007002 +#define SOFT_VOLUME2_GAIN_MASTER 0x00007003 +#define SOFT_VOLUME2_ENABLE_PARAM_LEN SOFT_VOLUME_ENABLE_PARAM_LEN +#define SOFT_VOLUME2_GAIN_2CH_PARAM_LEN SOFT_VOLUME_GAIN_2CH_PARAM_LEN +#define SOFT_VOLUME2_GAIN_MASTER_PARAM_LEN \ + SOFT_VOLUME_GAIN_MASTER_PARAM_LEN + +#define PBE_CONF_MODULE_ID 0x00010C2A +#define PBE_CONF_PARAM_ID 0x00010C49 + +#define PBE_MODULE 0x00008000 +#define PBE_ENABLE 0x00008001 +#define PBE_CONFIG 0x00008002 +#define PBE_ENABLE_PARAM_LEN 1 +#define PBE_CONFIG_PARAM_LEN 28 + +#define COMMAND_PAYLOAD_LEN 3 +#define COMMAND_PAYLOAD_SZ (COMMAND_PAYLOAD_LEN * sizeof(uint32_t)) +#define MAX_INBAND_PARAM_SZ 4096 +#define Q27_UNITY (1 << 27) +#define Q8_UNITY (1 << 8) +#define CUSTOM_OPENSL_PRESET 18 + +#define VIRTUALIZER_ENABLE_PARAM_SZ \ + (VIRTUALIZER_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define VIRTUALIZER_STRENGTH_PARAM_SZ \ + (VIRTUALIZER_STRENGTH_PARAM_LEN*sizeof(uint32_t)) +#define VIRTUALIZER_OUT_TYPE_PARAM_SZ \ + (VIRTUALIZER_OUT_TYPE_PARAM_LEN*sizeof(uint32_t)) +#define VIRTUALIZER_GAIN_ADJUST_PARAM_SZ \ + (VIRTUALIZER_GAIN_ADJUST_PARAM_LEN*sizeof(uint32_t)) +struct virtualizer_params { + uint32_t device; + uint32_t enable_flag; + uint32_t strength; + uint32_t out_type; + int32_t gain_adjust; +}; + +#define NUM_OSL_REVERB_PRESETS_SUPPORTED 6 +#define REVERB_ENABLE_PARAM_SZ \ + (REVERB_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_MODE_PARAM_SZ \ + (REVERB_MODE_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_PRESET_PARAM_SZ \ + (REVERB_PRESET_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_WET_MIX_PARAM_SZ \ + (REVERB_WET_MIX_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_GAIN_ADJUST_PARAM_SZ \ + (REVERB_GAIN_ADJUST_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_ROOM_LEVEL_PARAM_SZ \ + (REVERB_ROOM_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_ROOM_HF_LEVEL_PARAM_SZ \ + (REVERB_ROOM_HF_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DECAY_TIME_PARAM_SZ \ + (REVERB_DECAY_TIME_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DECAY_HF_RATIO_PARAM_SZ \ + (REVERB_DECAY_HF_RATIO_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_REFLECTIONS_LEVEL_PARAM_SZ \ + (REVERB_REFLECTIONS_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_REFLECTIONS_DELAY_PARAM_SZ \ + (REVERB_REFLECTIONS_DELAY_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_LEVEL_PARAM_SZ \ + (REVERB_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DELAY_PARAM_SZ \ + (REVERB_DELAY_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DIFFUSION_PARAM_SZ \ + (REVERB_DIFFUSION_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DENSITY_PARAM_SZ \ + (REVERB_DENSITY_PARAM_LEN*sizeof(uint32_t)) +struct reverb_params { + uint32_t device; + uint32_t enable_flag; + uint32_t mode; + uint32_t preset; + uint32_t wet_mix; + int32_t gain_adjust; + int32_t room_level; + int32_t room_hf_level; + uint32_t decay_time; + uint32_t decay_hf_ratio; + int32_t reflections_level; + uint32_t reflections_delay; + int32_t level; + uint32_t delay; + uint32_t diffusion; + uint32_t density; +}; + +#define BASS_BOOST_ENABLE_PARAM_SZ \ + (BASS_BOOST_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define BASS_BOOST_MODE_PARAM_SZ \ + (BASS_BOOST_MODE_PARAM_LEN*sizeof(uint32_t)) +#define BASS_BOOST_STRENGTH_PARAM_SZ \ + (BASS_BOOST_STRENGTH_PARAM_LEN*sizeof(uint32_t)) +struct bass_boost_params { + uint32_t device; + uint32_t enable_flag; + uint32_t mode; + uint32_t strength; +}; + + +#define MAX_EQ_BANDS 12 +#define MAX_OSL_EQ_BANDS 5 +#define EQ_ENABLE_PARAM_SZ \ + (EQ_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define EQ_CONFIG_PARAM_SZ \ + (EQ_CONFIG_PARAM_LEN*sizeof(uint32_t)) +#define EQ_CONFIG_PER_BAND_PARAM_SZ \ + (EQ_CONFIG_PER_BAND_PARAM_LEN*sizeof(uint32_t)) +#define EQ_CONFIG_PARAM_MAX_LEN (EQ_CONFIG_PARAM_LEN+\ + MAX_EQ_BANDS*EQ_CONFIG_PER_BAND_PARAM_LEN) +#define EQ_CONFIG_PARAM_MAX_SZ \ + (EQ_CONFIG_PARAM_MAX_LEN*sizeof(uint32_t)) +#define EQ_NUM_BANDS_PARAM_SZ \ + (EQ_NUM_BANDS_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_LEVELS_PARAM_SZ \ + (EQ_BAND_LEVELS_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_LEVEL_RANGE_PARAM_SZ \ + (EQ_BAND_LEVEL_RANGE_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_FREQS_PARAM_SZ \ + (EQ_BAND_FREQS_PARAM_LEN*sizeof(uint32_t)) +#define EQ_SINGLE_BAND_FREQ_RANGE_PARAM_SZ \ + (EQ_SINGLE_BAND_FREQ_RANGE_PARAM_LEN*sizeof(uint32_t)) +#define EQ_SINGLE_BAND_FREQ_PARAM_SZ \ + (EQ_SINGLE_BAND_FREQ_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_INDEX_PARAM_SZ \ + (EQ_BAND_INDEX_PARAM_LEN*sizeof(uint32_t)) +#define EQ_PRESET_ID_PARAM_SZ \ + (EQ_PRESET_ID_PARAM_LEN*sizeof(uint32_t)) +#define EQ_NUM_PRESETS_PARAM_SZ \ + (EQ_NUM_PRESETS_PARAM_LEN*sizeof(uint8_t)) +struct eq_config_t { + int32_t eq_pregain; + int32_t preset_id; + uint32_t num_bands; +}; +struct eq_per_band_config_t { + int32_t band_idx; + uint32_t filter_type; + uint32_t freq_millihertz; + int32_t gain_millibels; + uint32_t quality_factor; +}; +struct eq_per_band_freq_range_t { + uint32_t band_index; + uint32_t min_freq_millihertz; + uint32_t max_freq_millihertz; +}; + +struct eq_params { + uint32_t device; + uint32_t enable_flag; + struct eq_config_t config; + struct eq_per_band_config_t per_band_cfg[MAX_EQ_BANDS]; + struct eq_per_band_freq_range_t per_band_freq_range[MAX_EQ_BANDS]; + uint32_t band_index; + uint32_t freq_millihertz; +}; + +#define PBE_ENABLE_PARAM_SZ \ + (PBE_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define PBE_CONFIG_PARAM_SZ \ + (PBE_CONFIG_PARAM_LEN*sizeof(uint16_t)) +struct pbe_config_t { + int16_t real_bass_mix; + int16_t bass_color_control; + uint16_t main_chain_delay; + uint16_t xover_filter_order; + uint16_t bandpass_filter_order; + int16_t drc_delay; + uint16_t rms_tav; + int16_t exp_threshold; + uint16_t exp_slope; + int16_t comp_threshold; + uint16_t comp_slope; + uint16_t makeup_gain; + uint32_t comp_attack; + uint32_t comp_release; + uint32_t exp_attack; + uint32_t exp_release; + int16_t limiter_bass_threshold; + int16_t limiter_high_threshold; + int16_t limiter_bass_makeup_gain; + int16_t limiter_high_makeup_gain; + int16_t limiter_bass_gc; + int16_t limiter_high_gc; + int16_t limiter_delay; + uint16_t reserved; + /* place holder for filter coeffs to be followed */ + int32_t p1LowPassCoeffs[5*2]; + int32_t p1HighPassCoeffs[5*2]; + int32_t p1BandPassCoeffs[5*3]; + int32_t p1BassShelfCoeffs[5]; + int32_t p1TrebleShelfCoeffs[5]; +} __packed; + +struct pbe_params { + uint32_t device; + uint32_t enable_flag; + uint32_t cfg_len; + struct pbe_config_t config; +}; + +#define SOFT_VOLUME_ENABLE_PARAM_SZ \ + (SOFT_VOLUME_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define SOFT_VOLUME_GAIN_MASTER_PARAM_SZ \ + (SOFT_VOLUME_GAIN_MASTER_PARAM_LEN*sizeof(uint32_t)) +#define SOFT_VOLUME_GAIN_2CH_PARAM_SZ \ + (SOFT_VOLUME_GAIN_2CH_PARAM_LEN*sizeof(uint16_t)) +struct soft_volume_params { + uint32_t device; + uint32_t enable_flag; + uint32_t master_gain; + uint32_t left_gain; + uint32_t right_gain; +}; + +struct msm_nt_eff_all_config { + struct bass_boost_params bass_boost; + struct pbe_params pbe; + struct virtualizer_params virtualizer; + struct reverb_params reverb; + struct eq_params equalizer; + struct soft_volume_params saplus_vol; + struct soft_volume_params topo_switch_vol; +}; + +#endif /*_MSM_AUDIO_EFFECTS_H*/ diff --git a/include/uapi/sound/devdep_params.h b/include/uapi/sound/devdep_params.h new file mode 100644 index 000000000000..5061ec0da356 --- /dev/null +++ b/include/uapi/sound/devdep_params.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DEV_DEP_H +#define _DEV_DEP_H + +struct dolby_param_data { + int32_t version; + int32_t device_id; + int32_t be_id; + int32_t param_id; + int32_t length; + int32_t __user *data; +}; + +struct dolby_param_license { + int32_t dmid; + int32_t license_key; +}; + +#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM\ + _IOWR('U', 0x10, struct dolby_param_data) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM\ + _IOR('U', 0x11, struct dolby_param_data) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND\ + _IOWR('U', 0x13, struct dolby_param_data) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE\ + _IOWR('U', 0x14, struct dolby_param_license) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER\ + _IOR('U', 0x15, struct dolby_param_data) + +#define DTS_EAGLE_MODULE 0x00005000 +#define DTS_EAGLE_MODULE_ENABLE 0x00005001 +#define EAGLE_DRIVER_ID 0xF2 +#define DTS_EAGLE_IOCTL_GET_CACHE_SIZE _IOR(EAGLE_DRIVER_ID, 0, int) +#define DTS_EAGLE_IOCTL_SET_CACHE_SIZE _IOW(EAGLE_DRIVER_ID, 1, int) +#define DTS_EAGLE_IOCTL_GET_PARAM _IOR(EAGLE_DRIVER_ID, 2, void*) +#define DTS_EAGLE_IOCTL_SET_PARAM _IOW(EAGLE_DRIVER_ID, 3, void*) +#define DTS_EAGLE_IOCTL_SET_CACHE_BLOCK _IOW(EAGLE_DRIVER_ID, 4, void*) +#define DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE _IOW(EAGLE_DRIVER_ID, 5, void*) +#define DTS_EAGLE_IOCTL_GET_LICENSE _IOR(EAGLE_DRIVER_ID, 6, void*) +#define DTS_EAGLE_IOCTL_SET_LICENSE _IOW(EAGLE_DRIVER_ID, 7, void*) +#define DTS_EAGLE_IOCTL_SEND_LICENSE _IOW(EAGLE_DRIVER_ID, 8, int) +#define DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS _IOW(EAGLE_DRIVER_ID, 9, void*) +#define DTS_EAGLE_FLAG_IOCTL_PRE (1<<30) +#define DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE (1<<31) +#define DTS_EAGLE_FLAG_IOCTL_GETFROMCORE DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE +#define DTS_EAGLE_FLAG_IOCTL_MASK (~(DTS_EAGLE_FLAG_IOCTL_PRE | \ + DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE)) +#define DTS_EAGLE_FLAG_ALSA_GET (1<<31) + +struct dts_eagle_param_desc { + uint32_t id; + uint32_t size; + int32_t offset; + uint32_t device; +} __packed; + +#endif diff --git a/include/uapi/sound/lsm_params.h b/include/uapi/sound/lsm_params.h new file mode 100644 index 000000000000..eafdc117413a --- /dev/null +++ b/include/uapi/sound/lsm_params.h @@ -0,0 +1,175 @@ +#ifndef _UAPI_LSM_PARAMS_H__ +#define _UAPI_LSM_PARAMS_H__ + +#include +#include + +#define SNDRV_LSM_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0) + +#define LSM_OUT_FORMAT_PCM (0) +#define LSM_OUT_FORMAT_ADPCM (1 << 0) + +#define LSM_OUT_DATA_RAW (0) +#define LSM_OUT_DATA_PACKED (1) + +#define LSM_OUT_DATA_EVENTS_DISABLED (0) +#define LSM_OUT_DATA_EVENTS_ENABLED (1) + +#define LSM_OUT_TRANSFER_MODE_RT (0) +#define LSM_OUT_TRANSFER_MODE_FTRT (1) + +enum lsm_app_id { + LSM_VOICE_WAKEUP_APP_ID = 1, + LSM_VOICE_WAKEUP_APP_ID_V2 = 2, +}; + +enum lsm_detection_mode { + LSM_MODE_KEYWORD_ONLY_DETECTION = 1, + LSM_MODE_USER_KEYWORD_DETECTION +}; + +enum lsm_vw_status { + LSM_VOICE_WAKEUP_STATUS_RUNNING = 1, + LSM_VOICE_WAKEUP_STATUS_DETECTED, + LSM_VOICE_WAKEUP_STATUS_END_SPEECH, + LSM_VOICE_WAKEUP_STATUS_REJECTED +}; + +enum LSM_PARAM_TYPE { + LSM_ENDPOINT_DETECT_THRESHOLD = 0, + LSM_OPERATION_MODE, + LSM_GAIN, + LSM_MIN_CONFIDENCE_LEVELS, + LSM_REG_SND_MODEL, + LSM_DEREG_SND_MODEL, + LSM_CUSTOM_PARAMS, + /* driver ioctl will parse only so many params */ + LSM_PARAMS_MAX, +}; + +/* + * Data for LSM_ENDPOINT_DETECT_THRESHOLD param_type + * @epd_begin: Begin threshold + * @epd_end: End threshold + */ +struct snd_lsm_ep_det_thres { + __u32 epd_begin; + __u32 epd_end; +}; + +/* + * Data for LSM_OPERATION_MODE param_type + * @mode: The detection mode to be used + * @detect_failure: Setting to enable failure detections. + */ +struct snd_lsm_detect_mode { + enum lsm_detection_mode mode; + bool detect_failure; +}; + +/* + * Data for LSM_GAIN param_type + * @gain: The gain to be applied on LSM + */ +struct snd_lsm_gain { + __u16 gain; +}; + + +struct snd_lsm_sound_model_v2 { + __u8 __user *data; + __u8 *confidence_level; + __u32 data_size; + enum lsm_detection_mode detection_mode; + __u8 num_confidence_levels; + bool detect_failure; +}; + +struct snd_lsm_session_data { + enum lsm_app_id app_id; +}; + +struct snd_lsm_event_status { + __u16 status; + __u16 payload_size; + __u8 payload[0]; +}; + +struct snd_lsm_detection_params { + __u8 *conf_level; + enum lsm_detection_mode detect_mode; + __u8 num_confidence_levels; + bool detect_failure; +}; + +/* + * Param info for each parameter type + * @module_id: Module to which parameter is to be set + * @param_id: Parameter that is to be set + * @param_size: size (in number of bytes) for the data + * in param_data. + * For confidence levels, this is num_conf_levels + * For REG_SND_MODEL, this is size of sound model + * For CUSTOM_PARAMS, this is size of the entire blob of data + * @param_data: Data for the parameter. + * For some param_types this is a structure defined, ex: LSM_GAIN + * For CONFIDENCE_LEVELS, this is array of confidence levels + * For REG_SND_MODEL, this is the sound model data + * For CUSTOM_PARAMS, this is the blob of custom data. + */ +struct lsm_params_info { + __u32 module_id; + __u32 param_id; + __u32 param_size; + __u8 __user *param_data; + enum LSM_PARAM_TYPE param_type; +}; + +/* + * Data passed to the SET_PARAM_V2 IOCTL + * @num_params: Number of params that are to be set + * should not be greater than LSM_PARAMS_MAX + * @params: Points to an array of lsm_params_info + * Each entry points to one parameter to set + * @data_size: size (in bytes) for params + * should be equal to + * num_params * sizeof(struct lsm_parms_info) + */ +struct snd_lsm_module_params { + __u8 __user *params; + __u32 num_params; + __u32 data_size; +}; + +/* + * Data passed to LSM_OUT_FORMAT_CFG IOCTL + * @format: The media format enum + * @packing: indicates the packing method used for data path + * @events: indicates whether data path events need to be enabled + * @transfer_mode: indicates whether FTRT mode or RT mode. + */ +struct snd_lsm_output_format_cfg { + __u8 format; + __u8 packing; + __u8 events; + __u8 mode; +}; + +#define SNDRV_LSM_DEREG_SND_MODEL _IOW('U', 0x01, int) +#define SNDRV_LSM_EVENT_STATUS _IOW('U', 0x02, struct snd_lsm_event_status) +#define SNDRV_LSM_ABORT_EVENT _IOW('U', 0x03, int) +#define SNDRV_LSM_START _IOW('U', 0x04, int) +#define SNDRV_LSM_STOP _IOW('U', 0x05, int) +#define SNDRV_LSM_SET_SESSION_DATA _IOW('U', 0x06, struct snd_lsm_session_data) +#define SNDRV_LSM_REG_SND_MODEL_V2 _IOW('U', 0x07,\ + struct snd_lsm_sound_model_v2) +#define SNDRV_LSM_LAB_CONTROL _IOW('U', 0x08, uint32_t) +#define SNDRV_LSM_STOP_LAB _IO('U', 0x09) +#define SNDRV_LSM_SET_PARAMS _IOW('U', 0x0A, \ + struct snd_lsm_detection_params) +#define SNDRV_LSM_SET_MODULE_PARAMS _IOW('U', 0x0B, \ + struct snd_lsm_module_params) +#define SNDRV_LSM_OUT_FORMAT_CFG _IOW('U', 0x0C, \ + struct snd_lsm_output_format_cfg) + +#endif diff --git a/include/uapi/sound/msmcal-hwdep.h b/include/uapi/sound/msmcal-hwdep.h new file mode 100644 index 000000000000..2a294824fb00 --- /dev/null +++ b/include/uapi/sound/msmcal-hwdep.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _CALIB_HWDEP_H +#define _CALIB_HWDEP_H + +#define WCD9XXX_CODEC_HWDEP_NODE 1000 +enum wcd_cal_type { + WCD9XXX_MIN_CAL, + WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL, + WCD9XXX_MAD_CAL, + WCD9XXX_MBHC_CAL, + WCD9XXX_VBAT_CAL, + WCD9XXX_MAX_CAL, +}; + +struct wcdcal_ioctl_buffer { + __u32 size; + __u8 __user *buffer; + enum wcd_cal_type cal_type; +}; + +#define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \ + _IOW('U', 0x1, struct wcdcal_ioctl_buffer) + +#endif /*_CALIB_HWDEP_H*/ diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 182d92efc7c8..22b30734a58a 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -54,6 +54,7 @@ source "sound/soc/kirkwood/Kconfig" source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mediatek/Kconfig" +source "sound/soc/msm/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 9a30f21d16ee..14660758f38d 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mediatek/ +obj-$(CONFIG_SND_SOC) += msm/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig new file mode 100644 index 000000000000..a5cd94f91cfc --- /dev/null +++ b/sound/soc/msm/Kconfig @@ -0,0 +1,236 @@ +menu "MSM SoC Audio support" + +config SND_SOC_MSM_HOSTLESS_PCM + tristate + +config SND_SOC_MSM_QDSP6V2_INTF + bool "SoC Q6 audio driver for MSM/APQ" + depends on MSM_QDSP6_APRV2_GLINK + help + To add support for SoC audio on MSM/APQ. + This will enable all the platform specific + interactions towards DSP. It includes asm, + adm and afe interfaces on the DSP. + +config SND_SOC_QDSP6V2 + tristate "SoC ALSA audio driver for QDSP6V2" + select SND_SOC_MSM_QDSP6V2_INTF + select SND_SOC_COMPRESS + help + To add support for MSM QDSP6V2 Soc Audio. + This will enable sound soc platform specific + audio drivers. This includes q6asm, q6adm, + q6afe interfaces to DSP using apr. + +config SND_SOC_QDSP_DEBUG + bool "QDSP Audio Driver Debug Feature" + help + Configuration to enable debugging utilities for + QDSP6 based audio drivers. One debugging utility + is inducing kernel panic upon encountering critical + errors from DSP audio modules + +config DOLBY_DAP + bool "Enable Dolby DAP" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for dolby DAP post processing. + This support is to configure the post processing parameters + to DSP. The configuration includes sending the end point + device, end point dependent post processing parameters and + the various posrt processing parameters + +config DOLBY_DS2 + bool "Enable Dolby DS2" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for dolby DAP post processing. + This support is to configure the post processing parameters + to DSP. The configuration includes sending the end point + device, end point dependent post processing parameters and + the various posrt processing parameters + +config DTS_EAGLE + bool "Enable DTS Eagle Support" + depends on SND_SOC_MSM_QDSP6V2_INTF + select SND_HWDEP + help + To add DTS Eagle support on QDSP6 targets. + Eagle is a DTS pre/post processing + package that includes HeadphoneX. The configuration + includes sending tuning parameters of various modules. + +config DTS_SRS_TM + bool "Enable DTS SRS" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for DTS SRS post processing. + This support is to configure the post processing + parameters to DSP. The configuration includes sending + tuning parameters of various modules. + +config QTI_PP + bool "Enable QTI PP" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for default QTI post processing. + This support is to configure the post processing + parameters to DSP. The configuration includes sending + tuning parameters of various modules such as equalizer, + customized mixing. + +config QTI_PP_AUDIOSPHERE + bool "Enable QTI AUDIOSPHERE PP" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for QTI audio sphere post processing. + This support is to configure the post processing + parameters to DSP. The configuration includes sending + tuning parameters of audio sphere module. + +config SND_SOC_CPE + tristate "CPE drivers" + depends on SND_SOC_WCD_CPE + help + To add support for Codec Processing Engine. This support + is to enable CPE block on the codec and this config needs + to be added to codecs that contain the CPE hardware block. + The configuration includes the cpe lsm driver to enable + listen on codec. + +config SND_SOC_INT_CODEC + tristate "SoC Machine driver for MSMFALCON_INT" + depends on ARCH_QCOM + select SND_SOC_QDSP6V2 + select SND_SOC_MSM_STUB + select SND_SOC_MSM_HOSTLESS_PCM + select SND_DYNAMIC_MINORS + select MSM_QDSP6_APRV2_GLINK + select MSM_QDSP6_SSR + select MSM_QDSP6_PDR + select MSM_QDSP6_NOTIFIER + select MSM_QDSP6V2_CODECS + select SND_SOC_MSM_SWR + select SND_SOC_MSM8X16_WCD + select QTI_PP + select DTS_SRS_TM + select DOLBY_DAP + select DOLBY_DS2 + select SND_HWDEP + select MSM_ULTRASOUND + select DTS_EAGLE + select SND_SOC_MSMFALCON_COMMON + select SND_SOC_COMPRESS + help + To add support for SoC audio on MSM_INT. + This will enable sound soc drivers which + interfaces with DSP, also it will enable + the machine driver and the corresponding + DAI-links + +config SND_SOC_EXT_CODEC + tristate "SoC Machine driver for MSMFALCON_EXT" + depends on ARCH_QCOM + select SND_SOC_QDSP6V2 + select SND_SOC_MSM_STUB + select SND_SOC_MSM_HOSTLESS_PCM + select SND_DYNAMIC_MINORS + select MSM_QDSP6_APRV2_GLINK + select MSM_QDSP6_SSR + select MSM_QDSP6_PDR + select MSM_QDSP6_NOTIFIER + select MSM_QDSP6V2_CODECS + select SND_SOC_WCD9335 + select SND_SOC_WCD934X + select SND_SOC_WSA881X + select MFD_CORE + select QTI_PP + select DTS_SRS_TM + select DOLBY_DAP + select DOLBY_DS2 + select SND_SOC_CPE + select SND_SOC_WCD_CPE + select SND_HWDEP + select MSM_ULTRASOUND + select DTS_EAGLE + select SND_SOC_MSMFALCON_COMMON + select SND_SOC_COMPRESS + help + To add support for SoC audio on MSM_EXT. + This will enable sound soc drivers which + interfaces with DSP, also it will enable + the machine driver and the corresponding + DAI-links + +config SND_SOC_MSM8996 + tristate "SoC Machine driver for MSM8996 boards" + depends on ARCH_MSM8996 + select SND_SOC_COMPRESS + select SND_SOC_QDSP6V2 + select SND_SOC_MSM_STUB + select SND_SOC_MSM_HOSTLESS_PCM + select SND_DYNAMIC_MINORS + select MSM_QDSP6_APRV2 + select MSM_QDSP6V2_CODECS + select SND_SOC_WCD9335 + select SND_SOC_WSA881X + select SND_SOC_MSM_HDMI_CODEC_RX + select DTS_SRS_TM + select QTI_PP + select QTI_PP_AUDIOSPHERE + select SND_SOC_CPE + select MSM_ULTRASOUND + select DOLBY_DS2 + select SND_HWDEP + select DTS_EAGLE + help + To add support for SoC audio on MSM8996. + This will enable sound soc drivers which + interfaces with DSP, also it will enable + the machine driver and the corresponding + DAI-links + +config SND_SOC_MSM8998 + tristate "SoC Machine driver for MSM8998 boards" + depends on ARCH_QCOM + select SND_SOC_COMPRESS + select SND_SOC_QDSP6V2 + select SND_SOC_MSM_STUB + select SND_SOC_MSM_HOSTLESS_PCM + select SND_DYNAMIC_MINORS + select MSM_QDSP6_APRV2_GLINK + select MSM_QDSP6_SSR + select MSM_QDSP6_PDR + select MSM_QDSP6_NOTIFIER + select MSM_QDSP6V2_CODECS + select SND_SOC_WCD9335 + select SND_SOC_WCD934X + select SND_SOC_WSA881X + select SND_SOC_MSM_HDMI_CODEC_RX + select DTS_SRS_TM + select QTI_PP + select SND_SOC_CPE + select MSM_ULTRASOUND + select DOLBY_DS2 + select SND_HWDEP + select DTS_EAGLE + help + To add support for SoC audio on MSM8998. + This will enable sound soc drivers which + interfaces with DSP, also it will enable + the machine driver and the corresponding + DAI-links + +config SND_SOC_FALCON + tristate "SoC Machine driver for MSMFALCON boards" + depends on ARCH_MSMFALCON + select SND_SOC_INT_CODEC + select SND_SOC_EXT_CODEC + help + To add support for SoC audio on MSMFALCON. + This will enable sound soc drivers which + interfaces with DSP, also it will enable + the machine driver and the corresponding + DAI-links + +endmenu diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile new file mode 100644 index 000000000000..8df7fad3893d --- /dev/null +++ b/sound/soc/msm/Makefile @@ -0,0 +1,35 @@ +# MSM Machine Support + +snd-soc-hostless-pcm-objs := msm-pcm-hostless.o +obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o + +obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/ + +snd-soc-qdsp6v2-objs := msm-dai-fe.o +obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o + +#for CPE drivers +snd-soc-cpe-objs := msm-cpe-lsm.o +obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o + +# for MSM8996 sound card driver +snd-soc-msm8996-objs := msm8996.o +obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o + +# for MSM8998 sound card driver +snd-soc-msm8998-objs := msm8998.o +obj-$(CONFIG_SND_SOC_MSM8998) += snd-soc-msm8998.o + +# for MSMFALCON sound card driver +snd-soc-msmfalcon-common-objs := msm-audio-pinctrl.o msmfalcon-common.o +obj-$(CONFIG_SND_SOC_MSMFALCON_COMMON) += snd-soc-msmfalcon-common.o + +# for MSMFALCON sound card driver +snd-soc-int-codec-objs := msmfalcon-internal.o +obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-msmfalcon-common.o +obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-int-codec.o + +# for MSMFALCON sound card driver +snd-soc-ext-codec-objs := msmfalcon-external.o msmfalcon-ext-dai-links.o +obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-msmfalcon-common.o +obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-ext-codec.o diff --git a/sound/soc/msm/device_event.h b/sound/soc/msm/device_event.h new file mode 100644 index 000000000000..408d114e3c84 --- /dev/null +++ b/sound/soc/msm/device_event.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DEVICE_EVENT_H +#define __DEVICE_EVENT_H + +#define QC_AUDIO_EXTERNAL_SPK_1_EVENT "qc_ext_spk_1" +#define QC_AUDIO_EXTERNAL_SPK_2_EVENT "qc_ext_spk_2" +#define QC_AUDIO_EXTERNAL_MIC_EVENT "qc_ext_mic" + +#endif /* __DEVICE_EVENT_H */ diff --git a/sound/soc/msm/msm-audio-pinctrl.c b/sound/soc/msm/msm-audio-pinctrl.c new file mode 100644 index 000000000000..f0fba840eb2d --- /dev/null +++ b/sound/soc/msm/msm-audio-pinctrl.c @@ -0,0 +1,316 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include "msm-audio-pinctrl.h" + +/* + * pinctrl -- handle to query pinctrl apis + * cdc lines -- stores pinctrl handles for pinctrl states + * active_set -- maintain the overall pinctrl state + */ +struct cdc_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state **cdc_lines; + int active_set; +}; + +/* + * gpiosets -- stores all gpiosets mentioned in dtsi file + * gpiosets_comb_names -- stores all possible gpioset combinations + * gpioset_state -- maintains counter for each gpioset + * gpiosets_max -- maintain the total supported gpiosets + * gpiosets_comb_max -- maintain the total gpiosets combinations + */ +struct cdc_gpioset_info { + char **gpiosets; + char **gpiosets_comb_names; + uint8_t *gpioset_state; + int gpiosets_max; + int gpiosets_comb_max; +}; + +static struct cdc_pinctrl_info pinctrl_info[MAX_PINCTRL_CLIENT]; +static struct cdc_gpioset_info gpioset_info[MAX_PINCTRL_CLIENT]; + +/* Finds the index for the gpio set in the dtsi file */ +int msm_get_gpioset_index(enum pinctrl_client client, char *keyword) +{ + int i; + + for (i = 0; i < gpioset_info[client].gpiosets_max; i++) { + if (!(strcmp(gpioset_info[client].gpiosets[i], keyword))) + break; + } + /* Checking if the keyword is present in dtsi or not */ + if (i != gpioset_info[client].gpiosets_max) + return i; + else + return -EINVAL; +} + +/* + * This function reads the following from dtsi file + * 1. All gpio sets + * 2. All combinations of gpio sets + * 3. Pinctrl handles to gpio sets + * + * Returns error if there is + * 1. Problem reading from dtsi file + * 2. Memory allocation failure + */ +int msm_gpioset_initialize(enum pinctrl_client client, + struct device *dev) +{ + struct pinctrl *pinctrl; + const char *gpioset_names = "qcom,msm-gpios"; + const char *gpioset_combinations = "qcom,pinctrl-names"; + const char *gpioset_names_str = NULL; + const char *gpioset_comb_str = NULL; + int num_strings = 0; + int ret = 0; + int i = 0; + + pr_debug("%s\n", __func__); + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pinctrl_info[client].pinctrl = pinctrl; + + /* Reading of gpio sets */ + num_strings = of_property_count_strings(dev->of_node, + gpioset_names); + if (num_strings < 0) { + dev_err(dev, + "%s: missing %s in dt node or length is incorrect\n", + __func__, gpioset_names); + goto err; + } + gpioset_info[client].gpiosets_max = num_strings; + gpioset_info[client].gpiosets = devm_kzalloc(dev, + gpioset_info[client].gpiosets_max * + sizeof(char *), GFP_KERNEL); + if (!gpioset_info[client].gpiosets) { + dev_err(dev, "Can't allocate memory for gpio set names\n"); + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < num_strings; i++) { + ret = of_property_read_string_index(dev->of_node, + gpioset_names, i, &gpioset_names_str); + + gpioset_info[client].gpiosets[i] = devm_kzalloc(dev, + (strlen(gpioset_names_str) + 1), GFP_KERNEL); + + if (!gpioset_info[client].gpiosets[i]) { + dev_err(dev, "%s: Can't allocate gpiosets[%d] data\n", + __func__, i); + ret = -ENOMEM; + goto err; + } + strlcpy(gpioset_info[client].gpiosets[i], + gpioset_names_str, strlen(gpioset_names_str)+1); + gpioset_names_str = NULL; + } + num_strings = 0; + + /* Allocating memory for gpio set counter */ + gpioset_info[client].gpioset_state = devm_kzalloc(dev, + gpioset_info[client].gpiosets_max * + sizeof(uint8_t), GFP_KERNEL); + if (!gpioset_info[client].gpioset_state) { + dev_err(dev, "Can't allocate memory for gpio set counter\n"); + ret = -ENOMEM; + goto err; + } + + /* Reading of all combinations of gpio sets */ + num_strings = of_property_count_strings(dev->of_node, + gpioset_combinations); + if (num_strings < 0) { + dev_err(dev, + "%s: missing %s in dt node or length is incorrect\n", + __func__, gpioset_combinations); + goto err; + } + gpioset_info[client].gpiosets_comb_max = num_strings; + gpioset_info[client].gpiosets_comb_names = devm_kzalloc(dev, + num_strings * sizeof(char *), GFP_KERNEL); + if (!gpioset_info[client].gpiosets_comb_names) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) { + ret = of_property_read_string_index(dev->of_node, + gpioset_combinations, i, &gpioset_comb_str); + + gpioset_info[client].gpiosets_comb_names[i] = devm_kzalloc(dev, + (strlen(gpioset_comb_str) + 1), GFP_KERNEL); + if (!gpioset_info[client].gpiosets_comb_names[i]) { + ret = -ENOMEM; + goto err; + } + + strlcpy(gpioset_info[client].gpiosets_comb_names[i], + gpioset_comb_str, + strlen(gpioset_comb_str)+1); + pr_debug("%s: GPIO configuration %s\n", + __func__, + gpioset_info[client].gpiosets_comb_names[i]); + gpioset_comb_str = NULL; + } + + /* Allocating memory for handles to pinctrl states */ + pinctrl_info[client].cdc_lines = devm_kzalloc(dev, + num_strings * sizeof(char *), GFP_KERNEL); + if (!pinctrl_info[client].cdc_lines) { + ret = -ENOMEM; + goto err; + } + + /* Get pinctrl handles for gpio sets in dtsi file */ + for (i = 0; i < num_strings; i++) { + pinctrl_info[client].cdc_lines[i] = pinctrl_lookup_state( + pinctrl, + (const char *)gpioset_info[client]. + gpiosets_comb_names[i]); + if (IS_ERR(pinctrl_info[client].cdc_lines[i])) + pr_err("%s: Unable to get pinctrl handle for %s\n", + __func__, gpioset_info[client]. + gpiosets_comb_names[i]); + } + goto success; + +err: + /* Free up memory allocated for gpio set combinations */ + for (i = 0; i < gpioset_info[client].gpiosets_max; i++) { + if (gpioset_info[client].gpiosets[i] != NULL) { + devm_kfree(dev, gpioset_info[client].gpiosets[i]); + gpioset_info[client].gpiosets[i] = NULL; + } + } + if (gpioset_info[client].gpiosets != NULL) { + devm_kfree(dev, gpioset_info[client].gpiosets); + gpioset_info[client].gpiosets = NULL; + } + + /* Free up memory allocated for gpio set combinations */ + for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) { + if (gpioset_info[client].gpiosets_comb_names[i] != NULL) { + devm_kfree(dev, + gpioset_info[client].gpiosets_comb_names[i]); + gpioset_info[client].gpiosets_comb_names[i] = NULL; + } + } + if (gpioset_info[client].gpiosets_comb_names != NULL) { + devm_kfree(dev, gpioset_info[client].gpiosets_comb_names); + gpioset_info[client].gpiosets_comb_names = NULL; + } + + /* Free up memory allocated for handles to pinctrl states */ + if (pinctrl_info[client].cdc_lines != NULL) { + devm_kfree(dev, pinctrl_info[client].cdc_lines); + pinctrl_info[client].cdc_lines = NULL; + } + + /* Free up memory allocated for counter of gpio sets */ + if (gpioset_info[client].gpioset_state != NULL) { + devm_kfree(dev, gpioset_info[client].gpioset_state); + gpioset_info[client].gpioset_state = NULL; + } + +success: + return ret; +} + +int msm_gpioset_activate(enum pinctrl_client client, char *keyword) +{ + int ret = 0; + int gp_set = 0; + int active_set = 0; + + gp_set = msm_get_gpioset_index(client, keyword); + if (gp_set < 0) { + pr_err("%s: gpio set name does not exist\n", + __func__); + return gp_set; + } + + if (!gpioset_info[client].gpioset_state[gp_set]) { + /* + * If pinctrl pointer is not valid, + * no need to proceed further + */ + active_set = pinctrl_info[client].active_set; + if (IS_ERR(pinctrl_info[client].cdc_lines[active_set])) + return 0; + + pinctrl_info[client].active_set |= (1 << gp_set); + active_set = pinctrl_info[client].active_set; + pr_debug("%s: pinctrl.active_set: %d\n", __func__, active_set); + + /* Select the appropriate pinctrl state */ + ret = pinctrl_select_state(pinctrl_info[client].pinctrl, + pinctrl_info[client].cdc_lines[active_set]); + } + gpioset_info[client].gpioset_state[gp_set]++; + + return ret; +} + +int msm_gpioset_suspend(enum pinctrl_client client, char *keyword) +{ + int ret = 0; + int gp_set = 0; + int active_set = 0; + + gp_set = msm_get_gpioset_index(client, keyword); + if (gp_set < 0) { + pr_err("%s: gpio set name does not exist\n", + __func__); + return gp_set; + } + + if (gpioset_info[client].gpioset_state[gp_set] == 1) { + pinctrl_info[client].active_set &= ~(1 << gp_set); + /* + * If pinctrl pointer is not valid, + * no need to proceed further + */ + active_set = pinctrl_info[client].active_set; + if (IS_ERR(pinctrl_info[client].cdc_lines[active_set])) + return -EINVAL; + + pr_debug("%s: pinctrl.active_set: %d\n", __func__, + pinctrl_info[client].active_set); + /* Select the appropriate pinctrl state */ + ret = pinctrl_select_state(pinctrl_info[client].pinctrl, + pinctrl_info[client].cdc_lines[pinctrl_info[client]. + active_set]); + } + if (!(gpioset_info[client].gpioset_state[gp_set])) { + pr_err("%s: Invalid call to de activate gpios: %d\n", __func__, + gpioset_info[client].gpioset_state[gp_set]); + return -EINVAL; + } + + gpioset_info[client].gpioset_state[gp_set]--; + + return ret; +} diff --git a/sound/soc/msm/msm-audio-pinctrl.h b/sound/soc/msm/msm-audio-pinctrl.h new file mode 100644 index 000000000000..ec7c6aafdaea --- /dev/null +++ b/sound/soc/msm/msm-audio-pinctrl.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_AUDIO_PINCTRL_H +#define __MSM_AUDIO_PINCTRL_H + +enum pinctrl_client { + CLIENT_WCD, + CLIENT_WSA_BONGO_1, + CLIENT_WSA_BONGO_2, + MAX_PINCTRL_CLIENT, +}; + + +/* finds the index for the gpio set in the dtsi file */ +int msm_get_gpioset_index(enum pinctrl_client client, char *keyword); + +/* + * this function reads the following from dtsi file + * 1. all gpio sets + * 2. all combinations of gpio sets + * 3. pinctrl handles to gpio sets + * + * returns error if there is + * 1. problem reading from dtsi file + * 2. memory allocation failure + */ +int msm_gpioset_initialize(enum pinctrl_client client, struct device *dev); + +int msm_gpioset_activate(enum pinctrl_client client, char *keyword); + +int msm_gpioset_suspend(enum pinctrl_client client, char *keyword); + +#endif /* __MSM_AUDIO_PINCTRL_H */ diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c new file mode 100644 index 000000000000..1149c4a6e1b8 --- /dev/null +++ b/sound/soc/msm/msm-cpe-lsm.c @@ -0,0 +1,3143 @@ +/* + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAMPLE_RATE_48KHZ 48000 +#define SAMPLE_RATE_16KHZ 16000 +#define LSM_VOICE_WAKEUP_APP_V2 2 +#define AFE_PORT_ID_1 1 +#define AFE_PORT_ID_3 3 +#define AFE_OUT_PORT_2 2 +#define LISTEN_MIN_NUM_PERIODS 2 +#define LISTEN_MAX_NUM_PERIODS 12 +#define LISTEN_MAX_PERIOD_SIZE 61440 +#define LISTEN_MIN_PERIOD_SIZE 320 +#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256 +#define MSM_CPE_MAX_CUSTOM_PARAM_SIZE 2048 + +#define MSM_CPE_LAB_THREAD_TIMEOUT (3 * (HZ/10)) + +#define MSM_CPE_LSM_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define MSM_CPE_LSM_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 16000, 48000, 192000, 384000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + + +static struct snd_pcm_hardware msm_pcm_hardware_listen = { + .info = (SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000), + .rate_min = 16000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = LISTEN_MAX_NUM_PERIODS * + LISTEN_MAX_PERIOD_SIZE, + .period_bytes_min = LISTEN_MIN_PERIOD_SIZE, + .period_bytes_max = LISTEN_MAX_PERIOD_SIZE, + .periods_min = LISTEN_MIN_NUM_PERIODS, + .periods_max = LISTEN_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +enum { + AFE_CMD_INVALID = 0, + AFE_CMD_PORT_START, + AFE_CMD_PORT_SUSPEND, + AFE_CMD_PORT_RESUME, + AFE_CMD_PORT_STOP, +}; + +enum cpe_lab_thread_status { + MSM_LSM_LAB_THREAD_STOP, + MSM_LSM_LAB_THREAD_RUNNING, + MSM_LSM_LAB_THREAD_ERROR, +}; + +struct cpe_hw_params { + u32 sample_rate; + u16 sample_size; + u32 buf_sz; + u32 period_count; + u16 channels; +}; + +struct cpe_data_pcm_buf { + u8 *mem; + phys_addr_t phys; +}; + +struct cpe_lsm_lab { + atomic_t in_count; + atomic_t abort_read; + u32 dma_write; + u32 buf_idx; + u32 pcm_size; + enum cpe_lab_thread_status thread_status; + struct cpe_data_pcm_buf *pcm_buf; + wait_queue_head_t period_wait; + struct completion comp; + struct completion thread_complete; +}; + +struct cpe_priv { + void *core_handle; + struct snd_soc_codec *codec; + struct wcd_cpe_lsm_ops lsm_ops; + struct wcd_cpe_afe_ops afe_ops; + bool afe_mad_ctl; + u32 input_port_id; +}; + +struct cpe_lsm_data { + struct device *dev; + struct cpe_lsm_session *lsm_session; + struct mutex lsm_api_lock; + struct cpe_lsm_lab lab; + struct cpe_hw_params hw_params; + struct snd_pcm_substream *substream; + + wait_queue_head_t event_wait; + atomic_t event_avail; + atomic_t event_stop; + + u8 ev_det_status; + u8 ev_det_pld_size; + u8 *ev_det_payload; + + bool cpe_prepared; +}; + +static int msm_cpe_afe_mad_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cpe_priv *cpe = kcontrol->private_data; + + ucontrol->value.integer.value[0] = cpe->afe_mad_ctl; + return 0; +} + +static int msm_cpe_afe_mad_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cpe_priv *cpe = kcontrol->private_data; + + cpe->afe_mad_ctl = ucontrol->value.integer.value[0]; + return 0; +} + +static struct snd_kcontrol_new msm_cpe_kcontrols[] = { + SOC_SINGLE_EXT("CPE AFE MAD Enable", SND_SOC_NOPM, 0, 1, 0, + msm_cpe_afe_mad_ctl_get, msm_cpe_afe_mad_ctl_put), +}; + +/* + * cpe_get_private_data: obtain ASoC platform driver private data + * @substream: ASoC substream for which private data to be obtained + */ +static struct cpe_priv *cpe_get_private_data( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd; + + if (!substream || !substream->private_data) { + pr_err("%s: %s is invalid\n", + __func__, + (!substream) ? "substream" : "private_data"); + goto err_ret; + } + + rtd = substream->private_data; + + if (!rtd || !rtd->platform) { + pr_err("%s: %s is invalid\n", + __func__, + (!rtd) ? "runtime" : "platform"); + goto err_ret; + } + + return snd_soc_platform_get_drvdata(rtd->platform); + +err_ret: + return NULL; +} + +/* + * cpe_get_lsm_data: obtain the lsm session data given the substream + * @substream: ASoC substream for which lsm session data to be obtained + */ +static struct cpe_lsm_data *cpe_get_lsm_data( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return runtime->private_data; +} + +static void msm_cpe_process_event_status(void *data, + u8 detect_status, u8 size, u8 *payload) +{ + struct cpe_lsm_data *lsm_d = data; + + lsm_d->ev_det_status = detect_status; + lsm_d->ev_det_pld_size = size; + + lsm_d->ev_det_payload = kzalloc(size, GFP_KERNEL); + if (!lsm_d->ev_det_payload) + return; + + memcpy(lsm_d->ev_det_payload, payload, size); + + atomic_set(&lsm_d->event_avail, 1); + wake_up(&lsm_d->event_wait); +} + +static void msm_cpe_process_event_status_done(struct cpe_lsm_data *lsm_data) +{ + kfree(lsm_data->ev_det_payload); + lsm_data->ev_det_payload = NULL; + + lsm_data->ev_det_status = 0; + lsm_data->ev_det_pld_size = 0; +} + +/* + * msm_cpe_afe_port_cntl: Perform the afe port control + * @substream: substream for which afe port command to be performed + * @core_handle: handle to core + * @afe_ops: handle to the afe operations + * @afe_cfg: afe port configuration data + * @cmd: command to be sent to AFE + * + */ +static int msm_cpe_afe_port_cntl( + struct snd_pcm_substream *substream, + void *core_handle, + struct wcd_cpe_afe_ops *afe_ops, + struct wcd_cpe_afe_port_cfg *afe_cfg, + int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (!afe_cfg->port_id) { + /* + * It is possible driver can get closed without prepare, + * in which case afe ports will not be initialized. + */ + dev_dbg(rtd->dev, + "%s: Invalid afe port id\n", + __func__); + return 0; + } + + switch (cmd) { + case AFE_CMD_PORT_START: + rc = afe_ops->afe_port_start(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: AFE port start failed\n", + __func__); + break; + case AFE_CMD_PORT_SUSPEND: + rc = afe_ops->afe_port_suspend(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: afe_suspend failed, err = %d\n", + __func__, rc); + break; + case AFE_CMD_PORT_RESUME: + rc = afe_ops->afe_port_resume(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: afe_resume failed, err = %d\n", + __func__, rc); + break; + case AFE_CMD_PORT_STOP: + rc = afe_ops->afe_port_stop(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: afe_stopfailed, err = %d\n", + __func__, rc); + break; + } + + return rc; +} + +static int msm_cpe_lsm_lab_stop(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + struct wcd_cpe_afe_ops *afe_ops; + struct cpe_lsm_session *session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct msm_slim_dma_data *dma_data = NULL; + int rc; + + /* + * the caller is not aware of LAB status and will + * try to stop lab even if it is already stopped. + * return success right away is LAB is already stopped + */ + if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) { + dev_dbg(rtd->dev, + "%s: lab already stopped\n", + __func__); + return 0; + } + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + lsm_ops = &cpe->lsm_ops; + afe_ops = &cpe->afe_ops; + session = lsm_d->lsm_session; + if (rtd->cpu_dai) + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) { + dev_err(rtd->dev, + "%s: dma_data is not set\n", + __func__); + return -EINVAL; + } + + if (lab_d->thread_status == MSM_LSM_LAB_THREAD_RUNNING) { + dev_dbg(rtd->dev, "%s: stopping lab thread\n", + __func__); + rc = kthread_stop(session->lsm_lab_thread); + + /* + * kthread_stop returns EINTR if the thread_fn + * was not scheduled before calling kthread_stop. + * In this case, we dont need to wait for lab + * thread to complete as lab thread will not be + * scheduled at all. + */ + if (rc == -EINTR) + goto done; + + /* Wait for the lab thread to exit */ + rc = wait_for_completion_timeout( + &lab_d->thread_complete, + MSM_CPE_LAB_THREAD_TIMEOUT); + if (!rc) { + dev_err(rtd->dev, + "%s: Wait for lab thread timedout\n", + __func__); + return -ETIMEDOUT; + } + } + + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_PRE_DISABLE); + if (rc) + dev_err(rtd->dev, + "%s: PRE ch teardown failed, err = %d\n", + __func__, rc); + /* continue with teardown even if any intermediate step fails */ + rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, false); + if (rc) + dev_err(rtd->dev, + "%s: open data failed %d\n", __func__, rc); + dma_data->ph = 0; + + /* + * Even though LAB stop failed, + * output AFE port needs to be stopped + */ + rc = afe_ops->afe_port_stop(cpe->core_handle, + &session->afe_out_port_cfg); + if (rc) + dev_err(rtd->dev, + "%s: AFE out port stop failed, err = %d\n", + __func__, rc); + + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_POST_DISABLE); + if (rc) + dev_err(rtd->dev, + "%s: POST ch teardown failed, err = %d\n", + __func__, rc); + +done: + lab_d->thread_status = MSM_LSM_LAB_THREAD_STOP; + lab_d->buf_idx = 0; + atomic_set(&lab_d->in_count, 0); + lab_d->dma_write = 0; + + return 0; +} + +static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream, + struct cpe_lsm_session *session, + struct msm_slim_dma_data *dma_data) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct cpe_hw_params *hw_params = &lsm_d->hw_params; + struct cpe_data_pcm_buf *pcm_buf = NULL; + int rc = 0; + int dma_alloc = 0; + u32 count = 0; + u32 bufsz, bufcnt; + + if (lab_d->pcm_buf && + lab_d->pcm_buf->mem) { + dev_dbg(rtd->dev, + "%s: LAB buf already allocated\n", + __func__); + goto exit; + } + + bufsz = hw_params->buf_sz; + bufcnt = hw_params->period_count; + + dev_dbg(rtd->dev, + "%s:Buf Size %d Buf count %d\n", + __func__, + bufsz, bufcnt); + + pcm_buf = kzalloc(((sizeof(struct cpe_data_pcm_buf)) * bufcnt), + GFP_KERNEL); + if (!pcm_buf) { + rc = -ENOMEM; + goto exit; + } + + lab_d->pcm_buf = pcm_buf; + dma_alloc = bufsz * bufcnt; + pcm_buf->mem = NULL; + pcm_buf->mem = dma_alloc_coherent(dma_data->sdev->dev.parent, + dma_alloc, + &(pcm_buf->phys), + GFP_KERNEL); + if (!pcm_buf->mem) { + dev_err(rtd->dev, + "%s:DMA alloc failed size = %x\n", + __func__, dma_alloc); + rc = -ENOMEM; + goto fail; + } + + count = 0; + while (count < bufcnt) { + pcm_buf[count].mem = pcm_buf[0].mem + (count * bufsz); + pcm_buf[count].phys = pcm_buf[0].phys + (count * bufsz); + dev_dbg(rtd->dev, + "%s: pcm_buf[%d].mem %pK pcm_buf[%d].phys %pK\n", + __func__, count, + (void *)pcm_buf[count].mem, + count, &(pcm_buf[count].phys)); + count++; + } + + return 0; +fail: + if (pcm_buf) { + if (pcm_buf->mem) + dma_free_coherent(dma_data->sdev->dev.parent, dma_alloc, + pcm_buf->mem, pcm_buf->phys); + kfree(pcm_buf); + lab_d->pcm_buf = NULL; + } +exit: + return rc; +} + +static int msm_cpe_lab_buf_dealloc(struct snd_pcm_substream *substream, + struct cpe_lsm_session *session, struct msm_slim_dma_data *dma_data) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct cpe_hw_params *hw_params = &lsm_d->hw_params; + int rc = 0; + int dma_alloc = 0; + struct cpe_data_pcm_buf *pcm_buf = NULL; + int bufsz, bufcnt; + + bufsz = hw_params->buf_sz; + bufcnt = hw_params->period_count; + + dev_dbg(rtd->dev, + "%s:Buf Size %d Buf count %d\n", __func__, + bufsz, bufcnt); + + if (bufcnt <= 0 || bufsz <= 0) { + dev_err(rtd->dev, + "%s: Invalid params, bufsz = %u, bufcnt = %u\n", + __func__, bufsz, bufcnt); + return -EINVAL; + } + + pcm_buf = lab_d->pcm_buf; + dma_alloc = bufsz * bufcnt; + if (dma_data && pcm_buf) + dma_free_coherent(dma_data->sdev->dev.parent, dma_alloc, + pcm_buf->mem, pcm_buf->phys); + kfree(pcm_buf); + lab_d->pcm_buf = NULL; + return rc; +} + +/* + * msm_cpe_lab_thread: Initiated on KW detection + * @data: lab data + * + * Start lab thread and call CPE core API for SLIM + * read operations. + */ +static int msm_cpe_lab_thread(void *data) +{ + struct cpe_lsm_data *lsm_d = data; + struct cpe_lsm_session *session = lsm_d->lsm_session; + struct snd_pcm_substream *substream = lsm_d->substream; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct cpe_hw_params *hw_params = &lsm_d->hw_params; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + struct wcd_cpe_afe_ops *afe_ops; + struct cpe_data_pcm_buf *cur_buf, *next_buf; + struct msm_slim_dma_data *dma_data = NULL; + struct snd_soc_pcm_runtime *rtd = NULL; + bool wait_timedout = false; + int rc = 0; + u32 done_len = 0; + u32 buf_count = 0; + u32 prd_cnt; + + allow_signal(SIGKILL); + set_current_state(TASK_INTERRUPTIBLE); + + pr_debug("%s: Lab thread start\n", __func__); + init_completion(&lab_d->comp); + + if (PCM_RUNTIME_CHECK(substream)) { + rc = -EINVAL; + goto done; + } + + if (!cpe || !cpe->core_handle) { + pr_err("%s: Handle to %s is invalid\n", + __func__, + (!cpe) ? "cpe" : "core"); + rc = -EINVAL; + goto done; + } + + rtd = substream->private_data; + if (rtd->cpu_dai) + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) { + pr_err("%s: dma_data is not set\n", __func__); + rc = -EINVAL; + goto done; + } + + lsm_ops = &cpe->lsm_ops; + afe_ops = &cpe->afe_ops; + + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_PRE_ENABLE); + if (rc) { + dev_err(rtd->dev, + "%s: PRE ch setup failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, true); + if (rc) { + dev_err(rtd->dev, + "%s: open data failed %d\n", __func__, rc); + goto done; + } + + dev_dbg(rtd->dev, "%s: Established data channel\n", + __func__); + + init_waitqueue_head(&lab_d->period_wait); + memset(lab_d->pcm_buf[0].mem, 0, lab_d->pcm_size); + + rc = slim_port_xfer(dma_data->sdev, dma_data->ph, + lab_d->pcm_buf[0].phys, + hw_params->buf_sz, &lab_d->comp); + if (rc) { + dev_err(rtd->dev, + "%s: buf[0] slim_port_xfer failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = slim_port_xfer(dma_data->sdev, dma_data->ph, + lab_d->pcm_buf[1].phys, + hw_params->buf_sz, &lab_d->comp); + if (rc) { + dev_err(rtd->dev, + "%s: buf[0] slim_port_xfer failed, err = %d\n", + __func__, rc); + goto done; + } + + cur_buf = &lab_d->pcm_buf[0]; + next_buf = &lab_d->pcm_buf[2]; + prd_cnt = hw_params->period_count; + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_POST_ENABLE); + if (rc) { + dev_err(rtd->dev, + "%s: POST ch setup failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = afe_ops->afe_port_start(cpe->core_handle, + &session->afe_out_port_cfg); + if (rc) { + dev_err(rtd->dev, + "%s: AFE out port start failed, err = %d\n", + __func__, rc); + goto done; + } + + while (!kthread_should_stop() && + lab_d->thread_status != MSM_LSM_LAB_THREAD_ERROR) { + + rc = slim_port_xfer(dma_data->sdev, dma_data->ph, + next_buf->phys, + hw_params->buf_sz, &lab_d->comp); + if (rc) { + dev_err(rtd->dev, + "%s: slim_port_xfer failed, err = %d\n", + __func__, rc); + lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR; + } + + rc = wait_for_completion_timeout(&lab_d->comp, (2 * HZ/10)); + if (!rc) { + dev_err(rtd->dev, + "%s: wait timedout for slim buffer\n", + __func__); + wait_timedout = true; + } else { + wait_timedout = false; + } + + rc = slim_port_get_xfer_status(dma_data->sdev, + dma_data->ph, + &cur_buf->phys, &done_len); + if (rc || + (!rc && wait_timedout)) { + dev_err(rtd->dev, + "%s: xfer_status failure, rc = %d, wait_timedout = %s\n", + __func__, rc, + (wait_timedout ? "true" : "false")); + lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR; + } + + if (done_len || + ((!done_len) && + lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR)) { + atomic_inc(&lab_d->in_count); + lab_d->dma_write += snd_pcm_lib_period_bytes(substream); + snd_pcm_period_elapsed(substream); + wake_up(&lab_d->period_wait); + buf_count++; + + cur_buf = &lab_d->pcm_buf[buf_count % prd_cnt]; + next_buf = &lab_d->pcm_buf[(buf_count + 2) % prd_cnt]; + dev_dbg(rtd->dev, + "%s: Cur buf.mem = %pK Next Buf.mem = %pK\n" + " buf count = 0x%x\n", __func__, + cur_buf->mem, next_buf->mem, buf_count); + } else { + dev_err(rtd->dev, + "%s: SB get status, invalid len = 0x%x\n", + __func__, done_len); + } + done_len = 0; + } + +done: + if (rc) + lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR; + pr_debug("%s: Exit lab_thread, exit_status=%d, thread_status=%d\n", + __func__, rc, lab_d->thread_status); + complete(&lab_d->thread_complete); + + return 0; +} + +/* + * msm_cpe_lsm_open: ASoC call to open the stream + * @substream: substream that is to be opened + * + * Create session data for lsm session and open the lsm session + * on CPE. + */ +static int msm_cpe_lsm_open(struct snd_pcm_substream *substream) +{ + struct cpe_lsm_data *lsm_d; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + int rc = 0; + + if (!cpe || !cpe->codec) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + runtime->hw = msm_pcm_hardware_listen; + + rc = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (rc < 0) { + pr_err("snd_pcm_hw_constraint_list failed rc %d\n", rc); + return -EINVAL; + } + + /* Ensure that buffer size is a multiple of period size */ + rc = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (rc < 0) { + pr_err("%s: Unable to set pcm_param_periods, rc %d\n", + __func__, rc); + return -EINVAL; + } + + rc = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + LISTEN_MIN_NUM_PERIODS * LISTEN_MIN_PERIOD_SIZE, + LISTEN_MAX_NUM_PERIODS * LISTEN_MAX_PERIOD_SIZE); + if (rc < 0) { + pr_err("%s: Unable to set pcm constraints, rc %d\n", + __func__, rc); + return -EINVAL; + } + + cpe->core_handle = wcd_cpe_get_core_handle(cpe->codec); + + if (!cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid handle to codec core\n", + __func__); + return -EINVAL; + } + + lsm_ops = &cpe->lsm_ops; + lsm_d = kzalloc(sizeof(struct cpe_lsm_data), GFP_KERNEL); + if (!lsm_d) { + dev_err(rtd->dev, + "%s: ENOMEM for lsm session, size = %zd\n", + __func__, sizeof(struct cpe_lsm_data)); + rc = -ENOMEM; + goto fail_return; + } + mutex_init(&lsm_d->lsm_api_lock); + + lsm_d->lsm_session = lsm_ops->lsm_alloc_session(cpe->core_handle, + lsm_d, msm_cpe_process_event_status); + if (!lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: session allocation failed", + __func__); + rc = -EINVAL; + goto fail_session_alloc; + } + /* Explicitly Assign the LAB thread to STOP state */ + lsm_d->lab.thread_status = MSM_LSM_LAB_THREAD_STOP; + lsm_d->lsm_session->started = false; + lsm_d->substream = substream; + init_waitqueue_head(&lsm_d->lab.period_wait); + lsm_d->cpe_prepared = false; + + dev_dbg(rtd->dev, "%s: allocated session with id = %d\n", + __func__, lsm_d->lsm_session->id); + + + rc = lsm_ops->lsm_open_tx(cpe->core_handle, lsm_d->lsm_session, + LSM_VOICE_WAKEUP_APP_V2, 16000); + if (rc < 0) { + dev_err(rtd->dev, + "%s: OPEN_TX cmd failed, err = %d\n", + __func__, rc); + goto fail_open_tx; + } + + init_waitqueue_head(&lsm_d->event_wait); + atomic_set(&lsm_d->event_avail, 0); + atomic_set(&lsm_d->event_stop, 0); + runtime->private_data = lsm_d; + + return 0; + +fail_open_tx: + lsm_ops->lsm_dealloc_session(cpe->core_handle, lsm_d->lsm_session); + +fail_session_alloc: + mutex_destroy(&lsm_d->lsm_api_lock); + kfree(lsm_d); +fail_return: + return rc; +} + +/* + * msm_cpe_lsm_close: ASoC call to close/cleanup the stream + * @substream: substream that is to be closed + * + * Deallocate the session and release the AFE port. It is not + * required to deregister the sound model as long as we close + * the lsm session on CPE. + */ +static int msm_cpe_lsm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + struct cpe_lsm_session *session; + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *afe_cfg; + int rc = 0; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + lsm_ops = &cpe->lsm_ops; + session = lsm_d->lsm_session; + afe_ops = &cpe->afe_ops; + afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); + + /* + * If driver is closed without stopping LAB, + * explicitly stop LAB before cleaning up the + * driver resources. + */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to stop lab, error = %d\n", + __func__, rc); + return rc; + } + + rc = msm_cpe_afe_port_cntl(substream, + cpe->core_handle, + afe_ops, afe_cfg, + AFE_CMD_PORT_STOP); + + lsm_d->cpe_prepared = false; + + rc = lsm_ops->lsm_close_tx(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: lsm_close fail, err = %d\n", + __func__, rc); + return rc; + } + + lsm_ops->lsm_dealloc_session(cpe->core_handle, session); + runtime->private_data = NULL; + mutex_destroy(&lsm_d->lsm_api_lock); + kfree(lsm_d); + + return rc; +} + +static int msm_cpe_lsm_get_conf_levels( + struct cpe_lsm_session *session, + u8 *conf_levels_ptr) +{ + int rc = 0; + + if (session->num_confidence_levels <= 0) { + pr_debug("%s: conf_levels (%u), skip set params\n", + __func__, + session->num_confidence_levels); + goto done; + } + + session->conf_levels = kzalloc(session->num_confidence_levels, + GFP_KERNEL); + if (!session->conf_levels) { + rc = -ENOMEM; + goto done; + } + + if (copy_from_user(session->conf_levels, + conf_levels_ptr, + session->num_confidence_levels)) { + pr_err("%s: copy_from_user failed for confidence levels %u\n", + __func__, session->num_confidence_levels); + kfree(session->conf_levels); + session->conf_levels = NULL; + rc = -EFAULT; + goto done; + } + +done: + return rc; +} + +static int msm_cpe_lsm_validate_out_format( + struct snd_pcm_substream *substream, + struct snd_lsm_output_format_cfg *cfg) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (!cfg) { + dev_err(rtd->dev, + "%s: Invalid lsm out cfg\n", __func__); + rc = -EINVAL; + goto done; + } + + if (cfg->format != LSM_OUT_FORMAT_PCM && + cfg->format != LSM_OUT_FORMAT_ADPCM) { + dev_err(rtd->dev, + "%s: Invalid format %u\n", + __func__, cfg->format); + rc = -EINVAL; + goto done; + } + + if (cfg->packing != LSM_OUT_DATA_RAW && + cfg->packing != LSM_OUT_DATA_PACKED) { + dev_err(rtd->dev, + "%s: Invalid packing method %u\n", + __func__, cfg->packing); + rc = -EINVAL; + goto done; + } + + if (cfg->events != LSM_OUT_DATA_EVENTS_DISABLED && + cfg->events != LSM_OUT_DATA_EVENTS_ENABLED) { + dev_err(rtd->dev, + "%s: Invalid events provided %u\n", + __func__, cfg->events); + rc = -EINVAL; + goto done; + } + + if (cfg->mode != LSM_OUT_TRANSFER_MODE_RT && + cfg->mode != LSM_OUT_TRANSFER_MODE_FTRT) { + dev_err(rtd->dev, + "%s: Invalid transfer mode %u\n", + __func__, cfg->mode); + rc = -EINVAL; + goto done; + } + +done: + return rc; +} + +/* + * msm_cpe_lsm_ioctl_shared: Shared IOCTL for this platform driver + * @substream: ASoC substream for which the operation is invoked + * @cmd: command for the ioctl + * @arg: argument for the ioctl + * + * Perform dedicated listen functions like register sound model, + * deregister sound model, etc + * Called with lsm_api_lock acquired. + */ +static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_lsm_sound_model_v2 snd_model; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_session *session; + struct wcd_cpe_lsm_ops *lsm_ops; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct msm_slim_dma_data *dma_data = NULL; + struct snd_lsm_event_status *user; + struct snd_lsm_detection_params det_params; + int rc = 0; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + switch (cmd) { + case SNDRV_LSM_STOP_LAB: + dev_dbg(rtd->dev, + "%s: %s, lab_enable = %d, lab_thread_ststus = %d\n", + __func__, "SNDRV_LSM_STOP_LAB", + session->lab_enable, + lab_d->thread_status); + + if (session->lab_enable && + lab_d->thread_status != MSM_LSM_LAB_THREAD_STOP) { + atomic_inc(&lab_d->abort_read); + wake_up(&lab_d->period_wait); + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: stop LAB failed, error = %d\n", + __func__, rc); + return rc; + } + } else if (!session->lab_enable) { + dev_dbg(rtd->dev, + "%s: LAB already stopped\n", + __func__); + } + + break; + + case SNDRV_LSM_LAB_CONTROL: + if (copy_from_user(&session->lab_enable, (void *)arg, + sizeof(u32))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size %zd\n", + __func__, sizeof(u32)); + return -EFAULT; + } + + dev_dbg(rtd->dev, + "%s: %s, lab_enable = %d\n", + __func__, "SNDRV_LSM_LAB_CONTROL", + session->lab_enable); + if (rtd->cpu_dai) + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) { + dev_err(rtd->dev, + "%s: dma_data is not set\n", __func__); + return -EINVAL; + } + + if (session->lab_enable) { + rc = msm_cpe_lab_buf_alloc(substream, + session, dma_data); + if (IS_ERR_VALUE(rc)) { + dev_err(rtd->dev, + "%s: lab buffer alloc failed, err = %d\n", + __func__, rc); + return rc; + } + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = lab_d->pcm_buf[0].mem; + dma_buf->addr = lab_d->pcm_buf[0].phys; + dma_buf->bytes = (lsm_d->hw_params.buf_sz * + lsm_d->hw_params.period_count); + init_completion(&lab_d->thread_complete); + snd_pcm_set_runtime_buffer(substream, + &substream->dma_buffer); + rc = lsm_ops->lsm_lab_control(cpe->core_handle, + session, true); + if (IS_ERR_VALUE(rc)) { + dev_err(rtd->dev, + "%s: Lab Enable Failed rc %d\n", + __func__, rc); + return rc; + } + } else { + /* + * It is possible that lab is still enabled + * when trying to de-allocate the lab buffer. + * Make sure to disable lab before de-allocating + * the lab buffer. + */ + rc = msm_cpe_lsm_lab_stop(substream); + if (IS_ERR_VALUE(rc)) { + dev_err(rtd->dev, + "%s: LAB stop failed, error = %d\n", + __func__, rc); + return rc; + } + /* + * Buffer has to be de-allocated even if + * lab_control failed. + */ + rc = msm_cpe_lab_buf_dealloc(substream, + session, dma_data); + if (IS_ERR_VALUE(rc)) { + dev_err(rtd->dev, + "%s: lab buffer free failed, err = %d\n", + __func__, rc); + return rc; + } + } + break; + case SNDRV_LSM_REG_SND_MODEL_V2: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_REG_SND_MODEL_V2"); + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid argument to ioctl %s\n", + __func__, + "SNDRV_LSM_REG_SND_MODEL_V2"); + return -EINVAL; + } + + memcpy(&snd_model, arg, + sizeof(struct snd_lsm_sound_model_v2)); + + session->num_confidence_levels = + snd_model.num_confidence_levels; + rc = msm_cpe_lsm_get_conf_levels(session, + snd_model.confidence_level); + if (rc) { + dev_err(rtd->dev, + "%s: %s get_conf_levels fail, err = %d\n", + __func__, "SNDRV_LSM_REG_SND_MODEL_V2", + rc); + break; + } + + session->snd_model_data = kzalloc(snd_model.data_size, + GFP_KERNEL); + if (!session->snd_model_data) { + kfree(session->conf_levels); + session->conf_levels = NULL; + return -ENOMEM; + } + session->snd_model_size = snd_model.data_size; + + if (copy_from_user(session->snd_model_data, + snd_model.data, snd_model.data_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for snd_model\n", + __func__); + kfree(session->conf_levels); + kfree(session->snd_model_data); + session->conf_levels = NULL; + session->snd_model_data = NULL; + return -EFAULT; + } + + rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session, + session->snd_model_size); + if (rc != 0) { + dev_err(rtd->dev, + "%s: shared memory allocation failed, err = %d\n", + __func__, rc); + kfree(session->snd_model_data); + kfree(session->conf_levels); + session->snd_model_data = NULL; + session->conf_levels = NULL; + return rc; + } + + rc = lsm_ops->lsm_register_snd_model(cpe->core_handle, session, + snd_model.detection_mode, + snd_model.detect_failure); + if (rc != 0) { + dev_err(rtd->dev, + "%s: snd_model_reg failed, err = %d\n", + __func__, rc); + lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); + kfree(session->snd_model_data); + kfree(session->conf_levels); + session->snd_model_data = NULL; + session->conf_levels = NULL; + return rc; + } + + break; + + case SNDRV_LSM_DEREG_SND_MODEL: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_DEREG_SND_MODEL"); + + if (session->lab_enable) { + /* + * It is possible that lab is still enabled + * when trying to deregister sound model. + * Make sure to disable lab before de-allocating + * the lab buffer. + */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: LAB stop failed, error = %d\n", + __func__, rc); + return rc; + } + + rc = lsm_ops->lsm_lab_control(cpe->core_handle, + session, false); + if (rc) + dev_err(rtd->dev, + "%s: Lab Disable Failed rc %d\n", + __func__, rc); + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) + dev_err(rtd->dev, + "%s: dma_data is not set\n", __func__); + + /* + * Buffer has to be de-allocated even if + * lab_control failed and/or dma data is invalid. + */ + rc = msm_cpe_lab_buf_dealloc(substream, + session, dma_data); + if (IS_ERR_VALUE(rc)) + dev_err(rtd->dev, + "%s: lab buffer free failed, err = %d\n", + __func__, rc); + } + + rc = lsm_ops->lsm_deregister_snd_model( + cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: snd_model de-reg failed, err = %d\n", + __func__, rc); + return rc; + } + + kfree(session->snd_model_data); + kfree(session->conf_levels); + session->snd_model_data = NULL; + session->conf_levels = NULL; + + rc = lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: LSM shared memory dealloc failed, err = %d\n", + __func__, rc); + return rc; + } + + break; + + case SNDRV_LSM_EVENT_STATUS: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS"); + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid argument to ioctl %s\n", + __func__, + "SNDRV_LSM_EVENT_STATUS"); + return -EINVAL; + } + + user = arg; + + /* + * Release the api lock before wait to allow + * other IOCTLs to be invoked while waiting + * for event + */ + MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + rc = wait_event_freezable(lsm_d->event_wait, + (atomic_read(&lsm_d->event_avail) == 1) || + (atomic_read(&lsm_d->event_stop) == 1)); + + MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + if (!rc) { + if (atomic_read(&lsm_d->event_avail) == 1) { + rc = 0; + atomic_set(&lsm_d->event_avail, 0); + if (lsm_d->ev_det_pld_size > + user->payload_size) { + dev_err(rtd->dev, + "%s: avail pld_bytes = %u, needed = %u\n", + __func__, + user->payload_size, + lsm_d->ev_det_pld_size); + return -EINVAL; + } + + user->status = lsm_d->ev_det_status; + user->payload_size = lsm_d->ev_det_pld_size; + + memcpy(user->payload, + lsm_d->ev_det_payload, + lsm_d->ev_det_pld_size); + + } else if (atomic_read(&lsm_d->event_stop) == 1) { + dev_dbg(rtd->dev, + "%s: wait_aborted\n", __func__); + user->payload_size = 0; + rc = 0; + } + } + + break; + + case SNDRV_LSM_ABORT_EVENT: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_ABORT_EVENT"); + atomic_set(&lsm_d->event_stop, 1); + wake_up(&lsm_d->event_wait); + break; + + case SNDRV_LSM_START: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_START"); + rc = lsm_ops->lsm_start(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: lsm_start fail, err = %d\n", + __func__, rc); + return rc; + } + session->started = true; + break; + + case SNDRV_LSM_STOP: + dev_dbg(rtd->dev, + "%s: %s, lab_enable = %d, lab_thread_status = %d\n", + __func__, "SNDRV_LSM_STOP", + session->lab_enable, + lab_d->thread_status); + if ((session->lab_enable && + lab_d->thread_status == + MSM_LSM_LAB_THREAD_RUNNING)) { + /* Explicitly stop LAB */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: lab_stop failed, err = %d\n", + __func__, rc); + return rc; + } + } + + rc = lsm_ops->lsm_stop(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: lsm_stop fail err = %d\n", + __func__, rc); + + return rc; + } + session->started = false; + break; + + case SNDRV_LSM_SET_PARAMS: + if (!arg) { + dev_err(rtd->dev, + "%s: %s Invalid argument\n", + __func__, "SNDRV_LSM_SET_PARAMS"); + return -EINVAL; + } + memcpy(&det_params, arg, + sizeof(det_params)); + if (det_params.num_confidence_levels <= 0) { + dev_err(rtd->dev, + "%s: %s: Invalid confidence levels %u\n", + __func__, "SNDRV_LSM_SET_PARAMS", + det_params.num_confidence_levels); + return -EINVAL; + } + + session->num_confidence_levels = + det_params.num_confidence_levels; + rc = msm_cpe_lsm_get_conf_levels(session, + det_params.conf_level); + if (rc) { + dev_err(rtd->dev, + "%s: %s get_conf_levels fail, err = %d\n", + __func__, "SNDRV_LSM_SET_PARAMS", + rc); + break; + } + + rc = lsm_ops->lsm_set_data(cpe->core_handle, session, + det_params.detect_mode, + det_params.detect_failure); + if (rc) { + dev_err(rtd->dev, + "%s: lsm_set_data failed, err = %d\n", + __func__, rc); + return rc; + } + + kfree(session->conf_levels); + session->conf_levels = NULL; + + break; + + case SNDRV_LSM_OUT_FORMAT_CFG: { + struct snd_lsm_output_format_cfg u_fmt_cfg; + + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid argument to ioctl %s\n", + __func__, "SNDRV_LSM_OUT_FORMAT_CFG"); + return -EINVAL; + } + + if (copy_from_user(&u_fmt_cfg, arg, + sizeof(u_fmt_cfg))) { + dev_err(rtd->dev, + "%s: copy_from_user failed for out_fmt_cfg\n", + __func__); + return -EFAULT; + } + + if (msm_cpe_lsm_validate_out_format(substream, + &u_fmt_cfg)) + return -EINVAL; + + session->out_fmt_cfg.format = u_fmt_cfg.format; + session->out_fmt_cfg.pack_mode = u_fmt_cfg.packing; + session->out_fmt_cfg.data_path_events = + u_fmt_cfg.events; + session->out_fmt_cfg.transfer_mode = u_fmt_cfg.mode; + + rc = lsm_ops->lsm_set_fmt_cfg(cpe->core_handle, + session); + if (rc) { + dev_err(rtd->dev, + "%s: lsm_set_fmt_cfg failed, err = %d\n", + __func__, rc); + return rc; + } + } + break; + + default: + dev_dbg(rtd->dev, + "%s: Default snd_lib_ioctl cmd 0x%x\n", + __func__, cmd); + rc = snd_pcm_lib_ioctl(substream, cmd, arg); + } + + return rc; +} + +static int msm_cpe_lsm_lab_start(struct snd_pcm_substream *substream, + struct snd_lsm_event_status *event_status) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct cpe_lsm_lab *lab_d = NULL; + struct cpe_hw_params *hw_params; + struct wcd_cpe_lsm_ops *lsm_ops; + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *out_port; + int rc; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + __func__, substream); + return -EINVAL; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + lab_d = &lsm_d->lab; + afe_ops = &cpe->afe_ops; + hw_params = &lsm_d->hw_params; + + if (!session->started) { + dev_dbg(rtd->dev, + "%s: Session is stopped, cannot start LAB\n", + __func__); + return 0; + } + + reinit_completion(&lab_d->thread_complete); + + if (session->lab_enable && + event_status->status == + LSM_VOICE_WAKEUP_STATUS_DETECTED) { + out_port = &session->afe_out_port_cfg; + out_port->port_id = session->afe_out_port_id; + out_port->bit_width = hw_params->sample_size; + out_port->num_channels = hw_params->channels; + out_port->sample_rate = hw_params->sample_rate; + dev_dbg(rtd->dev, "%s: port_id= %u, bit_width= %u, rate= %u\n", + __func__, out_port->port_id, out_port->bit_width, + out_port->sample_rate); + + rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, + out_port); + if (rc) { + dev_err(rtd->dev, + "%s: Failed afe generic config v2, err = %d\n", + __func__, rc); + return rc; + } + + atomic_set(&lab_d->abort_read, 0); + dev_dbg(rtd->dev, + "%s: KW detected, scheduling LAB thread\n", + __func__); + + /* + * Even though thread might be only scheduled and + * not currently running, mark the internal driver + * status to running so driver can cancel this thread + * if it needs to before the thread gets chance to run. + */ + lab_d->thread_status = MSM_LSM_LAB_THREAD_RUNNING; + session->lsm_lab_thread = kthread_run( + msm_cpe_lab_thread, + lsm_d, + "lab_thread"); + } + + return 0; +} + +static bool msm_cpe_lsm_is_valid_stream(struct snd_pcm_substream *substream, + const char *func) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + func, substream); + return false; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + func); + return false; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + func); + return false; + } + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (!lsm_ops) { + dev_err(rtd->dev, + "%s: Invalid lsm_ops\n", func); + return false; + } + + return true; +} + +static int msm_cpe_lsm_set_epd(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + struct snd_lsm_ep_det_thres epd_thres; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size != sizeof(epd_thres)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&epd_thres, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, &epd_thres, + LSM_ENDPOINT_DETECT_THRESHOLD); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(epd_threshold) failed, rc %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_cpe_lsm_set_mode(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + struct snd_lsm_detect_mode det_mode; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size != sizeof(det_mode)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&det_mode, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, &det_mode, + LSM_OPERATION_MODE); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(epd_threshold) failed, rc %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_cpe_lsm_set_gain(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + struct snd_lsm_gain gain; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size != sizeof(gain)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&gain, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, &gain, + LSM_GAIN); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(epd_threshold) failed, rc %d\n", + __func__, rc); +done: + return rc; + +} + +static int msm_cpe_lsm_set_conf(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + session->num_confidence_levels = + p_info->param_size; + rc = msm_cpe_lsm_get_conf_levels(session, + p_info->param_data); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, NULL, + LSM_MIN_CONFIDENCE_LEVELS); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(conf_levels) failed, rc %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_cpe_lsm_reg_model(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + int rc; + size_t offset; + u8 *snd_model_ptr; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + lsm_ops->lsm_get_snd_model_offset(cpe->core_handle, + session, &offset); + session->snd_model_size = p_info->param_size + offset; + + session->snd_model_data = vzalloc(session->snd_model_size); + if (!session->snd_model_data) + return -ENOMEM; + + snd_model_ptr = ((u8 *) session->snd_model_data) + offset; + + if (copy_from_user(snd_model_ptr, + p_info->param_data, p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user for snd_model failed\n", + __func__); + rc = -EFAULT; + goto free_snd_model_data; + } + + rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session, + session->snd_model_size); + if (rc != 0) { + dev_err(rtd->dev, + "%s: shared memory allocation failed, err = %d\n", + __func__, rc); + rc = -EINVAL; + goto free_snd_model_data; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, NULL, + LSM_REG_SND_MODEL); + if (unlikely(rc)) { + dev_err(rtd->dev, + "%s: set_one_param(snd_model) failed, rc %d\n", + __func__, rc); + goto dealloc_shmem; + } + return 0; + +dealloc_shmem: + lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); + +free_snd_model_data: + vfree(session->snd_model_data); + return rc; +} + +static int msm_cpe_lsm_dereg_model(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, NULL, + LSM_DEREG_SND_MODEL); + if (rc) + dev_err(rtd->dev, + "%s: dereg_snd_model failed\n", + __func__); + return lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); +} + +static int msm_cpe_lsm_set_custom(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + u8 *data; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size > MSM_CPE_MAX_CUSTOM_PARAM_SIZE) { + dev_err(rtd->dev, + "%s: invalid size %d, max allowed %d\n", + __func__, p_info->param_size, + MSM_CPE_MAX_CUSTOM_PARAM_SIZE); + return -EINVAL; + } + + data = kzalloc(p_info->param_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for custom params, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_ret; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, data, + LSM_CUSTOM_PARAMS); + if (rc) + dev_err(rtd->dev, + "%s: custom_params failed, err = %d\n", + __func__, rc); +err_ret: + kfree(data); + return rc; +} + +static int msm_cpe_lsm_process_params(struct snd_pcm_substream *substream, + struct snd_lsm_module_params *p_data, + void *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct lsm_params_info *p_info; + int i; + int rc = 0; + + p_info = (struct lsm_params_info *) params; + + for (i = 0; i < p_data->num_params; i++) { + dev_dbg(rtd->dev, + "%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n", + __func__, i, p_info->module_id, + p_info->param_id, p_info->param_size, + p_info->param_type); + + switch (p_info->param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = msm_cpe_lsm_set_epd(substream, p_info); + break; + case LSM_OPERATION_MODE: + rc = msm_cpe_lsm_set_mode(substream, p_info); + break; + case LSM_GAIN: + rc = msm_cpe_lsm_set_gain(substream, p_info); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = msm_cpe_lsm_set_conf(substream, p_info); + break; + case LSM_REG_SND_MODEL: + rc = msm_cpe_lsm_reg_model(substream, p_info); + break; + case LSM_DEREG_SND_MODEL: + rc = msm_cpe_lsm_dereg_model(substream, p_info); + break; + case LSM_CUSTOM_PARAMS: + rc = msm_cpe_lsm_set_custom(substream, p_info); + break; + default: + dev_err(rtd->dev, + "%s: Invalid param_type %d\n", + __func__, p_info->param_type); + rc = -EINVAL; + break; + } + if (rc) { + pr_err("%s: set_param fail for param_type %d\n", + __func__, p_info->param_type); + return rc; + } + + p_info++; + } + + return rc; +} + +static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + struct snd_soc_pcm_runtime *rtd; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + __func__, substream); + return -EINVAL; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + switch (cmd) { + case SNDRV_LSM_REG_SND_MODEL_V2: { + struct snd_lsm_sound_model_v2 snd_model; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "LSM_REG_SND_MODEL_V2"); + return -EINVAL; + } + + if (copy_from_user(&snd_model, (void *)arg, + sizeof(struct snd_lsm_sound_model_v2))) { + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_sound_model_v2)); + err = -EFAULT; + goto done; + } + + err = msm_cpe_lsm_ioctl_shared(substream, cmd, + &snd_model); + } + break; + case SNDRV_LSM_EVENT_STATUS: { + struct snd_lsm_event_status u_event_status; + struct snd_lsm_event_status *event_status = NULL; + int u_pld_size = 0; + + if (copy_from_user(&u_event_status, (void *)arg, + sizeof(struct snd_lsm_event_status))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status)); + err = -EFAULT; + goto done; + } + + if (u_event_status.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, u_event_status.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + u_pld_size = sizeof(struct snd_lsm_event_status) + + u_event_status.payload_size; + + event_status = kzalloc(u_pld_size, GFP_KERNEL); + if (!event_status) { + err = -ENOMEM; + goto done; + } else { + event_status->payload_size = + u_event_status.payload_size; + err = msm_cpe_lsm_ioctl_shared(substream, + cmd, event_status); + } + + if (!err && copy_to_user(arg, event_status, u_pld_size)) { + dev_err(rtd->dev, + "%s: copy to user failed\n", + __func__); + kfree(event_status); + err = -EFAULT; + goto done; + } + + msm_cpe_lsm_lab_start(substream, event_status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + } + break; + case SNDRV_LSM_SET_PARAMS: { + struct snd_lsm_detection_params det_params; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SNDRV_LSM_SET_PARAMS"); + return -EINVAL; + } + + if (copy_from_user(&det_params, (void *) arg, + sizeof(det_params))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS", + sizeof(det_params)); + err = -EFAULT; + goto done; + } + + err = msm_cpe_lsm_ioctl_shared(substream, cmd, + &det_params); + } + break; + + case SNDRV_LSM_SET_MODULE_PARAMS: { + struct snd_lsm_module_params p_data; + size_t p_size; + u8 *params; + + if (!session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS"); + return -EINVAL; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: %s: No Param data to set\n", + __func__, "SET_MODULE_PARAMS"); + return -EINVAL; + } + + if (copy_from_user(&p_data, arg, + sizeof(p_data))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "p_data", sizeof(p_data)); + return -EFAULT; + } + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS", + p_data.num_params); + return -EINVAL; + } + + p_size = p_data.num_params * + sizeof(struct lsm_params_info); + + if (p_data.data_size != p_size) { + dev_err(rtd->dev, + "%s: %s: Invalid size %zd\n", + __func__, "SET_MODULE_PARAMS", p_size); + + return -EFAULT; + } + + params = kzalloc(p_size, GFP_KERNEL); + if (!params) + return -ENOMEM; + + if (copy_from_user(params, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params", p_data.data_size); + kfree(params); + return -EFAULT; + } + + err = msm_cpe_lsm_process_params(substream, &p_data, params); + if (err) + dev_err(rtd->dev, + "%s: %s: Failed to set params, err = %d\n", + __func__, "SET_MODULE_PARAMS", err); + kfree(params); + break; + } + default: + err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg); + break; + } + +done: + MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + return err; +} + +#ifdef CONFIG_COMPAT +struct snd_lsm_event_status32 { + u16 status; + u16 payload_size; + u8 payload[0]; +}; + +struct snd_lsm_sound_model_v2_32 { + compat_uptr_t data; + compat_uptr_t confidence_level; + u32 data_size; + enum lsm_detection_mode detection_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct snd_lsm_detection_params_32 { + compat_uptr_t conf_level; + enum lsm_detection_mode detect_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct lsm_params_info_32 { + u32 module_id; + u32 param_id; + u32 param_size; + compat_uptr_t param_data; + enum LSM_PARAM_TYPE param_type; +}; + +struct snd_lsm_module_params_32 { + compat_uptr_t params; + u32 num_params; + u32 data_size; +}; + +enum { + SNDRV_LSM_EVENT_STATUS32 = + _IOW('U', 0x02, struct snd_lsm_event_status32), + SNDRV_LSM_REG_SND_MODEL_V2_32 = + _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32), + SNDRV_LSM_SET_PARAMS32 = + _IOW('U', 0x0A, struct snd_lsm_detection_params_32), + SNDRV_LSM_SET_MODULE_PARAMS_32 = + _IOW('U', 0x0B, struct snd_lsm_module_params_32), +}; + +static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + struct snd_soc_pcm_runtime *rtd; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + __func__, substream); + return -EINVAL; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + switch (cmd) { + case SNDRV_LSM_REG_SND_MODEL_V2_32: { + struct snd_lsm_sound_model_v2 snd_model; + struct snd_lsm_sound_model_v2_32 snd_model32; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "LSM_REG_SND_MODEL_V2_32"); + return -EINVAL; + } + + dev_dbg(rtd->dev, + "%s: ioctl %s\n", __func__, + "SNDRV_LSM_REG_SND_MODEL_V2_32"); + + if (copy_from_user(&snd_model32, (void *)arg, + sizeof(snd_model32))) { + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, + sizeof(snd_model32)); + err = -EFAULT; + goto done; + } + + snd_model.data = compat_ptr(snd_model32.data); + snd_model.confidence_level = + compat_ptr(snd_model32.confidence_level); + snd_model.data_size = snd_model32.data_size; + snd_model.detect_failure = snd_model32.detect_failure; + snd_model.num_confidence_levels = + snd_model32.num_confidence_levels; + snd_model.detection_mode = snd_model32.detection_mode; + + cmd = SNDRV_LSM_REG_SND_MODEL_V2; + err = msm_cpe_lsm_ioctl_shared(substream, cmd, &snd_model); + if (err) + dev_err(rtd->dev, + "%s: %s failed, error = %d\n", + __func__, + "SNDRV_LSM_REG_SND_MODEL_V2_32", + err); + } + break; + case SNDRV_LSM_EVENT_STATUS32: { + struct snd_lsm_event_status *event_status = NULL; + struct snd_lsm_event_status u_event_status32; + struct snd_lsm_event_status *udata_32 = NULL; + int u_pld_size = 0; + + dev_dbg(rtd->dev, + "%s: ioctl %s\n", __func__, + "SNDRV_LSM_EVENT_STATUS32"); + + if (copy_from_user(&u_event_status32, (void *)arg, + sizeof(struct snd_lsm_event_status))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status)); + err = -EFAULT; + goto done; + } + + if (u_event_status32.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, u_event_status32.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + u_pld_size = sizeof(struct snd_lsm_event_status) + + u_event_status32.payload_size; + event_status = kzalloc(u_pld_size, GFP_KERNEL); + if (!event_status) { + dev_err(rtd->dev, + "%s: No memory for event status\n", + __func__); + err = -ENOMEM; + goto done; + } else { + event_status->payload_size = + u_event_status32.payload_size; + cmd = SNDRV_LSM_EVENT_STATUS; + err = msm_cpe_lsm_ioctl_shared(substream, + cmd, event_status); + if (err) + dev_err(rtd->dev, + "%s: %s failed, error = %d\n", + __func__, + "SNDRV_LSM_EVENT_STATUS32", + err); + } + + if (!err) { + udata_32 = kzalloc(u_pld_size, GFP_KERNEL); + if (!udata_32) { + dev_err(rtd->dev, + "%s: nomem for udata\n", + __func__); + err = -EFAULT; + } else { + udata_32->status = event_status->status; + udata_32->payload_size = + event_status->payload_size; + memcpy(udata_32->payload, + event_status->payload, + u_pld_size); + } + } + + if (!err && copy_to_user(arg, udata_32, + u_pld_size)) { + dev_err(rtd->dev, + "%s: copy to user failed\n", + __func__); + kfree(event_status); + kfree(udata_32); + err = -EFAULT; + goto done; + } + + msm_cpe_lsm_lab_start(substream, event_status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + kfree(udata_32); + } + break; + case SNDRV_LSM_SET_PARAMS32: { + struct snd_lsm_detection_params_32 det_params32; + struct snd_lsm_detection_params det_params; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SNDRV_LSM_SET_PARAMS32"); + return -EINVAL; + } + + if (copy_from_user(&det_params32, arg, + sizeof(det_params32))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS_32", + sizeof(det_params32)); + } else { + det_params.conf_level = + compat_ptr(det_params32.conf_level); + det_params.detect_mode = + det_params32.detect_mode; + det_params.num_confidence_levels = + det_params32.num_confidence_levels; + det_params.detect_failure = + det_params32.detect_failure; + cmd = SNDRV_LSM_SET_PARAMS; + err = msm_cpe_lsm_ioctl_shared(substream, cmd, + &det_params); + if (err) + dev_err(rtd->dev, + "%s: ioctl %s failed\n", __func__, + "SNDRV_LSM_SET_PARAMS"); + } + + break; + } + + case SNDRV_LSM_SET_MODULE_PARAMS_32: { + struct snd_lsm_module_params_32 p_data_32; + struct snd_lsm_module_params p_data; + u8 *params, *params32; + size_t p_size; + struct lsm_params_info_32 *p_info_32; + struct lsm_params_info *p_info; + int i; + + if (!session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS_32"); + return -EINVAL; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: %s: No Param data to set\n", + __func__, "SET_MODULE_PARAMS_32"); + return -EINVAL; + } + + if (copy_from_user(&p_data_32, arg, + sizeof(p_data_32))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SET_MODULE_PARAMS_32", + sizeof(p_data_32)); + return -EFAULT; + } + + p_data.params = compat_ptr(p_data_32.params); + p_data.num_params = p_data_32.num_params; + p_data.data_size = p_data_32.data_size; + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS_32", + p_data.num_params); + return -EINVAL; + } + + if (p_data.data_size != + (p_data.num_params * sizeof(struct lsm_params_info_32))) { + dev_err(rtd->dev, + "%s: %s: Invalid size %d\n", + __func__, "SET_MODULE_PARAMS_32", + p_data.data_size); + return -EINVAL; + } + + p_size = sizeof(struct lsm_params_info_32) * + p_data.num_params; + + params32 = kzalloc(p_size, GFP_KERNEL); + if (!params32) + return -ENOMEM; + + p_size = sizeof(struct lsm_params_info) * p_data.num_params; + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + kfree(params32); + return -ENOMEM; + } + + if (copy_from_user(params32, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params32", p_data.data_size); + kfree(params32); + kfree(params); + return -EFAULT; + } + + p_info_32 = (struct lsm_params_info_32 *) params32; + p_info = (struct lsm_params_info *) params; + for (i = 0; i < p_data.num_params; i++) { + p_info->module_id = p_info_32->module_id; + p_info->param_id = p_info_32->param_id; + p_info->param_size = p_info_32->param_size; + p_info->param_data = compat_ptr(p_info_32->param_data); + p_info->param_type = p_info_32->param_type; + + p_info_32++; + p_info++; + } + + err = msm_cpe_lsm_process_params(substream, + &p_data, params); + if (err) + dev_err(rtd->dev, + "%s: Failed to process params, err = %d\n", + __func__, err); + kfree(params); + kfree(params32); + break; + } + default: + err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg); + break; + } +done: + MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + return err; +} + +#else +#define msm_cpe_lsm_ioctl_compat NULL +#endif + +/* + * msm_cpe_lsm_prepare: prepare call from ASoC core for this platform + * @substream: ASoC substream for which the operation is invoked + * + * start the AFE port on CPE associated for this listen session + */ +static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream) +{ + int rc = 0; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *afe_cfg; + struct cpe_lsm_session *lsm_session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_hw_params lsm_param; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + pr_err("%s: XRUN ignore for now\n", __func__); + return 0; + } + + lsm_session = lsm_d->lsm_session; + lab_d->pcm_size = snd_pcm_lib_buffer_bytes(substream); + + dev_dbg(rtd->dev, + "%s: pcm_size 0x%x", __func__, lab_d->pcm_size); + + if (lsm_d->cpe_prepared) { + dev_dbg(rtd->dev, "%s: CPE is alredy prepared\n", + __func__); + return 0; + } + + lsm_ops = &cpe->lsm_ops; + afe_ops = &cpe->afe_ops; + afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); + + switch (cpe->input_port_id) { + case AFE_PORT_ID_3: + afe_cfg->port_id = AFE_PORT_ID_3; + afe_cfg->bit_width = 16; + afe_cfg->num_channels = 1; + afe_cfg->sample_rate = SAMPLE_RATE_48KHZ; + rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg); + break; + case AFE_PORT_ID_1: + default: + afe_cfg->port_id = AFE_PORT_ID_1; + afe_cfg->bit_width = 16; + afe_cfg->num_channels = 1; + afe_cfg->sample_rate = SAMPLE_RATE_16KHZ; + rc = afe_ops->afe_set_params(cpe->core_handle, + afe_cfg, cpe->afe_mad_ctl); + break; + } + + if (rc != 0) { + dev_err(rtd->dev, + "%s: cpe afe params failed for port = %d, err = %d\n", + __func__, afe_cfg->port_id, rc); + return rc; + } + lsm_param.sample_rate = afe_cfg->sample_rate; + lsm_param.num_chs = afe_cfg->num_channels; + lsm_param.bit_width = afe_cfg->bit_width; + rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session, + &lsm_param); + if (rc) + dev_dbg(rtd->dev, + "%s: failed to set lsm media fmt params, err = %d\n", + __func__, rc); + + /* Send connect to port (input) */ + rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session, + &cpe->input_port_id); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set connect input port, err=%d\n", + __func__, rc); + return rc; + } + + if (cpe->input_port_id != 3) { + rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle, + lsm_session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: failed to get port id, err = %d\n", + __func__, rc); + return rc; + } + /* Send connect to port (output) */ + rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session, + &lsm_session->afe_out_port_id); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set connect output port, err=%d\n", + __func__, rc); + return rc; + } + } + rc = msm_cpe_afe_port_cntl(substream, + cpe->core_handle, + afe_ops, afe_cfg, + AFE_CMD_PORT_START); + if (rc) + dev_err(rtd->dev, + "%s: cpe_afe_port start failed, err = %d\n", + __func__, rc); + else + lsm_d->cpe_prepared = true; + + return rc; +} + +/* + * msm_cpe_lsm_trigger: trigger call from ASoC core for this platform + * @substream: ASoC substream for which the operation is invoked + * @cmd: the trigger command from framework + * + * suspend/resume the AFE port on CPE associated with listen session + */ +static int msm_cpe_lsm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *afe_cfg; + int afe_cmd = AFE_CMD_INVALID; + int rc = 0; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + afe_ops = &cpe->afe_ops; + afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + afe_cmd = AFE_CMD_PORT_SUSPEND; + break; + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + afe_cmd = AFE_CMD_PORT_RESUME; + break; + + default: + afe_cmd = AFE_CMD_INVALID; + dev_dbg(rtd->dev, + "%s: unhandled trigger cmd %d\n", + __func__, cmd); + break; + } + + if (afe_cmd != AFE_CMD_INVALID) + rc = msm_cpe_afe_port_cntl(substream, + cpe->core_handle, + afe_ops, afe_cfg, + afe_cmd); + + return rc; +} + +static int msm_cpe_lsm_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_session *session = NULL; + struct cpe_hw_params *hw_params = NULL; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid %s\n", + __func__, + (!cpe) ? "cpe" : "core"); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid %s\n", + __func__, + (!lsm_d) ? "priv_data" : "session"); + return -EINVAL; + } + + session = lsm_d->lsm_session; + hw_params = &lsm_d->hw_params; + hw_params->buf_sz = (params_buffer_bytes(params) + / params_periods(params)); + hw_params->period_count = params_periods(params); + hw_params->channels = params_channels(params); + hw_params->sample_rate = params_rate(params); + + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + hw_params->sample_size = 16; + else if (params_format(params) == + SNDRV_PCM_FORMAT_S24_LE) + hw_params->sample_size = 24; + else if (params_format(params) == + SNDRV_PCM_FORMAT_S32_LE) + hw_params->sample_size = 32; + else { + dev_err(rtd->dev, + "%s: Invalid Format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + dev_dbg(rtd->dev, + "%s: Format %d buffer size(bytes) %d period count %d\n" + " Channel %d period in bytes 0x%x Period Size 0x%x rate = %d\n", + __func__, params_format(params), params_buffer_bytes(params), + params_periods(params), params_channels(params), + params_period_bytes(params), params_period_size(params), + params_rate(params)); + + return 0; +} + +static snd_pcm_uframes_t msm_cpe_lsm_pointer( + struct snd_pcm_substream *substream) +{ + + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_session *session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + + session = lsm_d->lsm_session; + if (lab_d->dma_write >= lab_d->pcm_size) + lab_d->dma_write = 0; + dev_dbg(rtd->dev, + "%s:pcm_dma_pos = %d\n", + __func__, lab_d->dma_write); + + return bytes_to_frames(runtime, (lab_d->dma_write)); +} + +static int msm_cpe_lsm_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct cpe_lsm_session *session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + char *pcm_buf; + int fbytes = 0; + int rc = 0; + + fbytes = frames_to_bytes(runtime, frames); + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + pr_err("%s: XRUN ignore for now\n", __func__); + return 0; + } + session = lsm_d->lsm_session; + + /* Check if buffer reading is already in error state */ + if (lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR) { + dev_err(rtd->dev, + "%s: Bufferring is in error state\n", + __func__); + /* + * Advance the period so there is no wait in case + * read is invoked even after error is propogated + */ + atomic_inc(&lab_d->in_count); + lab_d->dma_write += snd_pcm_lib_period_bytes(substream); + snd_pcm_period_elapsed(substream); + return -ENETRESET; + } else if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) { + dev_err(rtd->dev, + "%s: Buferring is in stopped\n", + __func__); + return -EIO; + } + + rc = wait_event_timeout(lab_d->period_wait, + (atomic_read(&lab_d->in_count) || + atomic_read(&lab_d->abort_read)), + (2 * HZ)); + if (atomic_read(&lab_d->abort_read)) { + pr_debug("%s: LSM LAB Abort read\n", __func__); + return -EIO; + } + if (lab_d->thread_status != MSM_LSM_LAB_THREAD_RUNNING) { + pr_err("%s: Lab stopped\n", __func__); + return -EIO; + } + if (!rc) { + pr_err("%s:LAB err wait_event_timeout\n", __func__); + rc = -EAGAIN; + goto fail; + } + if (lab_d->buf_idx >= (lsm_d->hw_params.period_count)) + lab_d->buf_idx = 0; + pcm_buf = (lab_d->pcm_buf[lab_d->buf_idx].mem); + pr_debug("%s: Buf IDX = 0x%x pcm_buf %pK\n", + __func__, lab_d->buf_idx, pcm_buf); + if (pcm_buf) { + if (copy_to_user(buf, pcm_buf, fbytes)) { + pr_err("Failed to copy buf to user\n"); + rc = -EFAULT; + goto fail; + } + } + lab_d->buf_idx++; + atomic_dec(&lab_d->in_count); + return 0; +fail: + return rc; +} + +/* + * msm_asoc_cpe_lsm_probe: ASoC framework for lsm platform driver + * @platform: platform registered with ASoC core + * + * Allocate the private data for this platform and obtain the ops for + * lsm and afe modules from underlying driver. Also find the codec + * for this platform as specified by machine driver for ASoC framework. + */ +static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform) +{ + struct snd_soc_card *card; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + struct cpe_priv *cpe_priv; + const struct snd_kcontrol_new *kcontrol; + bool found_runtime = false; + const char *cpe_dev_id = "qcom,msm-cpe-lsm-id"; + u32 port_id = 0; + int ret = 0; + int i; + + if (!platform || !platform->component.card) { + pr_err("%s: Invalid platform or card\n", + __func__); + return -EINVAL; + } + + card = platform->component.card; + + /* Match platform to codec */ + for (i = 0; i < card->num_links; i++) { + rtd = &card->rtd[i]; + if (!rtd->platform) + continue; + if (!strcmp(rtd->platform->component.name, + platform->component.name)) { + found_runtime = true; + break; + } + } + + if (!found_runtime) { + dev_err(platform->dev, + "%s: Failed to find runtime for platform\n", + __func__); + return -EINVAL; + } + + ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id, + &port_id); + if (ret) { + dev_dbg(platform->dev, + "%s: missing 0x%x in dt node\n", __func__, port_id); + port_id = 1; + } + + codec = rtd->codec; + + cpe_priv = kzalloc(sizeof(struct cpe_priv), + GFP_KERNEL); + if (!cpe_priv) + return -ENOMEM; + + cpe_priv->codec = codec; + cpe_priv->input_port_id = port_id; + wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops); + wcd_cpe_get_afe_ops(&cpe_priv->afe_ops); + + snd_soc_platform_set_drvdata(platform, cpe_priv); + kcontrol = &msm_cpe_kcontrols[0]; + snd_ctl_add(card->snd_card, snd_ctl_new1(kcontrol, cpe_priv)); + return 0; +} + +static struct snd_pcm_ops msm_cpe_lsm_ops = { + .open = msm_cpe_lsm_open, + .close = msm_cpe_lsm_close, + .ioctl = msm_cpe_lsm_ioctl, + .prepare = msm_cpe_lsm_prepare, + .trigger = msm_cpe_lsm_trigger, + .pointer = msm_cpe_lsm_pointer, + .copy = msm_cpe_lsm_copy, + .hw_params = msm_cpe_lsm_hwparams, + .compat_ioctl = msm_cpe_lsm_ioctl_compat, +}; + +static struct snd_soc_platform_driver msm_soc_cpe_platform = { + .ops = &msm_cpe_lsm_ops, + .probe = msm_asoc_cpe_lsm_probe, +}; + +/* + * msm_cpe_lsm_probe: platform driver probe + * @pdev: platform device + * + * Register the ASoC platform driver with ASoC core + */ +static int msm_cpe_lsm_probe(struct platform_device *pdev) +{ + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_cpe_platform); +} + +/* + * msm_cpe_lsm_remove: platform driver remove + * @pdev: platform device + * + * Deregister the ASoC platform driver + */ +static int msm_cpe_lsm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_cpe_lsm_dt_match[] = { + {.compatible = "qcom,msm-cpe-lsm" }, + { } +}; + +static struct platform_driver msm_cpe_lsm_driver = { + .driver = { + .name = "msm-cpe-lsm", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(msm_cpe_lsm_dt_match), + }, + .probe = msm_cpe_lsm_probe, + .remove = msm_cpe_lsm_remove, +}; +module_platform_driver(msm_cpe_lsm_driver); + +MODULE_DESCRIPTION("CPE LSM platform driver"); +MODULE_DEVICE_TABLE(of, msm_cpe_lsm_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c new file mode 100644 index 000000000000..081f8b4f109e --- /dev/null +++ b/sound/soc/msm/msm-dai-fe.c @@ -0,0 +1,2555 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct snd_soc_dai_ops msm_fe_dai_ops = {}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000, 352800, 384000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int multimedia_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + return 0; +} + +static int fe_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai || !dai->driver) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.stream_name; + intercon.sink = dai->driver->playback.aif_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + snd_soc_dapm_ignore_suspend(dapm, intercon.source); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.stream_name; + intercon.source = dai->driver->capture.aif_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + snd_soc_dapm_ignore_suspend(dapm, intercon.sink); + } + return 0; +} + +static struct snd_soc_dai_ops msm_fe_Multimedia_dai_ops = { + .startup = multimedia_startup, +}; + +static const struct snd_soc_component_driver msm_fe_dai_component = { + .name = "msm-dai-fe", +}; + +static struct snd_soc_dai_driver msm_fe_dais[] = { + { + .playback = { + .stream_name = "MultiMedia1 Playback", + .aif_name = "MM_DL1", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia1 Capture", + .aif_name = "MM_UL1", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia1", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia2 Playback", + .aif_name = "MM_DL2", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia2 Capture", + .aif_name = "MM_UL2", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia2", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "CS-VOICE Playback", + .aif_name = "CS-VOICE_DL1", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "CS-VOICE Capture", + .aif_name = "CS-VOICE_UL1", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "CS-VOICE", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoIP Playback", + .aif_name = "VOIP_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoIP Capture", + .aif_name = "VOIP_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoIP", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia3 Playback", + .aif_name = "MM_DL3", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 6, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia3 Capture", + .aif_name = "MM_UL3", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia3", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia4 Playback", + .aif_name = "MM_DL4", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia4", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia5 Playback", + .aif_name = "MM_DL5", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia5 Capture", + .aif_name = "MM_UL5", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia5", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia6 Playback", + .aif_name = "MM_DL6", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia6 Capture", + .aif_name = "MM_UL6", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia6", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia7 Playback", + .aif_name = "MM_DL7", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia7", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia8 Playback", + .aif_name = "MM_DL8", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia8 Capture", + .aif_name = "MM_UL8", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia8", + .probe = fe_dai_probe, + }, + /* FE DAIs created for hostless operation purpose */ + { + .playback = { + .stream_name = "SLIMBUS0_HOSTLESS Playback", + .aif_name = "SLIM0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS0_HOSTLESS Capture", + .aif_name = "SLIM0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS1_HOSTLESS Playback", + .aif_name = "SLIM1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS1_HOSTLESS Capture", + .aif_name = "SLIM1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS3_HOSTLESS Playback", + .aif_name = "SLIM3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS3_HOSTLESS Capture", + .aif_name = "SLIM3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS4_HOSTLESS Playback", + .aif_name = "SLIM4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS4_HOSTLESS Capture", + .aif_name = "SLIM4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS6_HOSTLESS Playback", + .aif_name = "SLIM6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS8_HOSTLESS Playback", + .aif_name = "SLIM8_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .aif_name = "SLIM8_UL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS8_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT_FM_HOSTLESS Playback", + .aif_name = "INTFM_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT_FM_HOSTLESS Capture", + .aif_name = "INTFM_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT_FM_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT_HFP_BT Hostless Playback", + .aif_name = "INTHFP_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + }, + .capture = { + .stream_name = "INT_HFP_BT Hostless Capture", + .aif_name = "INTHFP_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT_HFP_BT_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "AFE Playback", + .aif_name = "PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "AFE Capture", + .aif_name = "PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "AFE-PROXY", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "HDMI_HOSTLESS Playback", + .aif_name = "HDMI_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "HDMI_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "AUXPCM_HOSTLESS Playback", + .aif_name = "AUXPCM_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .capture = { + .stream_name = "AUXPCM_HOSTLESS Capture", + .aif_name = "AUXPCM_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "AUXPCM_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VOICE_STUB Playback", + .aif_name = "VOICE_STUB_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VOICE_STUB Capture", + .aif_name = "VOICE_STUB_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VOICE_STUB", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoLTE Playback", + .aif_name = "VoLTE_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoLTE Capture", + .aif_name = "VoLTE_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoLTE", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MI2S_RX_HOSTLESS Playback", + .aif_name = "MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "MI2S_TX_HOSTLESS Capture", + .aif_name = "MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SEC_I2S_RX_HOSTLESS Playback", + .aif_name = "SEC_I2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_I2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary MI2S_TX Hostless Capture", + .aif_name = "PRI_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary MI2S_RX Hostless Playback", + .aif_name = "PRI_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary MI2S_TX Hostless Capture", + .aif_name = "SEC_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary MI2S_RX Hostless Playback", + .aif_name = "SEC_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary MI2S_TX Hostless Capture", + .aif_name = "TERT_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary MI2S_RX Hostless Playback", + .aif_name = "TERT_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary MI2S_TX Hostless Capture", + .aif_name = "QUAT_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary MI2S_RX Hostless Playback", + .aif_name = "QUAT_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT0 MI2S_RX Hostless Playback", + .aif_name = "INT0_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT0_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT4 MI2S_RX Hostless Playback", + .aif_name = "INT4_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT4_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + /* TDM Hostless */ + { + .capture = { + .stream_name = "Primary TDM0 Hostless Capture", + .aif_name = "PRI_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM0 Hostless Playback", + .aif_name = "PRI_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM1 Hostless Capture", + .aif_name = "PRI_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM1 Hostless Playback", + .aif_name = "PRI_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM2 Hostless Capture", + .aif_name = "PRI_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM2 Hostless Playback", + .aif_name = "PRI_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM3 Hostless Capture", + .aif_name = "PRI_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM3 Hostless Playback", + .aif_name = "PRI_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM4 Hostless Capture", + .aif_name = "PRI_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM4 Hostless Playback", + .aif_name = "PRI_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM5 Hostless Capture", + .aif_name = "PRI_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM5 Hostless Playback", + .aif_name = "PRI_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM6 Hostless Capture", + .aif_name = "PRI_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM6 Hostless Playback", + .aif_name = "PRI_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM7 Hostless Capture", + .aif_name = "PRI_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM7 Hostless Playback", + .aif_name = "PRI_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM0 Hostless Capture", + .aif_name = "SEC_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM0 Hostless Playback", + .aif_name = "SEC_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM1 Hostless Capture", + .aif_name = "SEC_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM1 Hostless Playback", + .aif_name = "SEC_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM2 Hostless Capture", + .aif_name = "SEC_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM2 Hostless Playback", + .aif_name = "SEC_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM3 Hostless Capture", + .aif_name = "SEC_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM3 Hostless Playback", + .aif_name = "SEC_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM4 Hostless Capture", + .aif_name = "SEC_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM4 Hostless Playback", + .aif_name = "SEC_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM5 Hostless Capture", + .aif_name = "SEC_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM5 Hostless Playback", + .aif_name = "SEC_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM6 Hostless Capture", + .aif_name = "SEC_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM6 Hostless Playback", + .aif_name = "SEC_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM7 Hostless Capture", + .aif_name = "SEC_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM7 Hostless Playback", + .aif_name = "SEC_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM0 Hostless Capture", + .aif_name = "TERT_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM0 Hostless Playback", + .aif_name = "TERT_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM1 Hostless Capture", + .aif_name = "TERT_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM1 Hostless Playback", + .aif_name = "TERT_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM2 Hostless Capture", + .aif_name = "TERT_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM2 Hostless Playback", + .aif_name = "TERT_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM3 Hostless Capture", + .aif_name = "TERT_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM3 Hostless Playback", + .aif_name = "TERT_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM4 Hostless Capture", + .aif_name = "TERT_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM4 Hostless Playback", + .aif_name = "TERT_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM5 Hostless Capture", + .aif_name = "TERT_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM5 Hostless Playback", + .aif_name = "TERT_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM6 Hostless Capture", + .aif_name = "TERT_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM6 Hostless Playback", + .aif_name = "TERT_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM7 Hostless Capture", + .aif_name = "TERT_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM7 Hostless Playback", + .aif_name = "TERT_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM0 Hostless Capture", + .aif_name = "QUAT_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM0 Hostless Playback", + .aif_name = "QUAT_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM1 Hostless Capture", + .aif_name = "QUAT_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM1 Hostless Playback", + .aif_name = "QUAT_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM2 Hostless Capture", + .aif_name = "QUAT_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM2 Hostless Playback", + .aif_name = "QUAT_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM3 Hostless Capture", + .aif_name = "QUAT_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM3 Hostless Playback", + .aif_name = "QUAT_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM4 Hostless Capture", + .aif_name = "QUAT_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM4 Hostless Playback", + .aif_name = "QUAT_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM5 Hostless Capture", + .aif_name = "QUAT_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM5 Hostless Playback", + .aif_name = "QUAT_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM6 Hostless Capture", + .aif_name = "QUAT_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM6 Hostless Playback", + .aif_name = "QUAT_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM7 Hostless Capture", + .aif_name = "QUAT_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM7 Hostless Playback", + .aif_name = "QUAT_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Voice2 Playback", + .aif_name = "VOICE2_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "Voice2 Capture", + .aif_name = "VOICE2_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "Voice2", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Pseudo Playback", + .aif_name = "MM_DL9", + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_KNOT), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "Pseudo Capture", + .aif_name = "MM_UL9", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "Pseudo", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "DTMF_RX_HOSTLESS Playback", + .aif_name = "DTMF_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "DTMF_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "CPE Listen Audio capture", + .aif_name = "CPE_LSM_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "CPE_LSM_NOHOST", + }, + { + .playback = { + .stream_name = "VOLTE_STUB Playback", + .aif_name = "VOLTE_STUB_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VOLTE_STUB Capture", + .aif_name = "VOLTE_STUB_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VOLTE_STUB", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VOICE2_STUB Playback", + .aif_name = "VOICE2_STUB_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VOICE2_STUB Capture", + .aif_name = "VOICE2_STUB_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VOICE2_STUB", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia9 Playback", + .aif_name = "MM_DL9", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia9 Capture", + .aif_name = "MM_UL9", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia9", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "QCHAT Playback", + .aif_name = "QCHAT_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "QCHAT Capture", + .aif_name = "QCHAT_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QCHAT", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 1 Audio Service Capture", + .aif_name = "LSM1_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM1", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 2 Audio Service Capture", + .aif_name = "LSM2_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM2", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 3 Audio Service Capture", + .aif_name = "LSM3_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM3", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 4 Audio Service Capture", + .aif_name = "LSM4_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM4", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 5 Audio Service Capture", + .aif_name = "LSM5_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM5", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 6 Audio Service Capture", + .aif_name = "LSM6_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM6", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 7 Audio Service Capture", + .aif_name = "LSM7_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM7", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 8 Audio Service Capture", + .aif_name = "LSM8_UL_HL", + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 16000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM8", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoWLAN Playback", + .aif_name = "VoWLAN_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoWLAN Capture", + .aif_name = "VoWLAN_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoWLAN", + .probe = fe_dai_probe, + }, + /* FE DAIs created for multiple instances of offload playback */ + { + .playback = { + .stream_name = "MultiMedia10 Playback", + .aif_name = "MM_DL10", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia10", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia11 Playback", + .aif_name = "MM_DL11", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia11", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia12 Playback", + .aif_name = "MM_DL12", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia12", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia13 Playback", + .aif_name = "MM_DL13", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia13", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia14 Playback", + .aif_name = "MM_DL14", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia14", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia15 Playback", + .aif_name = "MM_DL15", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia15", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia16 Playback", + .aif_name = "MM_DL16", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia16", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoiceMMode1 Playback", + .aif_name = "VOICEMMODE1_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoiceMMode1 Capture", + .aif_name = "VOICEMMODE1_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoiceMMode1", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoiceMMode2 Playback", + .aif_name = "VOICEMMODE2_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoiceMMode2 Capture", + .aif_name = "VOICEMMODE2_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoiceMMode2", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia17 Capture", + .aif_name = "MM_UL17", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia17", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia18 Capture", + .aif_name = "MM_UL18", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia18", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia19 Capture", + .aif_name = "MM_UL19", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia19", + .probe = fe_dai_probe, + }, +}; + +static int msm_fe_dai_dev_probe(struct platform_device *pdev) +{ + + dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__, + dev_name(&pdev->dev)); + return snd_soc_register_component(&pdev->dev, &msm_fe_dai_component, + msm_fe_dais, ARRAY_SIZE(msm_fe_dais)); +} + +static int msm_fe_dai_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_fe_dt_match[] = { + {.compatible = "qcom,msm-dai-fe"}, + {} +}; + +static struct platform_driver msm_fe_dai_driver = { + .probe = msm_fe_dai_dev_probe, + .remove = msm_fe_dai_dev_remove, + .driver = { + .name = "msm-dai-fe", + .owner = THIS_MODULE, + .of_match_table = msm_dai_fe_dt_match, + }, +}; + +static int __init msm_fe_dai_init(void) +{ + return platform_driver_register(&msm_fe_dai_driver); +} +module_init(msm_fe_dai_init); + +static void __exit msm_fe_dai_exit(void) +{ + platform_driver_unregister(&msm_fe_dai_driver); +} +module_exit(msm_fe_dai_exit); + +/* Module information */ +MODULE_DESCRIPTION("MSM Frontend DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/msm-pcm-hostless.c b/sound/soc/msm/msm-pcm-hostless.c new file mode 100644 index 000000000000..57932433afe9 --- /dev/null +++ b/sound/soc/msm/msm-pcm-hostless.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + + +static int msm_pcm_hostless_prepare(struct snd_pcm_substream *substream) +{ + if (!substream) { + pr_err("%s: invalid params\n", __func__); + return -EINVAL; + } + pm_qos_remove_request(&substream->latency_pm_qos_req); + return 0; +} + +static struct snd_pcm_ops msm_pcm_hostless_ops = { + .prepare = msm_pcm_hostless_prepare +}; + +static struct snd_soc_platform_driver msm_soc_hostless_platform = { + .ops = &msm_pcm_hostless_ops, +}; + +static int msm_pcm_hostless_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_hostless_platform); +} + +static int msm_pcm_hostless_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_hostless_dt_match[] = { + {.compatible = "qcom,msm-pcm-hostless"}, + {} +}; + +static struct platform_driver msm_pcm_hostless_driver = { + .driver = { + .name = "msm-pcm-hostless", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_hostless_dt_match, + }, + .probe = msm_pcm_hostless_probe, + .remove = msm_pcm_hostless_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + return platform_driver_register(&msm_pcm_hostless_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_hostless_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("Hostless platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c new file mode 100644 index 000000000000..077005a54566 --- /dev/null +++ b/sound/soc/msm/msm8996.c @@ -0,0 +1,4003 @@ +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "../codecs/wcd9xxx-common.h" +#include "../codecs/wcd9330.h" +#include "../codecs/wcd9335.h" +#include "../codecs/wsa881x.h" + +#define DRV_NAME "msm8996-asoc-snd" + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_44P1KHZ 44100 + +#define MSM8996_SPK_ON 1 +#define MSM8996_HIFI_ON 1 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define DEV_NAME_STR_LEN 32 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +static int slim0_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int slim0_tx_sample_rate = SAMPLING_RATE_48KHZ; +static int slim1_tx_sample_rate = SAMPLING_RATE_48KHZ; +static int slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int slim0_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int slim1_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ; +static int slim5_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int slim5_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int slim6_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int slim6_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +static struct platform_device *spdev; +static int ext_us_amp_gpio = -1; +static int msm8996_spk_control = 1; +static int msm_slim_0_rx_ch = 1; +static int msm_slim_0_tx_ch = 1; +static int msm_slim_1_tx_ch = 1; +static int msm_slim_5_rx_ch = 1; +static int msm_slim_6_rx_ch = 1; +static int msm_hifi_control; +static int msm_vi_feed_tx_ch = 2; + +static int msm_hdmi_rx_ch = 2; +static int msm_proxy_rx_ch = 2; +static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int msm_tert_mi2s_tx_ch = 2; + +static bool codec_reg_done; + +static const char *const hifi_function[] = {"Off", "On"}; +static const char *const pin_states[] = {"Disable", "active"}; +static const char *const spk_function[] = {"Off", "On"}; +static const char *const slim0_rx_ch_text[] = {"One", "Two"}; +static const char *const slim5_rx_ch_text[] = {"One", "Two"}; +static const char *const slim6_rx_ch_text[] = {"One", "Two"}; +static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static char const *slim5_rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static char const *slim6_rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static char const *slim0_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_44P1", "KHZ_8", + "KHZ_16", "KHZ_32"}; +static char const *slim5_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_44P1"}; +static char const *slim6_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_44P1"}; +static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; + +static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192"}; + +static const char *const auxpcm_rate_text[] = {"8000", "16000"}; +static const struct soc_enum msm8996_auxpcm_enum[] = { + SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text), +}; + +static struct afe_clk_set mi2s_tx_clk = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +struct msm8996_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +static struct snd_soc_aux_dev *msm8996_aux_dev; +static struct snd_soc_codec_conf *msm8996_codec_conf; + +struct msm8996_asoc_mach_data { + u32 mclk_freq; + int us_euro_gpio; + int hph_en1_gpio; + int hph_en0_gpio; + struct snd_info_entry *codec_root; +}; + +struct msm8996_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); +}; + +static struct msm8996_asoc_wcd93xx_codec msm8996_codec_fn; + +struct msm8996_liquid_dock_dev { + int dock_plug_gpio; + int dock_plug_irq; + int dock_plug_det; + struct work_struct irq_work; + struct switch_dev audio_sdev; +}; +static struct msm8996_liquid_dock_dev *msm8996_liquid_dock_dev; + +static void *adsp_state_notifier; +static void *def_tasha_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm8996_wsa881x_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static void msm8996_liquid_docking_irq_work(struct work_struct *work) +{ + struct msm8996_liquid_dock_dev *dock_dev = + container_of(work, struct msm8996_liquid_dock_dev, + irq_work); + + dock_dev->dock_plug_det = + gpio_get_value(dock_dev->dock_plug_gpio); + + switch_set_state(&dock_dev->audio_sdev, dock_dev->dock_plug_det); + /* notify to audio daemon */ + sysfs_notify(&dock_dev->audio_sdev.dev->kobj, NULL, "state"); +} + +static irqreturn_t msm8996_liquid_docking_irq_handler(int irq, void *dev) +{ + struct msm8996_liquid_dock_dev *dock_dev = dev; + + /* switch speakers should not run in interrupt context */ + schedule_work(&dock_dev->irq_work); + return IRQ_HANDLED; +} + +static int msm8996_liquid_init_docking(void) +{ + int ret = 0; + int dock_plug_gpio = 0; + + /* plug in docking speaker+plug in device OR unplug one of them */ + u32 dock_plug_irq_flags = IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_SHARED; + + dock_plug_gpio = of_get_named_gpio(spdev->dev.of_node, + "qcom,dock-plug-det-irq", 0); + + if (dock_plug_gpio >= 0) { + msm8996_liquid_dock_dev = + kzalloc(sizeof(*msm8996_liquid_dock_dev), GFP_KERNEL); + if (!msm8996_liquid_dock_dev) { + ret = -ENOMEM; + goto exit; + } + + msm8996_liquid_dock_dev->dock_plug_gpio = dock_plug_gpio; + + ret = gpio_request(msm8996_liquid_dock_dev->dock_plug_gpio, + "dock-plug-det-irq"); + if (ret) { + pr_err("%s:failed request msm8996_liquid_dock_plug_gpio err = %d\n", + __func__, ret); + ret = -EINVAL; + goto fail_dock_gpio; + } + + msm8996_liquid_dock_dev->dock_plug_det = + gpio_get_value( + msm8996_liquid_dock_dev->dock_plug_gpio); + msm8996_liquid_dock_dev->dock_plug_irq = + gpio_to_irq( + msm8996_liquid_dock_dev->dock_plug_gpio); + + ret = request_irq(msm8996_liquid_dock_dev->dock_plug_irq, + msm8996_liquid_docking_irq_handler, + dock_plug_irq_flags, + "liquid_dock_plug_irq", + msm8996_liquid_dock_dev); + if (ret < 0) { + pr_err("%s: Request Irq Failed err = %d\n", + __func__, ret); + goto fail_dock_gpio; + } + + msm8996_liquid_dock_dev->audio_sdev.name = + QC_AUDIO_EXTERNAL_SPK_1_EVENT; + + if (switch_dev_register( + &msm8996_liquid_dock_dev->audio_sdev) < 0) { + pr_err("%s: dock device register in switch diretory failed\n", + __func__); + goto fail_switch_dev; + } + + INIT_WORK( + &msm8996_liquid_dock_dev->irq_work, + msm8996_liquid_docking_irq_work); + } + return 0; + +fail_switch_dev: + free_irq(msm8996_liquid_dock_dev->dock_plug_irq, + msm8996_liquid_dock_dev); +fail_dock_gpio: + gpio_free(msm8996_liquid_dock_dev->dock_plug_gpio); +exit: + kfree(msm8996_liquid_dock_dev); + msm8996_liquid_dock_dev = NULL; + return ret; +} + +static void msm8996_ext_control(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + pr_debug("%s: msm8996_spk_control = %d", __func__, + msm8996_spk_control); + if (msm8996_spk_control == MSM8996_SPK_ON) { + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp"); + } else { + snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_disable_pin(dapm, "Lineout_2 amp"); + } + snd_soc_dapm_sync(dapm); +} + +static int msm8996_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm8996_spk_control = %d\n", + __func__, msm8996_spk_control); + ucontrol->value.integer.value[0] = msm8996_spk_control; + return 0; +} + +static int msm8996_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + if (msm8996_spk_control == ucontrol->value.integer.value[0]) + return 0; + + msm8996_spk_control = ucontrol->value.integer.value[0]; + msm8996_ext_control(codec); + return 1; +} + +static int msm8996_hifi_ctrl(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = codec->component.card; + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, + msm_hifi_control); + if (pdata->hph_en1_gpio < 0) { + pr_err("%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM8996_HIFI_ON) { + gpio_direction_output(pdata->hph_en1_gpio, 1); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + gpio_direction_output(pdata->hph_en1_gpio, 0); + } + snd_soc_dapm_sync(dapm); + return 0; +} + +static int msm8996_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + return 0; +} + +static int msm8996_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm8996_hifi_ctrl(codec); + return 1; +} + +static int msm8996_ext_us_amp_init(void) +{ + int ret = 0; + + ext_us_amp_gpio = of_get_named_gpio(spdev->dev.of_node, + "qcom,ext-ult-spk-amp-gpio", 0); + if (ext_us_amp_gpio >= 0) { + ret = gpio_request(ext_us_amp_gpio, "ext_us_amp_gpio"); + if (ret) { + pr_err("%s: ext_us_amp_gpio request failed, ret:%d\n", + __func__, ret); + return ret; + } + gpio_direction_output(ext_us_amp_gpio, 0); + } + return ret; +} + +static void msm8996_ext_us_amp_enable(u32 on) +{ + if (on) + gpio_direction_output(ext_us_amp_gpio, 1); + else + gpio_direction_output(ext_us_amp_gpio, 0); + + pr_debug("%s: US Emitter GPIO enable:%s\n", __func__, + on ? "Enable" : "Disable"); +} + +static int msm_ext_ultrasound_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + pr_debug("%s()\n", __func__); + if (strcmp(w->name, "ultrasound amp")) { + if (!gpio_is_valid(ext_us_amp_gpio)) { + pr_err("%s: ext_us_amp_gpio isn't configured\n", + __func__); + return -EINVAL; + } + if (SND_SOC_DAPM_EVENT_ON(event)) + msm8996_ext_us_amp_enable(1); + else + msm8996_ext_us_amp_enable(0); + } else { + pr_err("%s() Invalid Widget = %s\n", + __func__, w->name); + return -EINVAL; + } + return 0; +} + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) { + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + } else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm8996_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm8996_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct snd_soc_card *card = codec->component.card; + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + + pr_debug("%s: msm_hifi_control = %d", __func__, + msm_hifi_control); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (msm_hifi_control == MSM8996_HIFI_ON) { + if (pdata->hph_en0_gpio < 0) { + pr_err("%s: hph_en0_gpio is invalid\n", + __func__); + ret = -EINVAL; + goto err; + } + gpio_direction_output(pdata->hph_en0_gpio, 1); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (msm_hifi_control == MSM8996_HIFI_ON) { + if (pdata->hph_en0_gpio < 0) { + pr_err("%s: hph_en0_gpio is invalid\n", + __func__); + ret = -EINVAL; + goto err; + } + gpio_direction_output(pdata->hph_en0_gpio, 0); + } + break; + } +err: + return ret; +} + +static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm8996_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm8996_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_SPK("ultrasound amp", msm_ext_ultrasound_event), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + SND_SOC_DAPM_MIC("Analog Mic7", NULL), + SND_SOC_DAPM_MIC("Analog Mic8", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), + SND_SOC_DAPM_MIC("Digital Mic6", NULL), +}; + +static struct snd_soc_dapm_route wcd9335_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static int slim5_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (slim5_rx_sample_rate) { + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: slim5_rx_sample_rate = %d\n", __func__, + slim5_rx_sample_rate); + + return 0; +} + +static int slim5_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 3: + slim5_rx_sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 2: + slim5_rx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + slim5_rx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim5_rx_sample_rate = SAMPLING_RATE_48KHZ; + } + + pr_debug("%s: slim5_rx_sample_rate = %d\n", __func__, + slim5_rx_sample_rate); + + return 0; +} + +static int slim6_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (slim6_rx_sample_rate) { + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: slim6_rx_sample_rate = %d\n", __func__, + slim6_rx_sample_rate); + + return 0; +} + +static int slim6_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + slim6_rx_sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 2: + slim6_rx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + slim6_rx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim6_rx_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: ucontrol value = %ld, slim6_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + slim6_rx_sample_rate); + + return 0; +} + +static int slim0_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim0_tx_bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: slim0_tx_bit_format = %d, ucontrol value = %ld\n", + __func__, slim0_tx_bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int slim0_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 2: + slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + slim0_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + default: + pr_err("%s: invalid value %ld\n", __func__, + ucontrol->value.integer.value[0]); + rc = -EINVAL; + break; + } + + pr_debug("%s: ucontrol value = %ld, slim0_tx_bit_format = %d\n", + __func__, ucontrol->value.integer.value[0], + slim0_tx_bit_format); + + return rc; +} + +static int slim0_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (slim0_rx_sample_rate) { + case SAMPLING_RATE_32KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_16KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_8KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__, + slim0_rx_sample_rate); + + return 0; +} + +static int slim0_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 6: + slim0_rx_sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + slim0_rx_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 4: + slim0_rx_sample_rate = SAMPLING_RATE_8KHZ; + break; + case 3: + slim0_rx_sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 2: + slim0_rx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + slim0_rx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim0_rx_sample_rate = SAMPLING_RATE_48KHZ; + } + + pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__, + slim0_rx_sample_rate); + + return 0; +} + +static int slim0_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (slim0_tx_sample_rate) { + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: slim0_tx_sample_rate = %d\n", __func__, + slim0_tx_sample_rate); + return 0; +} + +static int slim0_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 2: + slim0_tx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + slim0_tx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + slim0_tx_sample_rate = SAMPLING_RATE_48KHZ; + break; + default: + rc = -EINVAL; + pr_err("%s: invalid sample rate being passed\n", __func__); + break; + } + + pr_debug("%s: slim0_tx_sample_rate = %d\n", __func__, + slim0_tx_sample_rate); + return rc; +} + +static int slim5_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (slim5_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: slim5_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, slim5_rx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int slim5_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + slim5_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + slim5_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + slim5_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return 0; +} + +static int slim6_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (slim6_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: slim6_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, slim6_rx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int slim6_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + slim6_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + slim6_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + slim6_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return 0; +} + +static int slim0_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (slim0_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: slim0_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, slim0_rx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int slim0_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return 0; +} + +static int msm_slim_5_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_slim_5_rx_ch = %d\n", __func__, + msm_slim_5_rx_ch); + ucontrol->value.integer.value[0] = msm_slim_5_rx_ch - 1; + return 0; +} + +static int msm_slim_5_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_slim_5_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_slim_5_rx_ch = %d\n", __func__, + msm_slim_5_rx_ch); + return 1; +} + +static int msm_slim_6_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_slim_6_rx_ch = %d\n", __func__, + msm_slim_6_rx_ch); + ucontrol->value.integer.value[0] = msm_slim_6_rx_ch - 1; + return 0; +} + +static int msm_slim_6_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_slim_6_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_slim_6_rx_ch = %d\n", __func__, + msm_slim_6_rx_ch); + return 1; +} + +static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__, + msm_slim_0_rx_ch); + ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1; + return 0; +} + +static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__, + msm_slim_0_rx_ch); + return 1; +} + +static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__, + msm_slim_0_tx_ch); + ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1; + return 0; +} + +static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__, msm_slim_0_tx_ch); + return 1; +} + +static int msm_slim_1_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_slim_1_tx_ch = %d\n", __func__, + msm_slim_1_tx_ch); + ucontrol->value.integer.value[0] = msm_slim_1_tx_ch - 1; + return 0; +} + +static int msm_slim_1_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_slim_1_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_slim_1_tx_ch = %d\n", __func__, msm_slim_1_tx_ch); + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (hdmi_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, hdmi_rx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, hdmi_rx_bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, + msm_hdmi_rx_ch); + ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2; + + return 0; +} + +static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2; + if (msm_hdmi_rx_ch > 8) { + pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n", + __func__, msm_hdmi_rx_ch); + msm_hdmi_rx_ch = 8; + } + pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch); + + return 1; +} + +static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (hdmi_rx_sample_rate) { + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__, + hdmi_rx_sample_rate); + + return 0; +} + +static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 2: + hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ; + } + + pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__, + hdmi_rx_sample_rate); + + return 0; +} + +static int msm8996_auxpcm_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm8996_auxpcm_rate; + return 0; +} + +static int msm8996_auxpcm_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + msm8996_auxpcm_rate = SAMPLING_RATE_16KHZ; + break; + default: + msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ; + break; + } + return 0; +} + +static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch); + ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1; + return 0; +} + +static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch); + return 1; +} + +static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = msm8996_auxpcm_rate; + channels->min = channels->max = 1; + + return 0; +} + +static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch); + + if (channels->max < 2) + channels->min = channels->max = 2; + channels->min = channels->max = msm_proxy_rx_ch; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + return 0; +} + +static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + rate->min = rate->max = SAMPLING_RATE_48KHZ; + return 0; +} + +static int msm8996_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s channels->min %u channels->max %u ()\n", __func__, + channels->min, channels->max); + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + hdmi_rx_bit_format); + if (channels->max < 2) + channels->min = channels->max = 2; + rate->min = rate->max = hdmi_rx_sample_rate; + channels->min = channels->max = msm_hdmi_rx_ch; + + return 0; +} + +static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: channel:%d\n", __func__, msm_tert_mi2s_tx_ch); + rate->min = rate->max = SAMPLING_RATE_48KHZ; + channels->min = channels->max = msm_tert_mi2s_tx_ch; + return 0; +} + +static int msm8996_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("%s: substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + mi2s_tx_clk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX, + &mi2s_tx_clk); + if (ret < 0) { + pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed, err:%d\n", __func__, ret); +err: + return ret; +} + +static void msm8996_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + + pr_debug("%s: substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + mi2s_tx_clk.enable = 0; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX, + &mi2s_tx_clk); + if (ret < 0) + pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret); +} + +static struct snd_soc_ops msm8996_mi2s_be_ops = { + .startup = msm8996_mi2s_snd_startup, + .shutdown = msm8996_mi2s_snd_shutdown, +}; + +static int msm_slim_5_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim5_rx_bit_format); + rate->min = rate->max = slim5_rx_sample_rate; + channels->min = channels->max = msm_slim_5_rx_ch; + + pr_debug("%s: format = %d, rate = %d, channels = %d\n", + __func__, params_format(params), params_rate(params), + msm_slim_5_rx_ch); + + return 0; +} + +static int msm_slim_6_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim6_rx_bit_format); + rate->min = rate->max = slim6_rx_sample_rate; + channels->min = channels->max = msm_slim_6_rx_ch; + + pr_debug("%s: format = %d, rate = %d, channels = %d\n", + __func__, params_format(params), params_rate(params), + msm_slim_6_rx_ch); + + return 0; +} + +static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim0_rx_bit_format); + rate->min = rate->max = slim0_rx_sample_rate; + channels->min = channels->max = msm_slim_0_rx_ch; + + pr_debug("%s: format = %d, rate = %d, channels = %d\n", + __func__, params_format(params), params_rate(params), + msm_slim_0_rx_ch); + + return 0; +} + +static int msm_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s()\n", __func__); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, slim0_tx_bit_format); + rate->min = rate->max = slim0_tx_sample_rate; + channels->min = channels->max = msm_slim_0_tx_ch; + + return 0; +} + +static int msm_slim_1_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s()\n", __func__); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, slim1_tx_bit_format); + rate->min = rate->max = slim1_tx_sample_rate; + channels->min = channels->max = msm_slim_1_tx_ch; + + return 0; +} + +static int msm_slim_4_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + pr_debug("%s: msm_vi_feed_tx_ch: %d\n", __func__, msm_vi_feed_tx_ch); + + return 0; +} + +static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + int rc = 0; + void *config = NULL; + struct snd_soc_codec *codec = rtd->codec; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: enter\n", __func__); + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, config, + SLIMBUS_5_TX); + if (rc) { + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + } + + return rc; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + pr_debug("%s:\n", __func__); + rate->min = rate->max = SAMPLING_RATE_48KHZ; + return 0; +} + +static const struct soc_enum msm_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(2, spk_function), + SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text), + SOC_ENUM_SINGLE_EXT(8, slim0_tx_ch_text), + SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_bit_format_text), + rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim0_rx_sample_rate_text), + slim0_rx_sample_rate_text), + SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text), + SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text), + SOC_ENUM_SINGLE_EXT(4, slim5_rx_sample_rate_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim5_rx_bit_format_text), + slim5_rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(2, slim5_rx_ch_text), + SOC_ENUM_SINGLE_EXT(2, hifi_function), + SOC_ENUM_SINGLE_EXT(2, vi_feed_ch_text), + SOC_ENUM_SINGLE_EXT(4, slim6_rx_sample_rate_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim6_rx_bit_format_text), + slim6_rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(2, slim6_rx_ch_text), +}; + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm8996_get_spk, + msm8996_set_spk), + SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1], + msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", msm_snd_enum[10], + msm_slim_5_rx_ch_get, msm_slim_5_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", msm_snd_enum[15], + msm_slim_6_rx_ch_get, msm_slim_6_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2], + msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", msm_snd_enum[2], + msm_slim_1_tx_ch_get, msm_slim_1_tx_ch_put), + SOC_ENUM_EXT("AUX PCM SampleRate", msm8996_auxpcm_enum[0], + msm8996_auxpcm_rate_get, + msm8996_auxpcm_rate_put), + SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[3], + msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", msm_snd_enum[4], + slim0_rx_bit_format_get, slim0_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", msm_snd_enum[9], + slim5_rx_bit_format_get, slim5_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", msm_snd_enum[14], + slim6_rx_bit_format_get, slim6_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", msm_snd_enum[5], + slim0_rx_sample_rate_get, slim0_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", msm_snd_enum[8], + slim5_rx_sample_rate_get, slim5_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", msm_snd_enum[13], + slim6_rx_sample_rate_get, slim6_rx_sample_rate_put), + SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[4], + hdmi_rx_bit_format_get, hdmi_rx_bit_format_put), + SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[6], + msm_proxy_rx_ch_get, msm_proxy_rx_ch_put), + SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[7], + hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", msm_snd_enum[5], + slim0_tx_sample_rate_get, slim0_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX Format", msm_snd_enum[4], + slim0_tx_bit_format_get, slim0_tx_bit_format_put), + SOC_ENUM_EXT("HiFi Function", msm_snd_enum[11], msm8996_hifi_get, + msm8996_hifi_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", msm_snd_enum[12], + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), +}; + +static bool msm8996_swap_gnd_mic(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = codec->component.card; + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int value = gpio_get_value_cansleep(pdata->us_euro_gpio); + + pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value); + gpio_set_value_cansleep(pdata->us_euro_gpio, !value); + return true; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int rc; + void *config_data = NULL; + + pr_debug("%s: enter\n", __func__); + + if (!msm8996_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (rc) { + pr_err("%s: Failed to set codec registers config %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (rc) + pr_err("%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (rc) { + pr_err("%s: Failed to set slimbus slave config %d\n", + __func__, rc); + return rc; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm8996_adsp_state_callback(struct notifier_block *nb, + unsigned long value, void *priv) +{ + if (value == SUBSYS_BEFORE_SHUTDOWN) { + pr_debug("%s: ADSP is about to shutdown. Clearing AFE config\n", + __func__); + msm_afe_clear_config(); + } else if (value == SUBSYS_AFTER_POWERUP) { + pr_debug("%s: ADSP is up\n", __func__); + } + + return NOTIFY_OK; +} + +static struct notifier_block adsp_state_notifier_block = { + .notifier_call = msm8996_adsp_state_callback, + .priority = -INT_MAX, +}; + +static int msm8996_wcd93xx_codec_up(struct snd_soc_codec *codec) +{ + int err; + unsigned long timeout; + int adsp_ready = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (!q6core_is_adsp_ready()) { + pr_err_ratelimited("%s: ADSP Audio isn't ready\n", + __func__); + /* + * ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } else { + pr_debug("%s: ADSP Audio is ready\n", __func__); + adsp_ready = 1; + break; + } + } while (time_after(timeout, jiffies)); + + if (!adsp_ready) { + pr_err("%s: timed out waiting for ADSP Audio\n", __func__); + return -ETIMEDOUT; + } + + err = msm_afe_set_config(codec); + if (err) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, err); + return err; +} + +static int msm8996_tasha_codec_event_cb(struct snd_soc_codec *codec, + enum wcd9335_codec_event codec_event) +{ + switch (codec_event) { + case WCD9335_CODEC_EVENT_CODEC_UP: + return msm8996_wcd93xx_codec_up(codec); + default: + pr_err("%s: UnSupported codec event %d\n", + __func__, codec_event); + return -EINVAL; + } +} + +static int msm8996_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high) +{ + struct snd_soc_card *card = codec->component.card; + struct msm8996_asoc_mach_data *pdata; + int val; + + if (!card) + return 0; + + pdata = snd_soc_card_get_drvdata(card); + if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio)) + return 0; + + val = gpio_get_value_cansleep(pdata->hph_en0_gpio); + if ((!!val) == high) + return 0; + + gpio_direction_output(pdata->hph_en0_gpio, (int)high); + + return 1; +} + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int err; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux; + void *mbhc_calibration; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + err = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (err < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, err); + return err; + } + + err = msm8996_liquid_init_docking(); + if (err) { + pr_err("%s: 8996 init Docking stat IRQ failed (%d)\n", + __func__, err); + return err; + } + + err = msm8996_ext_us_amp_init(); + if (err) { + pr_err("%s: 8996 US Emitter GPIO init failed (%d)\n", + __func__, err); + return err; + } + + snd_soc_dapm_new_controls(dapm, msm8996_dapm_widgets, + ARRAY_SIZE(msm8996_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd9335_audio_paths, + ARRAY_SIZE(wcd9335_audio_paths)); + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp"); + + snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp"); + snd_soc_dapm_ignore_suspend(dapm, "ultrasound amp"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC6"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC0"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + msm8996_codec_fn.get_afe_config_fn = tasha_get_afe_config; + msm8996_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit; + + err = msm_afe_set_config(codec); + if (err) { + pr_err("%s: Failed to set AFE config %d\n", __func__, err); + goto out; + } + + config_data = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + err = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (err) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, err); + goto out; + } + } + config_data = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + err = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (err) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, err); + goto out; + } + } + config_data = msm8996_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + err = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0); + if (err) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, err); + goto out; + } + } + /* Start mbhc */ + tasha_mbhc_zdet_gpio_ctrl(msm8996_config_hph_en0_gpio, rtd->codec); + mbhc_calibration = def_tasha_mbhc_cal(); + if (mbhc_calibration) { + wcd_mbhc_cfg.calibration = mbhc_calibration; + err = tasha_mbhc_hs_detect(codec, &wcd_mbhc_cfg); + if (err) { + pr_err("%s: mbhc hs detect failed, err:%d\n", + __func__, err); + goto out; + } + } else { + pr_err("%s: mbhc_cfg calibration is NULL\n", __func__); + err = -ENOMEM; + goto out; + } + adsp_state_notifier = subsys_notif_register_notifier("adsp", + &adsp_state_notifier_block); + if (!adsp_state_notifier) { + pr_err("%s: Failed to register adsp state notifier\n", + __func__); + err = -EFAULT; + msm8996_codec_fn.mbhc_hs_detect_exit(codec); + goto out; + } + + tasha_event_register(msm8996_tasha_codec_event_cb, rtd->codec); + + /* + * Send speaker configuration only for WSA8810. + * Defalut configuration is for WSA8815. + */ + if (rtd_aux && rtd_aux->component) + if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || + !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { + tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tasha_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + codec_reg_done = true; + + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + err = 0; + goto out; + } + pdata->codec_root = entry; + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + + return 0; +out: + return err; +} + +static void *def_tasha_mbhc_cal(void) +{ + void *tasha_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tasha_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y)) + S(v_hs_max, 1500); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tasha_wcd_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + msm_slim_5_rx_ch); + rx_ch_count = msm_slim_5_rx_ch; + } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + msm_slim_6_rx_ch); + rx_ch_count = msm_slim_6_rx_ch; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + msm_slim_0_rx_ch); + rx_ch_count = msm_slim_0_rx_ch; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto end; + } + /* For _tx1 case */ + if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = msm_slim_0_tx_ch; + /* For _tx3 case */ + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = msm_slim_1_tx_ch; + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_3_TX) + /* DAI 5 is used for external EC reference from codec. + * Since Rx is fed as reference for EC, the config of + * this DAI is based on that of the Rx path. + */ + user_set_tx_ch = msm_slim_0_rx_ch; + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n", + __func__, msm_slim_0_tx_ch, user_set_tx_ch, + tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 tx_ch[SLIM_MAX_TX_PORTS]; + u32 tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: Invalid stream type %d\n", + __func__, substream->stream); + ret = -EINVAL; + goto end; + } + + pr_debug("%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, NULL, NULL); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto end; + } + + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: tx_ch_cnt(%d) be_id %d\n", + __func__, tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); +end: + return ret; +} + +static struct snd_soc_ops msm8996_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm8996_cpe_ops = { + .hw_params = msm_snd_cpe_hw_params, +}; + +static int msm8996_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static struct snd_soc_ops msm8996_slimbus_2_be_ops = { + .hw_params = msm8996_slimbus_2_hw_params, +}; + +static int msm8996_get_ll_qos_val(struct snd_pcm_runtime *runtime) +{ + int usecs; + + /* take 10% of period time as the deadline */ + usecs = (100000 / runtime->rate) * runtime->period_size; + usecs += ((100000 % runtime->rate) * runtime->period_size) / + runtime->rate; + + return usecs; +} + +static int msm8996_mm5_prepare(struct snd_pcm_substream *substream) +{ + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + msm8996_get_ll_qos_val(substream->runtime)); + return 0; +} + +static struct snd_soc_ops msm8996_mm5_ops = { + .prepare = msm8996_mm5_prepare, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm8996_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = "MSM8996 Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = "MSM8996 Media2", + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = "MSM8996 ULL", + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary MI2S TX_Hostless", + .stream_name = "Tertiary MI2S_TX Hostless Capture", + .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + { + .name = "MSM8996 Compress1", + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "VoLTE", + .stream_name = "VoLTE", + .cpu_dai_name = "VoLTE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOLTE, + }, + { + .name = "MSM8996 LowLatency", + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm8996_mm5_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = "MSM8996 Compress2", + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = "MSM8996 Compress3", + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = "MSM8996 ULL NOIRQ", + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + { + .name = "QCHAT", + .stream_name = "QCHAT", + .cpu_dai_name = "QCHAT", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_QCHAT, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + { + .name = "INT_HFP_BT Hostless", + .stream_name = "INT_HFP_BT Hostless", + .cpu_dai_name = "INT_HFP_BT_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM8996 HFP TX", + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = "MSM8996 Media9", + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = "VoWLAN", + .stream_name = "VoWLAN", + .cpu_dai_name = "VoWLAN", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOWLAN, + }, + { + .name = "MSM8996 Compress4", + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = "MSM8996 Compress5", + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = "MSM8996 Compress6", + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = "MSM8996 Compress7", + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = "MSM8996 Compress8", + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = "MSM8996 Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "Circuit-Switch Voice", + .stream_name = "CS-Voice", + .cpu_dai_name = "CS-VOICE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_CS_VOICE, + }, + { + .name = "Voice2", + .stream_name = "Voice2", + .cpu_dai_name = "Voice2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICE2, + }, +}; + +static struct snd_soc_dai_link msm8996_tasha_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_slim_4_tx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm8996_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm8996_slimbus_2_be_ops, + }, + /* CPE LSM direct dai-link */ + { + .name = "CPE Listen service", + .stream_name = "CPE Listen Audio Service", + .cpu_dai_name = "msm-dai-slim", + .platform_name = "msm-cpe-lsm", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "tasha_mad1", + .codec_name = "tasha_codec", + .ops = &msm8996_cpe_ops, + }, + /* slimbus rx 6 hostless */ + { + .name = "SLIMBUS_6 Hostless Playback", + .stream_name = "SLIMBUS_6 Hostless", + .cpu_dai_name = "SLIMBUS6_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* CPE LSM EC PP direct dai-link */ + { + .name = "CPE Listen service ECPP", + .stream_name = "CPE Listen Audio Service ECPP", + .cpu_dai_name = "CPE_LSM_NOHOST", + .platform_name = "msm-cpe-lsm.3", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "tasha_cpe", + .codec_name = "tasha_codec", + }, +}; + +static struct snd_soc_dai_link msm8996_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_auxpcm_be_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_auxpcm_be_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_tx_be_hw_params_fixup, + .ops = &msm8996_mi2s_be_ops, + .ignore_suspend = 1, + } +}; + +static struct snd_soc_dai_link msm8996_tasha_be_dai_links[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm8996_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm8996_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_slim_1_tx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_slim_5_rx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_slim_5_tx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_slim_6_rx_be_hw_params_fixup, + .ops = &msm8996_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm8996_hdmi_dai_link[] = { + /* HDMI BACK END DAI Link */ + { + .name = LPASS_BE_HDMI, + .stream_name = "HDMI Playback", + .cpu_dai_name = "msm-dai-q6-hdmi.8", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-hdmi-audio-codec-rx", + .codec_dai_name = "msm_hdmi_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_HDMI_RX, + .be_hw_params_fixup = msm8996_hdmi_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm8996_tasha_dai_links[ + ARRAY_SIZE(msm8996_common_dai_links) + + ARRAY_SIZE(msm8996_tasha_fe_dai_links) + + ARRAY_SIZE(msm8996_common_be_dai_links) + + ARRAY_SIZE(msm8996_tasha_be_dai_links) + + ARRAY_SIZE(msm8996_hdmi_dai_link)]; + +static int msm8996_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm8996_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + return -EINVAL; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + + return 0; +} + +struct snd_soc_card snd_soc_card_tasha_msm8996 = { + .name = "msm8996-tasha-snd-card", +}; + +static int msm8996_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm8996_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret; + + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + return ret; + } + } + + return 0; +} + +static int msm8996_prepare_hifi(struct snd_soc_card *card) +{ + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret; + + if (gpio_is_valid(pdata->hph_en1_gpio)) { + dev_dbg(card->dev, "%s: hph_en1_gpio request %d\n", __func__, + pdata->hph_en1_gpio); + ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio"); + if (ret) { + dev_err(card->dev, + "%s: hph_en1_gpio request failed, ret:%d\n", + __func__, ret); + return ret; + } + } + if (gpio_is_valid(pdata->hph_en0_gpio)) { + dev_dbg(card->dev, "%s: hph_en0_gpio request %d\n", __func__, + pdata->hph_en0_gpio); + ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio"); + if (ret) { + dev_err(card->dev, + "%s: hph_en0_gpio request failed, ret:%d\n", + __func__, ret); + return ret; + } + } + return 0; +} + +static const struct of_device_id msm8996_asoc_machine_of_match[] = { + { .compatible = "qcom,msm8996-asoc-snd-tasha", + .data = "tasha_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3, len_4; + const struct of_device_id *match; + + match = of_match_node(msm8996_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "tasha_codec")) { + card = &snd_soc_card_tasha_msm8996; + len_1 = ARRAY_SIZE(msm8996_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm8996_tasha_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm8996_common_be_dai_links); + + memcpy(msm8996_tasha_dai_links, + msm8996_common_dai_links, + sizeof(msm8996_common_dai_links)); + memcpy(msm8996_tasha_dai_links + len_1, + msm8996_tasha_fe_dai_links, + sizeof(msm8996_tasha_fe_dai_links)); + memcpy(msm8996_tasha_dai_links + len_2, + msm8996_common_be_dai_links, + sizeof(msm8996_common_be_dai_links)); + memcpy(msm8996_tasha_dai_links + len_3, + msm8996_tasha_be_dai_links, + sizeof(msm8996_tasha_be_dai_links)); + + dailink = msm8996_tasha_dai_links; + len_4 = len_3 + ARRAY_SIZE(msm8996_tasha_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) { + dev_dbg(dev, "%s(): hdmi audio support present\n", + __func__); + memcpy(dailink + len_4, msm8996_hdmi_dai_link, + sizeof(msm8996_hdmi_dai_link)); + len_4 += ARRAY_SIZE(msm8996_hdmi_dai_link); + } else { + dev_dbg(dev, "%s(): No hdmi audio support\n", __func__); + } + + if (card) { + card->dai_link = dailink; + card->num_links = len_4; + } + + return card; +} + +static int msm8996_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + char *dev_name_str = NULL; + struct msm8996_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + int found = 0; + int i; + int ret; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_dbg(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + return 0; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + return 0; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + return 0; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + return -EINVAL; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + return -EINVAL; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm8996_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) + return -ENOMEM; + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + return -EINVAL; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm8996_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm8996_aux_dev) + return -ENOMEM; + + /* Alloc array of codec conf struct */ + msm8996_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm8996_codec_conf) + return -ENOMEM; + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) + return -ENOMEM; + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + return -EINVAL; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm8996_aux_dev[i].name = dev_name_str; + msm8996_aux_dev[i].codec_name = NULL; + msm8996_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm8996_aux_dev[i].init = msm8996_wsa881x_init; + msm8996_codec_conf[i].dev_name = NULL; + msm8996_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm8996_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm8996_codec_conf; + card->aux_dev = msm8996_aux_dev; + + return 0; +} + +static int msm8996_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm8996_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + char *mclk_freq_prop_name; + const struct of_device_id *match; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm8996_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + match = of_match_node(msm8996_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: no matched codec is found.\n", + __func__); + goto err; + } + + mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq"; + + ret = of_property_read_u32(pdev->dev.of_node, + mclk_freq_prop_name, &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "Looking up %s property in node %s failed, err%d\n", + mclk_freq_prop_name, + pdev->dev.of_node->full_name, ret); + goto err; + } + + if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) { + dev_err(&pdev->dev, "unsupported mclk freq %u\n", + pdata->mclk_freq); + ret = -EINVAL; + goto err; + } + + spdev = pdev; + + ret = msm8996_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = msm8996_init_wsa_dev(pdev, card); + if (ret) + goto err; + + pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (pdata->hph_en1_gpio < 0) { + dev_dbg(&pdev->dev, "%s: %s property not found %d\n", + __func__, "qcom,hph-en1-gpio", pdata->hph_en1_gpio); + } + + pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (pdata->hph_en0_gpio < 0) { + dev_dbg(&pdev->dev, "%s: %s property not found %d\n", + __func__, "qcom,hph-en0-gpio", pdata->hph_en0_gpio); + } + ret = msm8996_prepare_hifi(card); + if (ret) + dev_dbg(&pdev->dev, "msm8996_prepare_hifi failed (%d)\n", + ret); + + ret = snd_soc_register_card(card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + else + dev_dbg(&pdev->dev, "Unknown value, set to default"); + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (pdata->us_euro_gpio < 0) { + dev_info(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", + pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected %d", + "qcom,us-euro-gpios", pdata->us_euro_gpio); + wcd_mbhc_cfg.swap_gnd_mic = msm8996_swap_gnd_mic; + } + + ret = msm8996_prepare_us_euro(card); + if (ret) + dev_info(&pdev->dev, "msm8996_prepare_us_euro failed (%d)\n", + ret); + return 0; +err: + if (pdata->us_euro_gpio > 0) { + dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n", + __func__, pdata->us_euro_gpio); + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + if (pdata->hph_en1_gpio > 0) { + dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n", + __func__, pdata->hph_en1_gpio); + gpio_free(pdata->hph_en1_gpio); + pdata->hph_en1_gpio = 0; + } + if (pdata->hph_en0_gpio > 0) { + dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n", + __func__, pdata->hph_en0_gpio); + gpio_free(pdata->hph_en0_gpio); + pdata->hph_en0_gpio = 0; + } + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm8996_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm8996_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(ext_us_amp_gpio)) + gpio_free(ext_us_amp_gpio); + + gpio_free(pdata->us_euro_gpio); + gpio_free(pdata->hph_en1_gpio); + gpio_free(pdata->hph_en0_gpio); + + if (msm8996_liquid_dock_dev != NULL) { + switch_dev_unregister(&msm8996_liquid_dock_dev->audio_sdev); + + if (msm8996_liquid_dock_dev->dock_plug_irq) + free_irq(msm8996_liquid_dock_dev->dock_plug_irq, + msm8996_liquid_dock_dev); + + if (msm8996_liquid_dock_dev->dock_plug_gpio) + gpio_free(msm8996_liquid_dock_dev->dock_plug_gpio); + + kfree(msm8996_liquid_dock_dev); + msm8996_liquid_dock_dev = NULL; + } + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver msm8996_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = msm8996_asoc_machine_of_match, + }, + .probe = msm8996_asoc_machine_probe, + .remove = msm8996_asoc_machine_remove, +}; +module_platform_driver(msm8996_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, msm8996_asoc_machine_of_match); diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c new file mode 100644 index 000000000000..c96c5bca7037 --- /dev/null +++ b/sound/soc/msm/msm8998.c @@ -0,0 +1,6828 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "../codecs/wcd9335.h" +#include "../codecs/wcd934x/wcd934x.h" +#include "../codecs/wcd934x/wcd934x-mbhc.h" +#include "../codecs/wsa881x.h" + +#define DRV_NAME "msm8998-asoc-snd" + +#define __CHIPSET__ "MSM8998 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define DEV_NAME_STR_LEN 32 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +#define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 8 + +#define MSM_HIFI_ON 1 + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + PCM_I2S_SEL_PRIM = 0, + PCM_I2S_SEL_SEC, + PCM_I2S_SEL_TERT, + PCM_I2S_SEL_QUAT, + PCM_I2S_SEL_MAX, +}; + +struct mi2s_aux_pcm_common_conf { + struct mutex lock; + void *pcm_i2s_sel_vt_addr; +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +struct auxpcm_conf { + struct mutex lock; + u32 ref_cnt; +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + HDMI_RX_IDX = 0, + DP_RX_IDX, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +struct msm_asoc_mach_data { + u32 mclk_freq; + int us_euro_gpio; /* used by gpio driver API */ + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct snd_info_entry *codec_root; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); +}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [HDMI_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_44P1", "KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const hifi_text[] = {"Off", "On"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); + +static struct platform_device *spdev; +static int msm_hifi_control; + +static bool is_initial_boot; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +static void *def_tasha_mbhc_cal(void); +static void *def_tavil_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, +}; + +static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK"}, + {"MIC BIAS2", NULL, "MCLK"}, + {"MIC BIAS3", NULL, "MCLK"}, + {"MIC BIAS4", NULL, "MCLK"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX]; +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; +static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) + port_id = SLIM_RX_0; + else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX"))) + port_id = SLIM_RX_2; + else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX"))) + port_id = SLIM_RX_5; + else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX"))) + port_id = SLIM_RX_6; + else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX"))) + port_id = SLIM_TX_0; + else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX"))) + port_id = SLIM_TX_1; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "HDMI_RX", sizeof("HDMI_RX"))) + idx = HDMI_RX_IDX; + else if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) + idx = DP_RX_IDX; + else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_hifi_ctrl(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, + msm_hifi_control); + + if (!pdata || !pdata->hph_en1_gpio_p) { + pr_err("%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM_HIFI_ON) { + msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + + return 0; +} + +static int msm_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm_hifi_ctrl(codec); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("HDMI_RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("HDMI_RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("HDMI_RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, + msm_hifi_put), +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + else if (!strcmp(dev_name(codec->dev), "tavil_codec")) + ret = tavil_cdc_mclk_enable(codec, enable); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control); + + if (!pdata || !pdata->hph_en0_gpio_p) { + pr_err("%s: hph_en0_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (msm_hifi_control != MSM_HIFI_ON) { + pr_debug("%s: HiFi mixer control is not set\n", + __func__); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + case MSM_BACKEND_DAI_HDMI_RX: + idx = HDMI_RX_IDX; + break; + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp be_id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = NULL; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->be_id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->be_id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + codec = rtd->codec; + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_HDMI_RX: + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->be_id); + if (IS_ERR_VALUE(idx)) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int value = 0; + + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + } else if (pdata->us_euro_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->us_euro_gpio); + gpio_set_value_cansleep(pdata->us_euro_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value); + return true; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(codec->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (q6core_is_adsp_ready()) { + pr_debug("%s: ADSP Audio is ready\n", __func__); + adsp_ready = 1; + break; + } + /* + * ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!adsp_ready) { + pr_err("%s: timed out waiting for ADSP Audio\n", __func__); + ret = -ETIMEDOUT; + goto err_fail; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err_fail: + return ret; +} + +static int msm8998_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto done; + } + codec = rtd->codec; + + ret = msm_adsp_power_up_config(codec); + if (ret < 0) { + dev_err(card->dev, + "%s: msm_adsp_power_up_config failed ret = %d!\n", + __func__, ret); + goto done; + } + break; + default: + break; + } +done: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = msm8998_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) + snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha, + ARRAY_SIZE(wcd_audio_paths_tasha)); + else + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + } + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + } else { + msm_codec_fn.get_afe_config_fn = tasha_get_afe_config; + msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit; + } + + ret = msm_adsp_power_up_config(codec); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err_afe_cfg; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (ret) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0); + if (ret) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + } + + /* + * Send speaker configuration only for WSA8810. + * Defalut configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component) + if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || + !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + } else { + if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component) + if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || + !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { + tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tasha_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err_snd_module; + } + pdata->codec_root = entry; + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + } +done: + codec_reg_done = true; + return 0; + +err_snd_module: +err_afe_cfg: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static void *def_tasha_mbhc_cal(void) +{ + void *tasha_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tasha_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tasha_wcd_cal; +} + +static void *def_tavil_mbhc_cal(void) +{ + void *tavil_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tavil_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tavil_wcd_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err_ch_map; + } + /* For _tx1 case */ + if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + } + +err_ch_map: + return ret; +} + +static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 tx_ch[SLIM_MAX_TX_PORTS]; + u32 tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: Invalid stream type %d\n", + __func__, substream->stream); + ret = -EINVAL; + goto err_stream_type; + } + + pr_debug("%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, NULL, NULL); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err_ch_map; + } + + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: tx_ch_cnt(%d) be_id %d\n", + __func__, tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); +err_ch_map: +err_stream_type: + return ret; +} + +static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } + +err_ch_map: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto exit; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n", + __func__, tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +exit: + return ret; +} + +static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id - 1; + return ret = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (++auxpcm_intf_conf[index].ref_cnt == 1) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(1, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + } + } + if (IS_ERR_VALUE(ret)) + auxpcm_intf_conf[index].ref_cnt--; + + mutex_unlock(&auxpcm_intf_conf[index].lock); + +done: + return ret; +} + +static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id - 1; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, + substream->name, substream->stream, + rtd->cpu_dai->name, rtd->cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, rtd->cpu_dai->id); + return; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (--auxpcm_intf_conf[index].ref_cnt == 0) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + auxpcm_intf_conf[index].ref_cnt++; + } + } + mutex_unlock(&auxpcm_intf_conf[index].lock); +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid be_id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } + + if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master) + mi2s_clk[dai_id].clk_freq_in_hz = 0; +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->be_id); + if (IS_ERR_VALUE(port_id)) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto done; + } + +done: + return ret; +} + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index > QUAT_MI2S) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + /* + * Muxtex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + ret = msm_mi2s_set_sclk(substream, true); + if (IS_ERR_VALUE(ret)) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_muxsel_virt_addr is NULL for dai %d\n", + __func__, index); + ret = -EINVAL; + goto clk_off; + } + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) + fmt = SND_SOC_DAIFMT_CBM_CFM; + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (IS_ERR_VALUE(ret)) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + } +clk_off: + if (IS_ERR_VALUE(ret)) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (IS_ERR_VALUE(ret)) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +done: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index > QUAT_MI2S) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) { + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + mi2s_intf_conf[index].ref_cnt++; + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_cpe_ops = { + .hw_params = msm_snd_cpe_hw_params, +}; + +static struct snd_soc_ops msm_slimbus_2_be_ops = { + .hw_params = msm_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(Compress3), + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(Compress9), + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* CPE LSM direct dai-link */ + { + .name = "CPE Listen service", + .stream_name = "CPE Listen Audio Service", + .cpu_dai_name = "msm-dai-slim", + .platform_name = "msm-cpe-lsm", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "tasha_mad1", + .codec_name = "tasha_codec", + .ops = &msm_cpe_ops, + }, + { + .name = "SLIMBUS_6 Hostless Playback", + .stream_name = "SLIMBUS_6 Hostless", + .cpu_dai_name = "SLIMBUS6_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* CPE LSM EC PP direct dai-link */ + { + .name = "CPE Listen service ECPP", + .stream_name = "CPE Listen Audio Service ECPP", + .cpu_dai_name = "CPE_LSM_NOHOST", + .platform_name = "msm-cpe-lsm.3", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "tasha_cpe", + .codec_name = "tasha_codec", + }, +}; + +static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* HDMI BACK END DAI Link */ + { + .name = LPASS_BE_HDMI, + .stream_name = "HDMI Playback", + .cpu_dai_name = "msm-dai-q6-hdmi.8", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_hdmi_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_HDMI_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_tasha_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tasha_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tasha_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static struct snd_soc_dai_link msm_tavil_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tavil_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static int msm_snd_card_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_tasha_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tasha_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_tavil_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +struct snd_soc_card snd_soc_card_tasha_msm = { + .name = "msm8998-tasha-snd-card", + .late_probe = msm_snd_card_late_probe, +}; + +struct snd_soc_card snd_soc_card_tavil_msm = { + .name = "msm8998-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + } + } + + return ret; +} + +static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + dev_err(codec->dev, "%s: add_codec_controls failed, err%d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + return 0; +} + +static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int ret = 0; + unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + slim_rx_cfg[0].channels, + rx_ch); + if (ret < 0) + pr_err("%s: RX failed to set cpu chan map error %d\n", + __func__, ret); + } else { + ret = snd_soc_dai_set_channel_map(cpu_dai, + slim_tx_cfg[0].channels, + tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: TX failed to set cpu chan map error %d\n", + __func__, ret); + } + + return ret; +} + +static struct snd_soc_ops msm_stub_be_ops = { + .hw_params = msm_snd_stub_hw_params, +}; + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_stub_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "msm8998-stub-snd-card", +}; + +static const struct of_device_id msm8998_asoc_machine_of_match[] = { + { .compatible = "qcom,msm8998-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,msm8998-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,msm8998-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3; + int total_links; + const struct of_device_id *match; + + match = of_match_node(msm8998_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "tasha_codec")) { + card = &snd_soc_card_tasha_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tasha_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_3 + ARRAY_SIZE(msm_tasha_be_dai_links); + memcpy(msm_tasha_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tasha_dai_links + len_1, + msm_tasha_fe_dai_links, + sizeof(msm_tasha_fe_dai_links)); + memcpy(msm_tasha_dai_links + len_2, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tasha_dai_links + len_3, + msm_tasha_be_dai_links, + sizeof(msm_tasha_be_dai_links)); + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tasha_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): External display audio support present\n", + __func__); + memcpy(msm_tasha_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_tasha_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_tasha_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_tasha_dai_links; + } else if (!strcmp(match->data, "tavil_codec")) { + card = &snd_soc_card_tavil_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_3 + ARRAY_SIZE(msm_tavil_be_dai_links); + memcpy(msm_tavil_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tavil_dai_links + len_1, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + memcpy(msm_tavil_dai_links + len_2, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_dai_links + len_3, + msm_tavil_be_dai_links, + sizeof(msm_tavil_be_dai_links)); + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tavil_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_tavil_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_tavil_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_tavil_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_tavil_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + len_1 = ARRAY_SIZE(msm_stub_fe_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links); + + memcpy(msm_stub_dai_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + memcpy(msm_stub_dai_links + len_1, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + + dailink = msm_stub_dai_links; + total_links = len_2; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + int ret = 0; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + ret = -EINVAL; + goto err_codec; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + +err_codec: + return ret; +} + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_dbg(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + goto err_dt; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + goto err_dt; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err_dt; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err_dt; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err_dt; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err_mem; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_dev_node; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_auxdev_mem; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_codec_conf; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_dev_str; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_dt_prop; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_dt_prop: + devm_kfree(&pdev->dev, dev_name_str); +err_dev_str: + devm_kfree(&pdev->dev, msm_codec_conf); +err_codec_conf: + devm_kfree(&pdev->dev, msm_aux_dev); +err_auxdev_mem: +err_dev_node: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err_mem: +err_dt: + return ret; +} + +static void i2s_auxpcm_init(struct platform_device *pdev) +{ + struct resource *muxsel; + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + char *str[PCM_I2S_SEL_MAX] = { + "lpaif_pri_mode_muxsel", + "lpaif_sec_mode_muxsel", + "lpaif_tert_mode_muxsel", + "lpaif_quat_mode_muxsel" + }; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < AUX_PCM_MAX; count++) { + mutex_init(&auxpcm_intf_conf[count].lock); + auxpcm_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + mutex_init(&mi2s_auxpcm_conf[count].lock); + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + str[count]); + if (muxsel) { + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr + = ioremap(muxsel->start, resource_size(muxsel)); + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) + if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr != + NULL) + iounmap( + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr); +} + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + char *mclk_freq_prop_name; + const struct of_device_id *match; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + match = of_match_node(msm8998_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: no matched codec is found.\n", + __func__); + goto err; + } + + if (!strcmp(match->data, "tasha_codec")) + mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq"; + else + mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq"; + + ret = of_property_read_u32(pdev->dev.of_node, + mclk_freq_prop_name, &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "Looking up %s property in node %s failed, err%d\n", + mclk_freq_prop_name, + pdev->dev.of_node->full_name, ret); + goto err; + } + + if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) { + dev_err(&pdev->dev, "unsupported mclk freq %u\n", + pdata->mclk_freq); + ret = -EINVAL; + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + spdev = pdev; + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); + } else { + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!pdata->hph_en1_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", + pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!pdata->hph_en0_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", + pdev->dev.of_node->full_name); + } + } + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + else + dev_dbg(&pdev->dev, "Unknown value, set to default"); + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio)) + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected", + "qcom,us-euro-gpios"); + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + ret = msm_prepare_us_euro(card); + if (ret) + dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n", + ret); + + i2s_auxpcm_init(pdev); + + is_initial_boot = true; + ret = audio_notifier_register("msm8998", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return 0; +err: + if (pdata->us_euro_gpio > 0) { + dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n", + __func__, pdata->us_euro_gpio); + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + gpio_free(pdata->us_euro_gpio); + i2s_auxpcm_deinit(); + + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver msm8998_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = msm8998_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(msm8998_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, msm8998_asoc_machine_of_match); diff --git a/sound/soc/msm/msmfalcon-common.c b/sound/soc/msm/msmfalcon-common.c new file mode 100644 index 000000000000..59289821e05f --- /dev/null +++ b/sound/soc/msm/msmfalcon-common.c @@ -0,0 +1,2806 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "msm-audio-pinctrl.h" +#include "msmfalcon-common.h" +#include "msmfalcon-internal.h" +#include "msmfalcon-external.h" +#include "../codecs/msm8x16/msm8x16-wcd.h" +#include "../codecs/wsa881x.h" + +#define DRV_NAME "msmfalcon-asoc-snd" + +#define DEV_NAME_STR_LEN 32 +#define DEFAULT_MCLK_RATE 9600000 + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + PCM_I2S_SEL_PRIM = 0, + PCM_I2S_SEL_SEC, + PCM_I2S_SEL_TERT, + PCM_I2S_SEL_QUAT, + PCM_I2S_SEL_MAX, +}; + +struct mi2s_aux_pcm_common_conf { + struct mutex lock; + void *pcm_i2s_sel_vt_addr; +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +struct auxpcm_conf { + struct mutex lock; + u32 ref_cnt; +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec); + +static struct wcd_mbhc_config mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = false, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = false, + .mbhc_micbias = 0, + .anc_micbias = 0, + .enable_anc_mic_detect = false, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_44P1", "KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192", "KHZ_384"}; + +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX]; +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; +static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX]; + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +const struct snd_kcontrol_new msm_common_snd_controls[] = { + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +/** + * msm_common_be_hw_params_fixup - updates settings of ALSA BE hw params. + * + * @rtd: runtime dailink instance + * @params: HW params of associated backend dailink. + * + * Returns 0. + */ +int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->be_id) { + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return rc; +} +EXPORT_SYMBOL(msm_common_be_hw_params_fixup); + +/** + * msm_aux_pcm_snd_startup - startup ops of auxpcm. + * + * @substream: PCM stream pointer of associated backend dailink + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id - 1; + return ret = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (++auxpcm_intf_conf[index].ref_cnt == 1) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(1, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + } + } + if (IS_ERR_VALUE(ret)) + auxpcm_intf_conf[index].ref_cnt--; + + mutex_unlock(&auxpcm_intf_conf[index].lock); + +done: + return ret; +} +EXPORT_SYMBOL(msm_aux_pcm_snd_startup); + +/** + * msm_aux_pcm_snd_shutdown - shutdown ops of auxpcm. + * + * @substream: PCM stream pointer of associated backend dailink + */ +void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id - 1; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, + substream->name, substream->stream, + rtd->cpu_dai->name, rtd->cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, rtd->cpu_dai->id); + return; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (--auxpcm_intf_conf[index].ref_cnt == 0) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + auxpcm_intf_conf[index].ref_cnt++; + } + } + mutex_unlock(&auxpcm_intf_conf[index].lock); +} +EXPORT_SYMBOL(msm_aux_pcm_snd_shutdown); + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid be_id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } + + if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master) + mi2s_clk[dai_id].clk_freq_in_hz = 0; +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->be_id); + if (IS_ERR_VALUE(port_id)) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto done; + } + +done: + return ret; +} + +/** + * msm_mi2s_snd_startup - startup ops of mi2s. + * + * @substream: PCM stream pointer of associated backend dailink + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index > QUAT_MI2S) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + /* + * Muxtex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + ret = msm_mi2s_set_sclk(substream, true); + if (IS_ERR_VALUE(ret)) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_muxsel_virt_addr is NULL for dai %d\n", + __func__, index); + ret = -EINVAL; + goto clk_off; + } + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) + fmt = SND_SOC_DAIFMT_CBM_CFM; + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (IS_ERR_VALUE(ret)) { + dev_err(rtd->card->dev, + "%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + } +clk_off: + if (IS_ERR_VALUE(ret)) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (IS_ERR_VALUE(ret)) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +done: + return ret; +} +EXPORT_SYMBOL(msm_mi2s_snd_startup); + +/** + * msm_mi2s_snd_shutdown - shutdown ops of mi2s. + * + * @substream: PCM stream pointer of associated backend dailink + */ +void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index > QUAT_MI2S) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) { + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + mi2s_intf_conf[index].ref_cnt++; + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} +EXPORT_SYMBOL(msm_mi2s_snd_shutdown); + +/* Validate whether US EU switch is present or not */ +static int msm_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + } + } + + return ret; +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int value = 0; + + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + } else if (pdata->us_euro_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->us_euro_gpio); + gpio_set_value_cansleep(pdata->us_euro_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value); + return true; +} + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *phandle; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto cpu_dai; + } + phandle = of_parse_phandle(cdev->of_node, + "asoc-platform", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = phandle; + dai_link[i].platform_name = NULL; + } +cpu_dai: + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index < 0) + goto codec_dai; + phandle = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = phandle; + dai_link[i].cpu_dai_name = NULL; + } +codec_dai: + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + phandle = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for codec dai %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = phandle; + dai_link[i].codec_name = NULL; + } + } +err: + return ret; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + return -EINVAL; + } + + + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + return 0; +} + + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + char *dev_name_str = NULL; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + int found = 0; + int i; + int ret; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_dbg(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + goto err_dt; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + goto err_dt; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err_dt; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err_dt; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err_dt; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err_mem; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_dev_node; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_auxdev_mem; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_codec_conf; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_dev_str; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_dt_prop; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_dt_prop: + devm_kfree(&pdev->dev, dev_name_str); +err_dev_str: + devm_kfree(&pdev->dev, msm_codec_conf); +err_codec_conf: + devm_kfree(&pdev->dev, msm_aux_dev); +err_auxdev_mem: +err_dev_node: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err_mem: +err_dt: + return ret; +} + +static void msm_free_auxdev_mem(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + int i; + + if (card->num_aux_devs > 0) { + for (i = 0; i < card->num_aux_devs; i++) { + kfree(msm_aux_dev[i].codec_name); + kfree(msm_codec_conf[i].dev_name); + kfree(msm_codec_conf[i].name_prefix); + } + } +} + +static void i2s_auxpcm_init(struct platform_device *pdev) +{ + struct resource *muxsel; + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + char *str[PCM_I2S_SEL_MAX] = { + "lpaif_pri_mode_muxsel", + "lpaif_sec_mode_muxsel", + "lpaif_tert_mode_muxsel", + "lpaif_quat_mode_muxsel" + }; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < AUX_PCM_MAX; count++) { + mutex_init(&auxpcm_intf_conf[count].lock); + auxpcm_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + mutex_init(&mi2s_auxpcm_conf[count].lock); + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + str[count]); + if (muxsel) { + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr + = ioremap(muxsel->start, resource_size(muxsel)); + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) + if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr != + NULL) + iounmap( + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr); +} + +static const struct of_device_id msmfalcon_asoc_machine_of_match[] = { + { .compatible = "qcom,msmfalcon-asoc-snd", + .data = "internal_codec"}, + { .compatible = "qcom,msmfalcon-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,msmfalcon-asoc-snd-tavil", + .data = "tavil_codec"}, + {}, +}; + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = NULL; + struct msm_asoc_mach_data *pdata = NULL; + const char *mclk = "qcom,msm-mclk-freq"; + int ret = -EINVAL, id; + const struct of_device_id *match; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + match = of_match_node(msmfalcon_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) + goto err; + + ret = of_property_read_u32(pdev->dev.of_node, mclk, &id); + if (ret) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, mclk); + id = DEFAULT_MCLK_RATE; + } + pdata->mclk_freq = id; + + if (!strcmp(match->data, "tasha_codec") || + !strcmp(match->data, "tavil_codec")) { + if (!strcmp(match->data, "tasha_codec")) + pdata->snd_card_val = EXT_SND_CARD_TASHA; + else + pdata->snd_card_val = EXT_SND_CARD_TAVIL; + ret = msm_ext_cdc_init(pdev, pdata, &card, &mbhc_cfg); + if (ret) + goto err; + } else if (!strcmp(match->data, "internal_codec")) { + pdata->snd_card_val = INT_SND_CARD; + ret = msm_int_cdc_init(pdev, pdata, &card, &mbhc_cfg); + if (ret) + goto err; + } else { + dev_err(&pdev->dev, + "%s: Not a matching DT sound node\n", __func__); + goto err; + } + if (!card) + goto err; + + if (pdata->snd_card_val == INT_SND_CARD) { + /*reading the gpio configurations from dtsi file*/ + ret = msm_gpioset_initialize(CLIENT_WCD, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, + "%s: error reading dtsi files%d\n", + __func__, ret); + goto err; + } + } + + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio)) + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected", + "qcom,us-euro-gpios"); + mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + ret = msm_prepare_us_euro(card); + if (ret) + dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n", + ret); + + i2s_auxpcm_init(pdev); + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) + goto err; + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + if (pdata->snd_card_val != INT_SND_CARD) + msm_ext_register_audio_notifier(); + return 0; +err: + if (pdata->us_euro_gpio > 0) { + dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n", + __func__, pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + if (pdata->hph_en1_gpio > 0) { + dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n", + __func__, pdata->hph_en1_gpio); + gpio_free(pdata->hph_en1_gpio); + pdata->hph_en1_gpio = 0; + } + if (pdata->hph_en0_gpio > 0) { + dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n", + __func__, pdata->hph_en0_gpio); + gpio_free(pdata->hph_en0_gpio); + pdata->hph_en0_gpio = 0; + } + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->snd_card_val == INT_SND_CARD) + mutex_destroy(&pdata->cdc_int_mclk0_mutex); + msm_free_auxdev_mem(pdev); + + gpio_free(pdata->us_euro_gpio); + gpio_free(pdata->hph_en1_gpio); + gpio_free(pdata->hph_en0_gpio); + i2s_auxpcm_deinit(); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver msmfalcon_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = msmfalcon_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(msmfalcon_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, msmfalcon_asoc_machine_of_match); diff --git a/sound/soc/msm/msmfalcon-common.h b/sound/soc/msm/msmfalcon-common.h new file mode 100644 index 000000000000..5f6b8592acec --- /dev/null +++ b/sound/soc/msm/msmfalcon-common.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_COMMON +#define __MSM_COMMON + +#include +#include +#include "../codecs/wcd-mbhc-v2.h" + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 8 + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +extern const struct snd_kcontrol_new msm_common_snd_controls[]; +struct msmfalcon_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); +}; + +enum { + INT_SND_CARD, + EXT_SND_CARD_TASHA, + EXT_SND_CARD_TAVIL, +}; + +struct msm_asoc_mach_data { + int us_euro_gpio; /* used by gpio driver API */ + int hph_en1_gpio; + int hph_en0_gpio; + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct snd_soc_codec *codec; + struct msmfalcon_codec msmfalcon_codec_fn; + struct snd_info_entry *codec_root; + int spk_ext_pa_gpio; + int mclk_freq; + int lb_mode; + int snd_card_val; + u8 micbias1_cap_mode; + u8 micbias2_cap_mode; + atomic_t int_mclk0_rsc_ref; + atomic_t int_mclk0_enabled; + struct mutex cdc_int_mclk0_mutex; + struct delayed_work disable_int_mclk0_work; + struct afe_clk_set digital_cdc_core_clk; +}; + +int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream); +void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream); +int msm_mi2s_snd_startup(struct snd_pcm_substream *substream); +void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream); +#endif diff --git a/sound/soc/msm/msmfalcon-ext-dai-links.c b/sound/soc/msm/msmfalcon-ext-dai-links.c new file mode 100644 index 000000000000..6f066c5945a9 --- /dev/null +++ b/sound/soc/msm/msmfalcon-ext-dai-links.c @@ -0,0 +1,1967 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "../codecs/wcd9335.h" +#include "msmfalcon-common.h" +#include "msmfalcon-external.h" + +#define DEV_NAME_STR_LEN 32 +#define __CHIPSET__ "MSMFALCON " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +static struct snd_soc_card snd_soc_card_msm_card_tavil; +static struct snd_soc_card snd_soc_card_msm_card_tasha; + +static struct snd_soc_ops msm_ext_slimbus_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_ext_cpe_ops = { + .hw_params = msm_snd_cpe_hw_params, +}; + +static struct snd_soc_ops msm_ext_slimbus_2_be_ops = { + .hw_params = msm_ext_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto exit; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n", + __func__, tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +exit: + return ret; +} + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + +static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { + /* tasha_vifeedback for speaker protection */ + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx2", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, + /* CPE LSM direct dai-link */ + { + .name = "CPE Listen service", + .stream_name = "CPE Listen Audio Service", + .cpu_dai_name = "msm-dai-slim", + .platform_name = "msm-cpe-lsm", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "tasha_mad1", + .codec_name = "tasha_codec", + .ops = &msm_ext_cpe_ops, + }, + { + .name = "SLIMBUS_6 Hostless Playback", + .stream_name = "SLIMBUS_6 Hostless", + .cpu_dai_name = "SLIMBUS6_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* CPE LSM EC PP direct dai-link */ + { + .name = "CPE Listen service ECPP", + .stream_name = "CPE Listen Audio Service ECPP", + .cpu_dai_name = "CPE_LSM_NOHOST", + .platform_name = "msm-cpe-lsm.3", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "tasha_cpe", + .codec_name = "tasha_codec", + }, +}; + +static struct snd_soc_dai_link msm_ext_tavil_fe_dai[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "ULL", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* This dai link has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + {/* hw:x,8 */ + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,9*/ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,10 */ + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,13 */ + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + /* LSM FE */ + {/* hw:x,14 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + {/* hw:x,15 */ + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + {/* hw:x,16 */ + .name = MSM_DAILINK_NAME(Compress3), + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,17 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + {/* hw:x,18 */ + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,19 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + {/* hw:x,24 */ + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM6 + }, + {/* hw:x,25 */ + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM7, + }, + {/* hw:x,26 */ + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM8, + }, + {/* hw:x,27 */ + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + {/* hw:x,28 */ + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = MSM_DAILINK_NAME(Compress9), + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_ext_common_be_dai[] = { + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_ext_tasha_dai_links[ +ARRAY_SIZE(msm_ext_common_fe_dai) + +ARRAY_SIZE(msm_ext_tasha_fe_dai) + +ARRAY_SIZE(msm_ext_common_be_dai) + +ARRAY_SIZE(msm_ext_tasha_be_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links) + +ARRAY_SIZE(msm_wcn_be_dai_links)]; + +static struct snd_soc_dai_link msm_ext_tavil_dai_links[ +ARRAY_SIZE(msm_ext_common_fe_dai) + +ARRAY_SIZE(msm_ext_tavil_fe_dai) + +ARRAY_SIZE(msm_ext_common_be_dai) + +ARRAY_SIZE(msm_ext_tavil_be_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links) + +ARRAY_SIZE(msm_wcn_be_dai_links)]; + +/** + * populate_snd_card_dailinks - prepares dailink array and initializes card. + * + * @dev: device handle + * + * Returns card on success or NULL on failure. + */ +struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, + int snd_card_val) +{ + struct snd_soc_card *card; + struct snd_soc_dai_link *msm_ext_dai_links = NULL; + int ret, len1, len2, len3, len4; + enum codec_variant codec_ver = 0; + + if (snd_card_val == EXT_SND_CARD_TASHA) { + card = &snd_soc_card_msm_card_tasha; + } else if (snd_card_val == EXT_SND_CARD_TAVIL) { + card = &snd_soc_card_msm_card_tavil; + } else { + dev_err(dev, "%s: failing as no matching card name\n", + __func__); + return NULL; + } + + card->dev = dev; + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(dev, "%s: parse card name failed, err:%d\n", + __func__, ret); + return NULL; + } + + if (strnstr(card->name, "tasha", strlen(card->name))) { + codec_ver = tasha_codec_ver(); + if (codec_ver == WCD9326) + card->name = "msmfalcon-tashalite-snd-card"; + + len1 = ARRAY_SIZE(msm_ext_common_fe_dai); + len2 = len1 + ARRAY_SIZE(msm_ext_tasha_fe_dai); + len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai); + memcpy(msm_ext_tasha_dai_links, msm_ext_common_fe_dai, + sizeof(msm_ext_common_fe_dai)); + memcpy(msm_ext_tasha_dai_links + len1, + msm_ext_tasha_fe_dai, sizeof(msm_ext_tasha_fe_dai)); + memcpy(msm_ext_tasha_dai_links + len2, + msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + memcpy(msm_ext_tasha_dai_links + len3, + msm_ext_tasha_be_dai, sizeof(msm_ext_tasha_be_dai)); + len4 = len3 + ARRAY_SIZE(msm_ext_tasha_be_dai); + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_ext_tasha_dai_links + len4, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + len4 += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_ext_tasha_dai_links + len4, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_ext_tasha_dai_links + len4, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + len4 += ARRAY_SIZE(msm_wcn_be_dai_links); + } + msm_ext_dai_links = msm_ext_tasha_dai_links; + } else if (strnstr(card->name, "tavil", strlen(card->name))) { + len1 = ARRAY_SIZE(msm_ext_common_fe_dai); + len2 = len1 + ARRAY_SIZE(msm_ext_tavil_fe_dai); + len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai); + memcpy(msm_ext_tavil_dai_links, msm_ext_common_fe_dai, + sizeof(msm_ext_common_fe_dai)); + memcpy(msm_ext_tavil_dai_links + len1, + msm_ext_tavil_fe_dai, sizeof(msm_ext_tavil_fe_dai)); + memcpy(msm_ext_tavil_dai_links + len2, + msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + memcpy(msm_ext_tavil_dai_links + len3, + msm_ext_tavil_be_dai, sizeof(msm_ext_tavil_be_dai)); + len4 = len3 + ARRAY_SIZE(msm_ext_tavil_be_dai); + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_ext_tavil_dai_links + len4, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + len4 += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_ext_tavil_dai_links + len4, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_ext_tavil_dai_links + len4, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + len4 += ARRAY_SIZE(msm_wcn_be_dai_links); + } + msm_ext_dai_links = msm_ext_tavil_dai_links; + } else { + dev_err(dev, "%s: failing as no matching card name\n", + __func__); + return NULL; + } + card->dai_link = msm_ext_dai_links; + card->num_links = len4; + + return card; +} +EXPORT_SYMBOL(populate_snd_card_dailinks); diff --git a/sound/soc/msm/msmfalcon-external.c b/sound/soc/msm/msmfalcon-external.c new file mode 100644 index 000000000000..d7b002e43f9c --- /dev/null +++ b/sound/soc/msm/msmfalcon-external.c @@ -0,0 +1,1765 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "msm-audio-pinctrl.h" +#include "msmfalcon-common.h" +#include "msmfalcon-external.h" +#include "../codecs/wcd9335.h" +#include "../codecs/wcd934x/wcd934x.h" +#include "../codecs/wcd934x/wcd934x-mbhc.h" + +#define MSMFALCON_SPK_ON 1 +#define MSMFALCON_SPK_OFF 0 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +static int msm_ext_spk_control = 1; +static struct wcd_mbhc_config *wcd_mbhc_cfg_ptr; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); +}; + +static struct msm_asoc_wcd93xx_codec msm_codec_fn; +static struct platform_device *spdev; + +static bool is_initial_boot; + +static void *def_ext_mbhc_cal(void); + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const spk_function_text[] = {"Off", "On"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"}; + +static SOC_ENUM_SINGLE_EXT_DECL(spk_func_en, spk_function_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) + port_id = SLIM_RX_0; + else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX"))) + port_id = SLIM_RX_2; + else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX"))) + port_id = SLIM_RX_5; + else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX"))) + port_id = SLIM_RX_6; + else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX"))) + port_id = SLIM_TX_0; + else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX"))) + port_id = SLIM_TX_1; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static void *def_ext_mbhc_cal(void) +{ + void *tavil_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tavil_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tavil_wcd_cal; +} + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + + +static void msm_ext_control(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + + pr_debug("%s: msm_ext_spk_control = %d", __func__, msm_ext_spk_control); + if (msm_ext_spk_control == MSMFALCON_SPK_ON) { + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp"); + } else { + snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp"); + } + snd_soc_dapm_sync(dapm); +} + +static int msm_ext_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_ext_spk_control = %d\n", + __func__, msm_ext_spk_control); + ucontrol->value.integer.value[0] = msm_ext_spk_control; + return 0; +} + +static int msm_ext_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + pr_debug("%s()\n", __func__); + if (msm_ext_spk_control == ucontrol->value.integer.value[0]) + return 0; + + msm_ext_spk_control = ucontrol->value.integer.value[0]; + msm_ext_control(codec); + return 1; +} + + +int msm_ext_enable_codec_mclk(struct snd_soc_codec *codec, int enable, + bool dapm) +{ + int ret; + + pr_debug("%s: enable = %d\n", __func__, enable); + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + else if (!strcmp(dev_name(codec->dev), "tavil_codec")) + ret = tavil_cdc_mclk_enable(codec, enable); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("Speaker Function", spk_func_en, msm_ext_get_spk, + msm_ext_set_spk), + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), +}; + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +/** + * msm_ext_be_hw_params_fixup - updates settings of ALSA BE hw params. + * + * @rtd: runtime dailink instance + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or rc on failure. + */ +int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = rtd->codec; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->be_id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->be_id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return rc; +} +EXPORT_SYMBOL(msm_ext_be_hw_params_fixup); + +/** + * msm_snd_hw_params - hw params ops of backend dailink. + * + * @substream: PCM stream of associated backend dailink. + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or ret on failure. + */ +int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } else { + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err_ch_map; + } + /* For _tx1 case */ + if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + } + +err_ch_map: + return ret; +} +EXPORT_SYMBOL(msm_snd_hw_params); + +/** + * msm_ext_slimbus_2_hw_params - hw params ops of slimbus_2 BE. + * + * @substream: PCM stream of associated backend dailink. + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or ret on failure. + */ +int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} +EXPORT_SYMBOL(msm_ext_slimbus_2_hw_params); + +/** + * msm_snd_cpe_hw_params - hw params ops of CPE backend. + * + * @substream: PCM stream of associated backend dailink. + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or ret on failure. + */ +int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + int ret = 0; + u32 tx_ch[SLIM_MAX_TX_PORTS]; + u32 tx_ch_cnt = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: Invalid stream type %d\n", + __func__, substream->stream); + ret = -EINVAL; + goto end; + } + + pr_debug("%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, NULL, NULL); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto end; + } + + pr_debug("%s: tx_ch_cnt(%d) be_id %d\n", + __func__, tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } +end: + return ret; +} +EXPORT_SYMBOL(msm_snd_cpe_hw_params); + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int rc; + void *config_data; + + pr_debug("%s: enter\n", __func__); + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (rc) { + pr_err("%s: Failed to set codec registers config %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (rc) + pr_err("%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (rc) { + pr_err("%s: Failed to set slimbus slave config %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + rc = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (rc) { + pr_err("%s: Failed to set AANC version %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (rc) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + rc = afe_set_config(AFE_CLIP_BANK_SEL, + config_data, 0); + if (rc) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (rc) + pr_err("%s: Failed to set cdc register page config\n", + __func__); + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (q6core_is_adsp_ready()) { + pr_debug("%s: ADSP Audio is ready\n", __func__); + adsp_ready = 1; + break; + } + /* + * ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!adsp_ready) { + pr_err("%s: timed out waiting for ADSP Audio\n", __func__); + ret = -ETIMEDOUT; + goto err_fail; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err_fail: + return ret; +} + +static int msmfalcon_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto done; + } + codec = rtd->codec; + + ret = msm_adsp_power_up_config(codec); + if (ret < 0) { + dev_err(card->dev, + "%s: msm_adsp_power_up_config failed ret = %d!\n", + __func__, ret); + goto done; + } + break; + default: + break; + } +done: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = msmfalcon_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata; + int val; + + if (!card) + return 0; + + pdata = snd_soc_card_get_drvdata(card); + if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio)) + return 0; + + val = gpio_get_value_cansleep(pdata->hph_en0_gpio); + if ((!!val) == high) + return 0; + + gpio_direction_output(pdata->hph_en0_gpio, (int)high); + + return 1; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_ext_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_ext_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_ext_enable_codec_mclk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_ext_enable_codec_mclk(codec, 0, true); + } + return 0; +} + +static int msm_ext_prepare_hifi(struct msm_asoc_mach_data *pdata) +{ + int ret = 0; + + if (gpio_is_valid(pdata->hph_en1_gpio)) { + pr_debug("%s: hph_en1_gpio request %d\n", __func__, + pdata->hph_en1_gpio); + ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio"); + if (ret) { + pr_err("%s: hph_en1_gpio request failed, ret:%d\n", + __func__, ret); + goto err; + } + } + if (gpio_is_valid(pdata->hph_en0_gpio)) { + pr_debug("%s: hph_en0_gpio request %d\n", __func__, + pdata->hph_en0_gpio); + ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio"); + if (ret) + pr_err("%s: hph_en0_gpio request failed, ret:%d\n", + __func__, ret); + } + +err: + return ret; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY_S("MCLK", -1, SND_SOC_NOPM, 0, 0, + msm_ext_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("MCLK TX", -1, SND_SOC_NOPM, 0, 0, + msm_ext_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + SND_SOC_DAPM_MIC("Analog Mic7", NULL), + SND_SOC_DAPM_MIC("Analog Mic8", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), + SND_SOC_DAPM_MIC("Digital Mic6", NULL), +}; + +static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK"}, + {"MIC BIAS2", NULL, "MCLK"}, + {"MIC BIAS3", NULL, "MCLK"}, + {"MIC BIAS4", NULL, "MCLK"}, +}; + +/** + * msm_audrx_init - Audio init function of sound card instantiate. + * + * @rtd: runtime dailink instance + * + * Returns 0 on success or ret on failure. + */ +int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_debug("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed: %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) + snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha, + ARRAY_SIZE(wcd_audio_paths_tasha)); + else + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp"); + + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8"); + + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC6"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC0"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HEADPHONE"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + + snd_soc_dapm_sync(dapm); + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + } else { + msm_codec_fn.get_afe_config_fn = tasha_get_afe_config; + msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit; + } + + ret = msm_adsp_power_up_config(codec); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err_afe_cfg; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (ret) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0); + if (ret) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + } + + /* + * Send speaker configuration only for WSA8810. + * Defalut configuration is for WSA8815. + */ + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + if (rtd_aux && rtd_aux->component) + if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || + !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + } else { + if (rtd_aux && rtd_aux->component) + if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || + !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { + tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tasha_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err_snd_module; + } + pdata->codec_root = entry; + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + tasha_mbhc_zdet_gpio_ctrl(msm_config_hph_en0_gpio, rtd->codec); + } + + wcd_mbhc_cfg_ptr->calibration = def_ext_mbhc_cal(); + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + if (wcd_mbhc_cfg_ptr->calibration) { + pdata->codec = codec; + ret = tavil_mbhc_hs_detect(codec, wcd_mbhc_cfg_ptr); + if (ret < 0) + pr_err("%s: Failed to intialise mbhc %d\n", + __func__, ret); + } else { + pr_err("%s: wcd_mbhc_cfg calibration is NULL\n", + __func__); + ret = -ENOMEM; + goto err_mbhc_cal; + } + } else { + if (wcd_mbhc_cfg_ptr->calibration) { + pdata->codec = codec; + ret = tasha_mbhc_hs_detect(codec, wcd_mbhc_cfg_ptr); + if (ret < 0) + pr_err("%s: Failed to intialise mbhc %d\n", + __func__, ret); + } else { + pr_err("%s: wcd_mbhc_cfg calibration is NULL\n", + __func__); + ret = -ENOMEM; + goto err_mbhc_cal; + } + + } +done: + return 0; + +err_snd_module: +err_afe_cfg: +err_mbhc_cal: + return ret; +} +EXPORT_SYMBOL(msm_audrx_init); + +/** + * msm_ext_register_audio_notifier - register SSR notifier. + */ +void msm_ext_register_audio_notifier(void) +{ + int ret; + + ret = audio_notifier_register("msmfalcon", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); +} +EXPORT_SYMBOL(msm_ext_register_audio_notifier); + +/** + * msm_ext_cdc_init - external codec machine specific init. + * + * @pdev: platform device handle + * @pdata: private data of machine driver + * @card: sound card pointer reference + * @mbhc_cfg: MBHC config reference + * + * Returns 0 on success or ret on failure. + */ +int msm_ext_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1) +{ + int ret = 0; + + wcd_mbhc_cfg_ptr = wcd_mbhc_cfg_ptr1; + pdev->id = 0; + wcd_mbhc_cfg_ptr->moisture_en = true; + wcd_mbhc_cfg_ptr->mbhc_micbias = MIC_BIAS_2; + wcd_mbhc_cfg_ptr->anc_micbias = MIC_BIAS_2; + wcd_mbhc_cfg_ptr->enable_anc_mic_detect = false; + + *card = populate_snd_card_dailinks(&pdev->dev, pdata->snd_card_val); + if (!(*card)) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EPROBE_DEFER; + goto err; + } + spdev = pdev; + platform_set_drvdata(pdev, *card); + snd_soc_card_set_drvdata(*card, pdata); + is_initial_boot = true; + pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!gpio_is_valid(pdata->hph_en1_gpio)) + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!gpio_is_valid(pdata->hph_en1_gpio) && (!pdata->hph_en1_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!gpio_is_valid(pdata->hph_en0_gpio)) + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!gpio_is_valid(pdata->hph_en0_gpio) && (!pdata->hph_en0_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", pdev->dev.of_node->full_name); + } + + ret = msm_ext_prepare_hifi(pdata); + if (ret) { + dev_dbg(&pdev->dev, "msm_ext_prepare_hifi failed (%d)\n", + ret); + ret = 0; + } +err: + return ret; +} +EXPORT_SYMBOL(msm_ext_cdc_init); diff --git a/sound/soc/msm/msmfalcon-external.h b/sound/soc/msm/msmfalcon-external.h new file mode 100644 index 000000000000..654cb70b9c84 --- /dev/null +++ b/sound/soc/msm/msmfalcon-external.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSMFALCON_EXTERNAL +#define __MSMFALCON_EXTERNAL + +int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_audrx_init(struct snd_soc_pcm_runtime *rtd); +int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, + int snd_card_val); +int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +#ifdef CONFIG_SND_SOC_EXT_CODEC +int msm_ext_cdc_init(struct platform_device *, struct msm_asoc_mach_data *, + struct snd_soc_card **, struct wcd_mbhc_config *); +void msm_ext_register_audio_notifier(void); +#else +inline int msm_ext_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1) +{ + return 0; +} + +inline void msm_ext_register_audio_notifier(void) +{ +} +#endif +#endif diff --git a/sound/soc/msm/msmfalcon-internal.c b/sound/soc/msm/msmfalcon-internal.c new file mode 100644 index 000000000000..34db65049f7c --- /dev/null +++ b/sound/soc/msm/msmfalcon-internal.c @@ -0,0 +1,2970 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "msm-audio-pinctrl.h" +#include "msmfalcon-common.h" +#include "../codecs/msm8x16/msm8x16-wcd.h" + +#define __CHIPSET__ "MSMFALCON " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define DEFAULT_MCLK_RATE 9600000 +#define NATIVE_MCLK_RATE 11289600 + +#define WCD_MBHC_DEF_RLOADS 5 + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +enum { + INT0_MI2S = 0, + INT1_MI2S, + INT2_MI2S, + INT3_MI2S, + INT4_MI2S, + INT5_MI2S, + INT6_MI2S, + INT_MI2S_MAX, +}; + +enum { + BT_SLIM7, + FM_SLIM8, + SLIM_MAX, +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + +static struct afe_clk_set int_mi2s_clk[INT_MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +/* Default configuration of MI2S channels */ +static struct dev_config int_mi2s_cfg[] = { + [INT0_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [INT1_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT2_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT3_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT4_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT5_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [INT6_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config bt_fm_cfg[] = { + [BT_SLIM7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [FM_SLIM8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static char const *int_mi2s_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192"}; +static const char *const int_mi2s_ch_text[] = {"One", "Two"}; +static const char *const int_mi2s_tx_ch_text[] = {"One", "Two", + "Three", "Four"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static const char *const loopback_mclk_text[] = {"DISABLE", "ENABLE"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"}; + +static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_chs, int_mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_chs, int_mi2s_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_chs, int_mi2s_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_chs, int_mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int5_mi2s_tx_chs, int_mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(loopback_mclk_en, loopback_mclk_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); + +static int msm_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, int enable, + bool dapm); +static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream); +static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream); + +static struct wcd_mbhc_config *mbhc_cfg_ptr; + +static int int_mi2s_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int int_mi2s_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int int_mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "INT0_MI2S", sizeof("INT0_MI2S"))) + port_id = INT0_MI2S; + else if (strnstr(kcontrol->id.name, "INT2_MI2S", sizeof("INT2_MI2S"))) + port_id = INT2_MI2S; + else if (strnstr(kcontrol->id.name, "INT3_MI2S", sizeof("INT3_MI2S"))) + port_id = INT3_MI2S; + else if (strnstr(kcontrol->id.name, "INT4_MI2S", sizeof("INT4_MI2S"))) + port_id = INT4_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int int_mi2s_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = int_mi2s_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + int_mi2s_get_bit_format_val(int_mi2s_cfg[ch_num].bit_format); + + pr_debug("%s: int_mi2s[%d]_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, int_mi2s_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int int_mi2s_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = int_mi2s_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + int_mi2s_cfg[ch_num].bit_format = + int_mi2s_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: int_mi2s[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, int_mi2s_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int int_mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int int_mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int int_mi2s_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + int_mi2s_cfg[idx].sample_rate = + int_mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__, + idx, int_mi2s_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int int_mi2s_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + int_mi2s_get_sample_rate_val(int_mi2s_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__, + idx, int_mi2s_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int int_mi2s_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: int_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, int_mi2s_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = int_mi2s_cfg[idx].channels - 1; + + return 0; +} + +static int int_mi2s_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + int_mi2s_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: int_mi2s_[%d]_ch = %d\n", __func__, + idx, int_mi2s_cfg[idx].channels); + + return 1; +} + +static const struct snd_soc_dapm_widget msm_int_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("INT_MCLK0", -1, SND_SOC_NOPM, 0, 0, + msm_int_mclk0_event, SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic2", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic3", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic4", msm_dmic_event), +}; + +static int msm_config_hph_compander_gpio(bool enable) +{ + int ret = 0; + + pr_debug("%s: %s HPH Compander\n", __func__, + enable ? "Enable" : "Disable"); + + if (enable) { + ret = msm_gpioset_activate(CLIENT_WCD, "comp_gpio"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "comp_gpio"); + goto done; + } + } else { + ret = msm_gpioset_suspend(CLIENT_WCD, "comp_gpio"); + if (ret) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "comp_gpio"); + goto done; + } + } + +done: + return ret; +} + +static int is_ext_spk_gpio_support(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata) +{ + const char *spk_ext_pa = "qcom,msm-spk-ext-pa"; + + pr_debug("%s:Enter\n", __func__); + + pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node, + spk_ext_pa, 0); + + if (pdata->spk_ext_pa_gpio < 0) { + dev_dbg(&pdev->dev, + "%s: missing %s in dt node\n", __func__, spk_ext_pa); + } else { + if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) { + pr_err("%s: Invalid external speaker gpio: %d", + __func__, pdata->spk_ext_pa_gpio); + return -EINVAL; + } + } + return 0; +} + +static int enable_spk_ext_pa(struct snd_soc_codec *codec, int enable) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int ret; + + if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) { + pr_err("%s: Invalid gpio: %d\n", __func__, + pdata->spk_ext_pa_gpio); + return false; + } + + pr_debug("%s: %s external speaker PA\n", __func__, + enable ? "Enable" : "Disable"); + + if (enable) { + ret = msm_gpioset_activate(CLIENT_WCD, "ext_spk_gpio"); + if (ret) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "ext_spk_gpio"); + return ret; + } + gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable); + } else { + gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable); + ret = msm_gpioset_suspend(CLIENT_WCD, "ext_spk_gpio"); + if (ret) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "ext_spk_gpio"); + return ret; + } + } + return 0; +} + +static int int_mi2s_get_idx_from_beid(int32_t be_id) +{ + int idx = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_INT0_MI2S_RX: + idx = INT0_MI2S; + break; + case MSM_BACKEND_DAI_INT2_MI2S_TX: + idx = INT2_MI2S; + break; + case MSM_BACKEND_DAI_INT3_MI2S_TX: + idx = INT3_MI2S; + break; + case MSM_BACKEND_DAI_INT4_MI2S_RX: + idx = INT4_MI2S; + break; + case MSM_BACKEND_DAI_INT5_MI2S_TX: + idx = INT5_MI2S; + break; + default: + idx = INT0_MI2S; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s()\n", __func__); + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int int_mi2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int idx; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->be_id) { + case MSM_BACKEND_DAI_INT0_MI2S_RX: + case MSM_BACKEND_DAI_INT2_MI2S_TX: + case MSM_BACKEND_DAI_INT3_MI2S_TX: + case MSM_BACKEND_DAI_INT4_MI2S_RX: + case MSM_BACKEND_DAI_INT5_MI2S_TX: + idx = int_mi2s_get_idx_from_beid(dai_link->be_id); + rate->min = rate->max = int_mi2s_cfg[idx].sample_rate; + channels->min = channels->max = + int_mi2s_cfg[idx].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + int_mi2s_cfg[idx].bit_format); + break; + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return 0; +} + +static int msm_btfm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (dai_link->be_id) { + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + bt_fm_cfg[BT_SLIM7].bit_format); + rate->min = rate->max = bt_fm_cfg[BT_SLIM7].sample_rate; + channels->min = channels->max = + bt_fm_cfg[BT_SLIM7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = bt_fm_cfg[FM_SLIM8].sample_rate; + channels->min = channels->max = + bt_fm_cfg[FM_SLIM8].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return 0; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + (int_mi2s_cfg[INT5_MI2S].channels/2 - 1); + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int_mi2s_cfg[INT5_MI2S].channels = + roundup_pow_of_two(ucontrol->value.integer.value[0] + 2); + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", + __func__, int_mi2s_cfg[INT5_MI2S].channels); + return 1; +} + +static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + struct msm_asoc_mach_data *pdata = NULL; + int clk_freq_in_hz; + bool int_mclk0_freq_chg = false; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: enable %d mclk ref counter %d\n", + __func__, enable, + atomic_read(&pdata->int_mclk0_rsc_ref)); + if (enable) { + if (int_mi2s_cfg[INT0_MI2S].sample_rate == + SAMPLING_RATE_44P1KHZ) + clk_freq_in_hz = NATIVE_MCLK_RATE; + else + clk_freq_in_hz = pdata->mclk_freq; + + if (pdata->digital_cdc_core_clk.clk_freq_in_hz + != clk_freq_in_hz) + int_mclk0_freq_chg = true; + if (!atomic_read(&pdata->int_mclk0_rsc_ref) || + int_mclk0_freq_chg) { + cancel_delayed_work_sync( + &pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == false || + int_mclk0_freq_chg) { + pdata->digital_cdc_core_clk.clk_freq_in_hz = + clk_freq_in_hz; + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: failed to enable CCLK\n", + __func__); + mutex_unlock( + &pdata->cdc_int_mclk0_mutex); + return ret; + } + pr_debug("enabled digital codec core clk\n"); + atomic_set(&pdata->int_mclk0_enabled, true); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + atomic_inc(&pdata->int_mclk0_rsc_ref); + } else { + cancel_delayed_work_sync(&pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == true) { + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) + pr_err("%s: failed to disable CCLK\n", + __func__); + atomic_set(&pdata->int_mclk0_enabled, false); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + return ret; +} + +static int loopback_mclk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int loopback_mclk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: mclk_rsc_ref %d enable %ld\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref), + ucontrol->value.integer.value[0]); + switch (ucontrol->value.integer.value[0]) { + case 1: + ret = msm_gpioset_activate(CLIENT_WCD, "int_pdm"); + if (ret) { + pr_err("%s: failed to enable the pri gpios: %d\n", + __func__, ret); + break; + } + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if ((!atomic_read(&pdata->int_mclk0_rsc_ref)) && + (!atomic_read(&pdata->int_mclk0_enabled))) { + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: failed to enable the MCLK: %d\n", + __func__, ret); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + ret = msm_gpioset_suspend(CLIENT_WCD, + "int_pdm"); + if (ret) + pr_err("%s: failed to disable the pri gpios: %d\n", + __func__, ret); + break; + } + atomic_set(&pdata->int_mclk0_enabled, true); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + atomic_inc(&pdata->int_mclk0_rsc_ref); + msm8x16_wcd_mclk_enable(codec, 1, true); + break; + case 0: + if (atomic_read(&pdata->int_mclk0_rsc_ref) <= 0) + break; + msm8x16_wcd_mclk_enable(codec, 0, true); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if ((!atomic_dec_return(&pdata->int_mclk0_rsc_ref)) && + (atomic_read(&pdata->int_mclk0_enabled))) { + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: failed to disable the CCLK: %d\n", + __func__, ret); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + break; + } + atomic_set(&pdata->int_mclk0_enabled, false); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + ret = msm_gpioset_suspend(CLIENT_WCD, "int_pdm"); + if (ret) + pr_err("%s: failed to disable the pri gpios: %d\n", + __func__, ret); + break; + default: + pr_err("%s: Unexpected input value\n", __func__); + break; + } + return ret; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (bt_fm_cfg[BT_SLIM7].sample_rate) { + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + bt_fm_cfg[BT_SLIM7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 0: + default: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n", + __func__, + bt_fm_cfg[BT_SLIM7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("INT0_MI2S_RX Format", int0_mi2s_rx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT2_MI2S_TX Format", int2_mi2s_tx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT3_MI2S_TX Format", int3_mi2s_tx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT0_MI2S_RX SampleRate", int0_mi2s_rx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT2_MI2S_TX SampleRate", int2_mi2s_tx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT3_MI2S_TX SampleRate", int3_mi2s_tx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT0_MI2S_RX SampleRate", int0_mi2s_rx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT2_MI2S_TX SampleRate", int2_mi2s_tx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT3_MI2S_TX SampleRate", int3_mi2s_tx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT0_MI2S_RX Channels", int0_mi2s_rx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("INT2_MI2S_TX Channels", int2_mi2s_tx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("INT3_MI2S_TX Channels", int3_mi2s_tx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("Loopback MCLK", loopback_mclk_en, + loopback_mclk_get, loopback_mclk_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_swr_controls[] = { + SOC_ENUM_EXT("INT4_MI2S_RX Format", int4_mi2s_rx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT4_MI2S_RX SampleRate", int4_mi2s_rx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT4_MI2S_RX SampleRate", int4_mi2s_rx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT4_MI2S_RX Channels", int4_mi2s_rx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", int5_mi2s_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), +}; + +static int msm_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = msm_gpioset_activate(CLIENT_WCD, "dmic_gpio"); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %sd", + __func__, "dmic_gpio"); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = msm_gpioset_suspend(CLIENT_WCD, "dmic_gpio"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "dmic_gpio"); + return ret; + } + break; + default: + pr_err("%s: invalid DAPM event %d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_POST_PMD: + pr_debug("%s: mclk_res_ref = %d\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref)); + ret = msm_gpioset_suspend(CLIENT_WCD, "int_pdm"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "int_pdm"); + return ret; + } + if (atomic_read(&pdata->int_mclk0_rsc_ref) == 0) { + pr_debug("%s: disabling MCLK\n", __func__); + /* disable the codec mclk config*/ + msm8x16_wcd_mclk_enable(codec, 0, true); + msm_int_enable_dig_cdc_clk(codec, 0, true); + } + break; + default: + pr_err("%s: invalid DAPM event %d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static int int_mi2s_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_INT0_MI2S_RX: + afe_port_id = AFE_PORT_ID_INT0_MI2S_RX; + break; + case MSM_BACKEND_DAI_INT2_MI2S_TX: + afe_port_id = AFE_PORT_ID_INT2_MI2S_TX; + break; + case MSM_BACKEND_DAI_INT3_MI2S_TX: + afe_port_id = AFE_PORT_ID_INT3_MI2S_TX; + break; + case MSM_BACKEND_DAI_INT4_MI2S_RX: + afe_port_id = AFE_PORT_ID_INT4_MI2S_RX; + break; + case MSM_BACKEND_DAI_INT5_MI2S_TX: + afe_port_id = AFE_PORT_ID_INT5_MI2S_TX; + break; + default: + pr_err("%s: Invalid be_id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static int int_mi2s_get_index(int port_id) +{ + int index; + + switch (port_id) { + case AFE_PORT_ID_INT0_MI2S_RX: + index = INT0_MI2S; + break; + case AFE_PORT_ID_INT2_MI2S_TX: + index = INT2_MI2S; + break; + case AFE_PORT_ID_INT3_MI2S_TX: + index = INT3_MI2S; + break; + case AFE_PORT_ID_INT4_MI2S_RX: + index = INT4_MI2S; + break; + case AFE_PORT_ID_INT5_MI2S_TX: + index = INT5_MI2S; + break; + default: + pr_err("%s: Invalid port_id: %d\n", __func__, port_id); + index = -EINVAL; + } + + return index; +} + +static u32 get_int_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_int_mi2s_clk_val(int idx, int stream) +{ + u32 bit_per_sample; + + bit_per_sample = + get_int_mi2s_bits_per_sample(int_mi2s_cfg[idx].bit_format); + int_mi2s_clk[idx].clk_freq_in_hz = + (int_mi2s_cfg[idx].sample_rate * int_mi2s_cfg[idx].channels + * bit_per_sample); +} + +static int int_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int port_id = 0; + int index; + + port_id = int_mi2s_get_port_id(rtd->dai_link->be_id); + if (IS_ERR_VALUE(port_id)) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + index = int_mi2s_get_index(port_id); + if (index < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + if (enable) { + update_int_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + int_mi2s_clk[index].clk_freq_in_hz); + } + + int_mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &int_mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto done; + } + +done: + return ret; +} + +static int msm_swr_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, true); + if (ret < 0) { + pr_err("%s: failed to enable sclk %d\n", + __func__, ret); + return ret; + } + /* Enable the codec mclk config */ + ret = msm_gpioset_activate(CLIENT_WCD, "swr_pin"); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %sd", + __func__, "swr_pin"); + return ret; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret); + + return ret; +} + +static void msm_swr_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed; ret=%d\n", __func__, + ret); +} + +static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + int ret = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, true); + if (ret < 0) { + pr_err("%s: failed to enable sclk %d\n", + __func__, ret); + return ret; + } + ret = msm_int_enable_dig_cdc_clk(codec, 1, true); + if (ret < 0) { + pr_err("failed to enable mclk\n"); + return ret; + } + /* Enable the codec mclk config */ + ret = msm_gpioset_activate(CLIENT_WCD, "int_pdm"); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "int_pdm"); + return ret; + } + msm8x16_wcd_mclk_enable(codec, 1, true); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret); + + return ret; +} + +static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed; ret=%d\n", __func__, + ret); + if (atomic_read(&pdata->int_mclk0_rsc_ref) > 0) { + atomic_dec(&pdata->int_mclk0_rsc_ref); + pr_debug("%s: decrementing mclk_res_ref %d\n", + __func__, + atomic_read(&pdata->int_mclk0_rsc_ref)); + } +} + +static void *def_msm_int_wcd_mbhc_cal(void) +{ + void *msm_int_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_low, *btn_high; + + msm_int_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!msm_int_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(msm_int_wcd_cal)->X) = (Y)) + S(v_hs_max, 1500); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal); + btn_low = btn_cfg->_v_btn_low; + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + /* + * In SW we are maintaining two sets of threshold register + * one for current source and another for Micbias. + * all btn_low corresponds to threshold for current source + * all bt_high corresponds to threshold for Micbias + * Below thresholds are based on following resistances + * 0-70 == Button 0 + * 110-180 == Button 1 + * 210-290 == Button 2 + * 360-680 == Button 3 + */ + btn_low[0] = 75; + btn_high[0] = 75; + btn_low[1] = 150; + btn_high[1] = 150; + btn_low[2] = 225; + btn_high[2] = 225; + btn_low[3] = 450; + btn_high[3] = 450; + btn_low[4] = 500; + btn_high[4] = 500; + + return msm_int_wcd_cal; +} + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = -ENOMEM; + + pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + + snd_soc_add_codec_controls(codec, msm_common_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + + snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets, + ARRAY_SIZE(msm_int_dapm_widgets)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "HEADPHONE"); + snd_soc_dapm_ignore_suspend(dapm, "SPK_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4"); + + snd_soc_dapm_sync(dapm); + + msm8x16_wcd_spk_ext_pa_cb(enable_spk_ext_pa, codec); + msm8x16_wcd_hph_comp_cb(msm_config_hph_compander_gpio, codec); + + mbhc_cfg_ptr->calibration = def_msm_int_wcd_mbhc_cal(); + if (mbhc_cfg_ptr->calibration) { + ret = msm8x16_wcd_hs_detect(codec, mbhc_cfg_ptr); + if (ret) { + pr_err("%s: msm8x16_wcd_hs_detect failed\n", __func__); + kfree(mbhc_cfg_ptr->calibration); + return ret; + } + } + return 0; +} + +static int msm_swr_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + + snd_soc_add_codec_controls(codec, msm_swr_controls, + ARRAY_SIZE(msm_swr_controls)); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1_SWR Playback"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed_SWR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1_SWR VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_SWR"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto exit; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n", + __func__, tx_ch_cnt, dai_link->be_id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +exit: + return ret; +} + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static struct snd_soc_ops msm_int_mi2s_be_ops = { + .startup = msm_int_mi2s_snd_startup, + .shutdown = msm_int_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_swr_mi2s_be_ops = { + .startup = msm_swr_mi2s_snd_startup, + .shutdown = msm_swr_mi2s_snd_shutdown, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_int_dai[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "ULL", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "INT4 MI2S_RX Hostless", + .stream_name = "INT4 MI2S_RX Hostless", + .cpu_dai_name = "INT4_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + {/* hw:x,8 */ + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,9*/ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,10 */ + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,13 */ + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + /* LSM FE */ + {/* hw:x,14 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + {/* hw:x,15 */ + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + {/* hw:x,16 */ + .name = MSM_DAILINK_NAME(Compress3), + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,17 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + {/* hw:x,18 */ + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,19 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + {/* hw:x,24 */ + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM6 + }, + {/* hw:x,25 */ + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM7, + }, + {/* hw:x,26 */ + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM8, + }, + {/* hw:x,27 */ + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + {/* hw:x,28 */ + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = MSM_DAILINK_NAME(Compress9), + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,35 */ + .name = LPASS_BE_INT5_MI2S_TX, + .stream_name = "INT5_mi2s Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.12", + .platform_name = "msm-pcm-hostless", + .codec_name = "msm_swr_codec", + .codec_dai_name = "msm_swr_vifeedback", + .be_id = MSM_BACKEND_DAI_INT5_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_swr_mi2s_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, + {/* hw:x,36 */ + .name = "Primary MI2S_RX Hostless", + .stream_name = "Primary MI2S_RX Hostless", + .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,37 */ + .name = "Secondary MI2S_RX Hostless", + .stream_name = "Secondary MI2S_RX Hostless", + .cpu_dai_name = "SEC_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,38 */ + .name = "Tertiary MI2S_RX Hostless", + .stream_name = "Tertiary MI2S_RX Hostless", + .cpu_dai_name = "TERT_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,39 */ + .name = "INT0 MI2S_RX Hostless", + .stream_name = "INT0 MI2S_RX Hostless", + .cpu_dai_name = "INT0_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* Backend I2S DAI Links */ + { + .name = LPASS_BE_INT0_MI2S_RX, + .stream_name = "INT0 MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.7", + .platform_name = "msm-pcm-routing", + .codec_name = "cajon_codec", + .codec_dai_name = "msm8x16_wcd_i2s_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .be_id = MSM_BACKEND_DAI_INT0_MI2S_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_int_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT4_MI2S_RX, + .stream_name = "INT4 MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.11", + .platform_name = "msm-pcm-routing", + .codec_name = "msm_swr_codec", + .codec_dai_name = "msm_swr_i2s_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT4_MI2S_RX, + .init = &msm_swr_audrx_init, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_swr_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT2_MI2S_TX, + .stream_name = "INT2 MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.9", + .platform_name = "msm-pcm-routing", + .codec_name = "cajon_codec", + .codec_dai_name = "msm8x16_wcd_i2s_tx2", + .no_pcm = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .be_id = MSM_BACKEND_DAI_INT2_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_int_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT3_MI2S_TX, + .stream_name = "INT3 MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.10", + .platform_name = "msm-pcm-routing", + .codec_name = "cajon_codec", + .codec_dai_name = "msm8x16_wcd_i2s_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .be_id = MSM_BACKEND_DAI_INT3_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_int_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_int_dai_links[ +ARRAY_SIZE(msm_int_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links)+ +ARRAY_SIZE(msm_wcn_be_dai_links)]; + +static struct snd_soc_card msmfalcon_card = { + /* snd_soc_card_msmfalcon */ + .name = "msmfalcon-snd-card", + .dai_link = msm_int_dai, + .num_links = ARRAY_SIZE(msm_int_dai), +}; + +static void msm_disable_int_mclk0(struct work_struct *work) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct delayed_work *dwork; + int ret = 0; + + dwork = to_delayed_work(work); + pdata = container_of(dwork, struct msm_asoc_mach_data, + disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__, + atomic_read(&pdata->int_mclk0_enabled), + atomic_read(&pdata->int_mclk0_rsc_ref)); + + if (atomic_read(&pdata->int_mclk0_enabled) == true + && atomic_read(&pdata->int_mclk0_rsc_ref) == 0) { + pr_debug("Disable the mclk\n"); + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) + pr_err("%s failed to disable the CCLK\n", __func__); + atomic_set(&pdata->int_mclk0_enabled, false); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); +} + +static void msm_int_dt_parse_cap_info(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata) +{ + const char *ext1_cap = "qcom,msm-micbias1-ext-cap"; + const char *ext2_cap = "qcom,msm-micbias2-ext-cap"; + + pdata->micbias1_cap_mode = + (of_property_read_bool(pdev->dev.of_node, ext1_cap) ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + + pdata->micbias2_cap_mode = + (of_property_read_bool(pdev->dev.of_node, ext2_cap) ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); +} + +static struct snd_soc_card *msm_int_populate_sndcard_dailinks( + struct device *dev) +{ + struct snd_soc_card *card = &msmfalcon_card; + struct snd_soc_dai_link *dailink; + int len1; + + card->name = dev_name(dev); + len1 = ARRAY_SIZE(msm_int_dai); + memcpy(msm_int_dai_links, msm_int_dai, sizeof(msm_int_dai)); + dailink = msm_int_dai_links; + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(dailink + len1, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + len1 += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(dailink + len1, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + len1 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(dailink + len1, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + len1 += ARRAY_SIZE(msm_wcn_be_dai_links); + } + card->dai_link = dailink; + card->num_links = len1; + return card; +} + +static int msm_internal_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card *card) +{ + const char *type = NULL; + const char *hs_micbias_type = "qcom,msm-hs-micbias-type"; + int ret; + + ret = is_ext_spk_gpio_support(pdev, pdata); + if (ret < 0) + dev_dbg(&pdev->dev, + "%s: doesn't support external speaker pa\n", + __func__); + + ret = of_property_read_string(pdev->dev.of_node, + hs_micbias_type, &type); + if (ret) { + dev_err(&pdev->dev, "%s: missing %s in dt node\n", + __func__, hs_micbias_type); + goto err; + } + if (!strcmp(type, "external")) { + dev_dbg(&pdev->dev, "Headset is using external micbias\n"); + mbhc_cfg_ptr->hs_ext_micbias = true; + } else { + dev_dbg(&pdev->dev, "Headset is using internal micbias\n"); + mbhc_cfg_ptr->hs_ext_micbias = false; + } + + /* initialize the int_mclk0 */ + pdata->digital_cdc_core_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + pdata->digital_cdc_core_clk.clk_id = + Q6AFE_LPASS_CLK_ID_INT_MCLK_0; + pdata->digital_cdc_core_clk.clk_freq_in_hz = + pdata->mclk_freq; + pdata->digital_cdc_core_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + pdata->digital_cdc_core_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + pdata->digital_cdc_core_clk.enable = 1; + + /* Initialize loopback mode to false */ + pdata->lb_mode = false; + + msm_int_dt_parse_cap_info(pdev, pdata); + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) + goto err; + /* initialize timer */ + INIT_DELAYED_WORK(&pdata->disable_int_mclk0_work, + msm_disable_int_mclk0); + mutex_init(&pdata->cdc_int_mclk0_mutex); + atomic_set(&pdata->int_mclk0_rsc_ref, 0); + atomic_set(&pdata->int_mclk0_enabled, false); + + dev_info(&pdev->dev, "%s: default codec configured\n", __func__); + + return 0; +err: + return ret; +} + +/** + * msm_int_cdc_init - internal codec machine specific init. + * + * @pdev: platform device handle + * @pdata: private data of machine driver + * @card: sound card pointer reference + * @mbhc_cfg: MBHC config reference + * + * Returns 0. + */ +int msm_int_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *mbhc_cfg) +{ + mbhc_cfg_ptr = mbhc_cfg; + + *card = msm_int_populate_sndcard_dailinks(&pdev->dev); + msm_internal_init(pdev, pdata, *card); + return 0; +} +EXPORT_SYMBOL(msm_int_cdc_init); diff --git a/sound/soc/msm/msmfalcon-internal.h b/sound/soc/msm/msmfalcon-internal.h new file mode 100644 index 000000000000..e5e3e7c66246 --- /dev/null +++ b/sound/soc/msm/msmfalcon-internal.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSMFALCON_INTERNAL +#define __MSMFALCON_INTERNAL + +#include + +#ifdef CONFIG_SND_SOC_INT_CODEC +int msm_int_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *mbhc_cfg); +#else +int msm_int_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +#endif +#endif diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile new file mode 100644 index 000000000000..461c09db2937 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/Makefile @@ -0,0 +1,21 @@ +snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \ + msm-compress-q6-v2.o msm-compr-q6-v2.o \ + msm-pcm-afe-v2.o msm-pcm-voip-v2.o \ + msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o \ + msm-lsm-client.o msm-pcm-host-voice-v2.o \ + msm-audio-effects-q6-v2.o msm-pcm-loopback-v2.o \ + msm-dai-slim.o \ + adsp_err.o +obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \ + msm-dai-stub-v2.o +obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o +obj-$(CONFIG_DTS_EAGLE) += msm-dts-eagle.o +obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o +obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o +obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o +obj-$(CONFIG_QTI_PP) += msm-qti-pp-config.o +obj-y += audio_calibration.o audio_cal_utils.o q6adm.o q6afe.o q6asm.o \ + q6audio-v2.o q6voice.o q6core.o rtac.o q6lsm.o audio_slimslave.o \ + msm-pcm-q6-noirq.o +ocmem-audio-objs += audio_ocmem.o +obj-$(CONFIG_AUDIO_OCMEM) += ocmem-audio.o diff --git a/sound/soc/msm/qdsp6v2/adsp_err.c b/sound/soc/msm/qdsp6v2/adsp_err.c new file mode 100644 index 000000000000..d17bd6ab58be --- /dev/null +++ b/sound/soc/msm/qdsp6v2/adsp_err.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + + +/* ERROR STRING */ +/* Success. The operation completed with no errors. */ +#define ADSP_EOK_STR "ADSP_EOK" +/* General failure. */ +#define ADSP_EFAILED_STR "ADSP_EFAILED" +/* Bad operation parameter. */ +#define ADSP_EBADPARAM_STR "ADSP_EBADPARAM" +/* Unsupported routine or operation. */ +#define ADSP_EUNSUPPORTED_STR "ADSP_EUNSUPPORTED" +/* Unsupported version. */ +#define ADSP_EVERSION_STR "ADSP_EVERSION" +/* Unexpected problem encountered. */ +#define ADSP_EUNEXPECTED_STR "ADSP_EUNEXPECTED" +/* Unhandled problem occurred. */ +#define ADSP_EPANIC_STR "ADSP_EPANIC" +/* Unable to allocate resource. */ +#define ADSP_ENORESOURCE_STR "ADSP_ENORESOURCE" +/* Invalid handle. */ +#define ADSP_EHANDLE_STR "ADSP_EHANDLE" +/* Operation is already processed. */ +#define ADSP_EALREADY_STR "ADSP_EALREADY" +/* Operation is not ready to be processed. */ +#define ADSP_ENOTREADY_STR "ADSP_ENOTREADY" +/* Operation is pending completion. */ +#define ADSP_EPENDING_STR "ADSP_EPENDING" +/* Operation could not be accepted or processed. */ +#define ADSP_EBUSY_STR "ADSP_EBUSY" +/* Operation aborted due to an error. */ +#define ADSP_EABORTED_STR "ADSP_EABORTED" +/* Operation preempted by a higher priority. */ +#define ADSP_EPREEMPTED_STR "ADSP_EPREEMPTED" +/* Operation requests intervention to complete. */ +#define ADSP_ECONTINUE_STR "ADSP_ECONTINUE" +/* Operation requests immediate intervention to complete. */ +#define ADSP_EIMMEDIATE_STR "ADSP_EIMMEDIATE" +/* Operation is not implemented. */ +#define ADSP_ENOTIMPL_STR "ADSP_ENOTIMPL" +/* Operation needs more data or resources. */ +#define ADSP_ENEEDMORE_STR "ADSP_ENEEDMORE" +/* Operation does not have memory. */ +#define ADSP_ENOMEMORY_STR "ADSP_ENOMEMORY" +/* Item does not exist. */ +#define ADSP_ENOTEXIST_STR "ADSP_ENOTEXIST" +/* Unexpected error code. */ +#define ADSP_ERR_MAX_STR "ADSP_ERR_MAX" + +#ifdef CONFIG_SND_SOC_QDSP_DEBUG +static bool adsp_err_panic; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_adsp_err; + +static ssize_t adsp_err_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char cmd; + + if (copy_from_user(&cmd, ubuf, 1)) + return -EFAULT; + + if (cmd == '0') + adsp_err_panic = false; + else + adsp_err_panic = true; + + return cnt; +} + +static const struct file_operations adsp_err_debug_ops = { + .write = adsp_err_debug_write, +}; +#endif +#endif + +struct adsp_err_code { + int lnx_err_code; + char *adsp_err_str; +}; + + +static struct adsp_err_code adsp_err_code_info[ADSP_ERR_MAX+1] = { + { 0, ADSP_EOK_STR}, + { -ENOTRECOVERABLE, ADSP_EFAILED_STR}, + { -EINVAL, ADSP_EBADPARAM_STR}, + { -EOPNOTSUPP, ADSP_EUNSUPPORTED_STR}, + { -ENOPROTOOPT, ADSP_EVERSION_STR}, + { -ENOTRECOVERABLE, ADSP_EUNEXPECTED_STR}, + { -ENOTRECOVERABLE, ADSP_EPANIC_STR}, + { -ENOSPC, ADSP_ENORESOURCE_STR}, + { -EBADR, ADSP_EHANDLE_STR}, + { -EALREADY, ADSP_EALREADY_STR}, + { -EPERM, ADSP_ENOTREADY_STR}, + { -EINPROGRESS, ADSP_EPENDING_STR}, + { -EBUSY, ADSP_EBUSY_STR}, + { -ECANCELED, ADSP_EABORTED_STR}, + { -EAGAIN, ADSP_EPREEMPTED_STR}, + { -EAGAIN, ADSP_ECONTINUE_STR}, + { -EAGAIN, ADSP_EIMMEDIATE_STR}, + { -EAGAIN, ADSP_ENOTIMPL_STR}, + { -ENODATA, ADSP_ENEEDMORE_STR}, + { -EADV, ADSP_ERR_MAX_STR}, + { -ENOMEM, ADSP_ENOMEMORY_STR}, + { -ENODEV, ADSP_ENOTEXIST_STR}, + { -EADV, ADSP_ERR_MAX_STR}, +}; + +#ifdef CONFIG_SND_SOC_QDSP_DEBUG +static inline void adsp_err_check_panic(u32 adsp_error) +{ + if (adsp_err_panic && adsp_error != ADSP_EALREADY) + panic("%s: encounter adsp_err=0x%x\n", __func__, adsp_error); +} +#else +static inline void adsp_err_check_panic(u32 adsp_error) {} +#endif + +int adsp_err_get_lnx_err_code(u32 adsp_error) +{ + adsp_err_check_panic(adsp_error); + + if (adsp_error > ADSP_ERR_MAX) + return adsp_err_code_info[ADSP_ERR_MAX].lnx_err_code; + else + return adsp_err_code_info[adsp_error].lnx_err_code; +} + +char *adsp_err_get_err_str(u32 adsp_error) +{ + if (adsp_error > ADSP_ERR_MAX) + return adsp_err_code_info[ADSP_ERR_MAX].adsp_err_str; + else + return adsp_err_code_info[adsp_error].adsp_err_str; +} + +#if defined(CONFIG_SND_SOC_QDSP_DEBUG) && defined(CONFIG_DEBUG_FS) +static int __init adsp_err_init(void) +{ + + + debugfs_adsp_err = debugfs_create_file("msm_adsp_audio_debug", + S_IFREG | 0444, NULL, NULL, + &adsp_err_debug_ops); + + return 0; +} + +device_initcall(adsp_err_init); +#endif diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/sound/soc/msm/qdsp6v2/audio_cal_utils.c new file mode 100644 index 000000000000..f88087ba969b --- /dev/null +++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c @@ -0,0 +1,965 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include + +static int unmap_memory(struct cal_type_data *cal_type, + struct cal_block_data *cal_block); + +size_t get_cal_info_size(int32_t cal_type) +{ + size_t size = 0; + + switch (cal_type) { + case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_top); + break; + case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_top); + break; + case CVP_VOCPROC_STATIC_CAL_TYPE: + size = sizeof(struct audio_cal_info_vocproc); + break; + case CVP_VOCPROC_DYNAMIC_CAL_TYPE: + size = sizeof(struct audio_cal_info_vocvol); + break; + case CVS_VOCSTRM_STATIC_CAL_TYPE: + size = 0; + break; + case CVP_VOCDEV_CFG_CAL_TYPE: + size = sizeof(struct audio_cal_info_vocdev_cfg); + break; + case CVP_VOCPROC_STATIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_col); + break; + case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_col); + break; + case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_col); + break; + case ADM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_adm_top); + break; + case ADM_CUST_TOPOLOGY_CAL_TYPE: + case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: + size = 0; + break; + case ADM_AUDPROC_CAL_TYPE: + size = sizeof(struct audio_cal_info_audproc); + break; + case ADM_AUDVOL_CAL_TYPE: + case ADM_RTAC_AUDVOL_CAL_TYPE: + size = sizeof(struct audio_cal_info_audvol); + break; + case ASM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_asm_top); + break; + case ASM_CUST_TOPOLOGY_CAL_TYPE: + size = 0; + break; + case ASM_AUDSTRM_CAL_TYPE: + size = sizeof(struct audio_cal_info_audstrm); + break; + case AFE_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe_top); + break; + case AFE_CUST_TOPOLOGY_CAL_TYPE: + size = 0; + break; + case AFE_COMMON_RX_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe); + break; + case AFE_COMMON_TX_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe); + break; + case AFE_FB_SPKR_PROT_CAL_TYPE: + size = sizeof(struct audio_cal_info_spk_prot_cfg); + break; + case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_info_sp_th_vi_ftm_cfg), + sizeof(struct audio_cal_info_sp_th_vi_param)); + break; + case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_info_sp_ex_vi_ftm_cfg), + sizeof(struct audio_cal_info_sp_ex_vi_param)); + break; + case AFE_ANC_CAL_TYPE: + size = 0; + break; + case AFE_AANC_CAL_TYPE: + size = sizeof(struct audio_cal_info_aanc); + break; + case AFE_HW_DELAY_CAL_TYPE: + size = sizeof(struct audio_cal_info_hw_delay); + break; + case AFE_SIDETONE_CAL_TYPE: + size = sizeof(struct audio_cal_info_sidetone); + break; + case LSM_CUST_TOPOLOGY_CAL_TYPE: + size = 0; + break; + case LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm_top); + break; + case ULP_LSM_TOPOLOGY_ID_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm_top); + break; + case LSM_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm); + break; + case ADM_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case ADM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case ASM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_APR_CAL_TYPE: + size = 0; + break; + case MAD_CAL_TYPE: + size = 0; + break; + case ULP_AFE_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe); + break; + case ULP_LSM_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm); + break; + case DTS_EAGLE_CAL_TYPE: + size = 0; + break; + case AUDIO_CORE_METAINFO_CAL_TYPE: + size = sizeof(struct audio_cal_info_metainfo); + break; + case SRS_TRUMEDIA_CAL_TYPE: + size = 0; + break; + default: + pr_err("%s:Invalid cal type %d!", + __func__, cal_type); + } + return size; +} + +size_t get_user_cal_type_size(int32_t cal_type) +{ + size_t size = 0; + + switch (cal_type) { + case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_voc_top); + break; + case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_voc_top); + break; + case CVP_VOCPROC_STATIC_CAL_TYPE: + size = sizeof(struct audio_cal_type_vocproc); + break; + case CVP_VOCPROC_DYNAMIC_CAL_TYPE: + size = sizeof(struct audio_cal_type_vocvol); + break; + case CVS_VOCSTRM_STATIC_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case CVP_VOCDEV_CFG_CAL_TYPE: + size = sizeof(struct audio_cal_type_vocdev_cfg); + break; + case CVP_VOCPROC_STATIC_COL_CAL_TYPE: + case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: + case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_type_voc_col); + break; + case ADM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_adm_top); + break; + case ADM_CUST_TOPOLOGY_CAL_TYPE: + case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case ADM_AUDPROC_CAL_TYPE: + size = sizeof(struct audio_cal_type_audproc); + break; + case ADM_AUDVOL_CAL_TYPE: + case ADM_RTAC_AUDVOL_CAL_TYPE: + size = sizeof(struct audio_cal_type_audvol); + break; + case ASM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_asm_top); + break; + case ASM_CUST_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case ASM_AUDSTRM_CAL_TYPE: + size = sizeof(struct audio_cal_type_audstrm); + break; + case AFE_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe_top); + break; + case AFE_CUST_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case AFE_COMMON_RX_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe); + break; + case AFE_COMMON_TX_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe); + break; + case AFE_FB_SPKR_PROT_CAL_TYPE: + size = sizeof(struct audio_cal_type_fb_spk_prot_cfg); + break; + case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_type_sp_th_vi_ftm_cfg), + sizeof(struct audio_cal_type_sp_th_vi_param)); + break; + case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_type_sp_ex_vi_ftm_cfg), + sizeof(struct audio_cal_type_sp_ex_vi_param)); + break; + case AFE_ANC_CAL_TYPE: + size = 0; + break; + case AFE_AANC_CAL_TYPE: + size = sizeof(struct audio_cal_type_aanc); + break; + case AFE_HW_DELAY_CAL_TYPE: + size = sizeof(struct audio_cal_type_hw_delay); + break; + case AFE_SIDETONE_CAL_TYPE: + size = sizeof(struct audio_cal_type_sidetone); + break; + case LSM_CUST_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm_top); + break; + case ULP_LSM_TOPOLOGY_ID_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm_top); + break; + case LSM_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm); + break; + case ADM_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case ADM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case ASM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_APR_CAL_TYPE: + size = 0; + break; + case MAD_CAL_TYPE: + size = 0; + break; + case ULP_AFE_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe); + break; + case ULP_LSM_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm); + break; + case DTS_EAGLE_CAL_TYPE: + size = 0; + break; + case AUDIO_CORE_METAINFO_CAL_TYPE: + size = sizeof(struct audio_cal_type_metainfo); + break; + case SRS_TRUMEDIA_CAL_TYPE: + size = 0; + break; + default: + pr_err("%s:Invalid cal type %d!", + __func__, cal_type); + } + return size; +} + +int32_t cal_utils_get_cal_type_version(void *cal_type_data) +{ + struct audio_cal_type_basic *data = NULL; + + data = (struct audio_cal_type_basic *)cal_type_data; + + return data->cal_hdr.version; +} + +static struct cal_type_data *create_cal_type_data( + struct cal_type_info *info) +{ + struct cal_type_data *cal_type = NULL; + + if ((info->reg.cal_type < 0) || + (info->reg.cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d is Invalid!\n", + __func__, info->reg.cal_type); + goto done; + } + + if (info->cal_util_callbacks.match_block == NULL) { + pr_err("%s: cal type %d no method to match blocks!\n", + __func__, info->reg.cal_type); + goto done; + } + + cal_type = kmalloc(sizeof(*cal_type), GFP_KERNEL); + if (cal_type == NULL) + goto done; + + INIT_LIST_HEAD(&cal_type->cal_blocks); + mutex_init(&cal_type->lock); + memcpy(&cal_type->info, info, + sizeof(cal_type->info)); +done: + return cal_type; +} + +int cal_utils_create_cal_types(int num_cal_types, + struct cal_type_data **cal_type, + struct cal_type_info *info) +{ + int ret = 0; + int i; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if (info == NULL) { + pr_err("%s: info is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + ret = -EINVAL; + goto done; + } + + for (i = 0; i < num_cal_types; i++) { + if ((info[i].reg.cal_type < 0) || + (info[i].reg.cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d at index %d is Invalid!\n", + __func__, info[i].reg.cal_type, i); + ret = -EINVAL; + goto done; + } + + cal_type[i] = create_cal_type_data(&info[i]); + if (cal_type[i] == NULL) { + pr_err("%s: Could not allocate cal_type of index %d!\n", + __func__, i); + ret = -EINVAL; + goto done; + } + + ret = audio_cal_register(1, &info[i].reg); + if (ret < 0) { + pr_err("%s: audio_cal_register failed, ret = %d!\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + pr_debug("%s: cal type %d at index %d!\n", + __func__, info[i].reg.cal_type, i); + } +done: + return ret; +} + +static void delete_cal_block(struct cal_block_data *cal_block) +{ + pr_debug("%s\n", __func__); + + if (cal_block == NULL) + goto done; + + list_del(&cal_block->list); + kfree(cal_block->client_info); + cal_block->client_info = NULL; + kfree(cal_block->cal_info); + cal_block->cal_info = NULL; + if (cal_block->map_data.ion_client != NULL) { + msm_audio_ion_free(cal_block->map_data.ion_client, + cal_block->map_data.ion_handle); + cal_block->map_data.ion_client = NULL; + cal_block->map_data.ion_handle = NULL; + } + kfree(cal_block); +done: + return; +} + +static void destroy_all_cal_blocks(struct cal_type_data *cal_type) +{ + int ret = 0; + struct list_head *ptr, *next; + struct cal_block_data *cal_block; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + ret = unmap_memory(cal_type, cal_block); + if (ret < 0) { + pr_err("%s: unmap_memory failed, cal type %d, ret = %d!\n", + __func__, + cal_type->info.reg.cal_type, + ret); + } + delete_cal_block(cal_block); + cal_block = NULL; + } +} + +static void destroy_cal_type_data(struct cal_type_data *cal_type) +{ + if (cal_type == NULL) + goto done; + + destroy_all_cal_blocks(cal_type); + list_del(&cal_type->cal_blocks); + kfree(cal_type); +done: + return; +} + +void cal_utils_destroy_cal_types(int num_cal_types, + struct cal_type_data **cal_type) +{ + int i; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + goto done; + } + + for (i = 0; i < num_cal_types; i++) { + audio_cal_deregister(1, &cal_type[i]->info.reg); + destroy_cal_type_data(cal_type[i]); + cal_type[i] = NULL; + } +done: + return; +} + +struct cal_block_data *cal_utils_get_only_cal_block( + struct cal_type_data *cal_type) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + if (cal_type == NULL) + goto done; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + break; + } +done: + return cal_block; +} + +bool cal_utils_match_buf_num(struct cal_block_data *cal_block, + void *user_data) +{ + bool ret = false; + struct audio_cal_type_basic *data = user_data; + + if (cal_block->buffer_number == data->cal_hdr.buffer_number) + ret = true; + + return ret; +} + +static struct cal_block_data *get_matching_cal_block( + struct cal_type_data *cal_type, + void *data) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_type->info.cal_util_callbacks. + match_block(cal_block, data)) + return cal_block; + } + + return NULL; +} + +static int cal_block_ion_alloc(struct cal_block_data *cal_block) +{ + int ret = 0; + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } + + ret = msm_audio_ion_import("audio_cal_client", + &cal_block->map_data.ion_client, + &cal_block->map_data.ion_handle, + cal_block->map_data.ion_map_handle, + NULL, 0, + &cal_block->cal_data.paddr, + &cal_block->map_data.map_size, + &cal_block->cal_data.kvaddr); + if (ret) { + pr_err("%s: audio ION import failed, rc = %d\n", + __func__, ret); + ret = -ENOMEM; + goto done; + } +done: + return ret; +} + +static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type, + struct audio_cal_type_basic *basic_cal, + size_t client_info_size, void *client_info) +{ + struct cal_block_data *cal_block = NULL; + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + goto done; + } else if (basic_cal == NULL) { + pr_err("%s: basic_cal is NULL!\n", __func__); + goto done; + } + + cal_block = kzalloc(sizeof(*cal_block), + GFP_KERNEL); + if (cal_block == NULL) + goto done; + + INIT_LIST_HEAD(&cal_block->list); + list_add_tail(&cal_block->list, &cal_type->cal_blocks); + + cal_block->map_data.ion_map_handle = basic_cal->cal_data.mem_handle; + if (basic_cal->cal_data.mem_handle > 0) { + if (cal_block_ion_alloc(cal_block)) { + pr_err("%s: cal_block_ion_alloc failed!\n", + __func__); + goto err; + } + } + if (client_info_size > 0) { + cal_block->client_info_size = client_info_size; + cal_block->client_info = kmalloc(client_info_size, GFP_KERNEL); + if (cal_block->client_info == NULL) { + pr_err("%s: could not allocats client_info!\n", + __func__); + goto err; + } + if (client_info != NULL) + memcpy(cal_block->client_info, client_info, + client_info_size); + } + + cal_block->cal_info = kzalloc( + get_cal_info_size(cal_type->info.reg.cal_type), + GFP_KERNEL); + if (cal_block->cal_info == NULL) { + pr_err("%s: could not allocats cal_info!\n", + __func__); + goto err; + } + cal_block->buffer_number = basic_cal->cal_hdr.buffer_number; + pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pK!\n", + __func__, cal_type->info.reg.cal_type, + cal_block->buffer_number, + cal_block->map_data.ion_map_handle, + cal_block->map_data.map_size, + &cal_block->cal_data.paddr); +done: + return cal_block; +err: + kfree(cal_block); + cal_block = NULL; + return cal_block; +} + +void cal_utils_clear_cal_block_q6maps(int num_cal_types, + struct cal_type_data **cal_type) +{ + int i = 0; + struct list_head *ptr, *next; + struct cal_block_data *cal_block; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + goto done; + } + + for (; i < num_cal_types; i++) { + if (cal_type[i] == NULL) + continue; + + mutex_lock(&cal_type[i]->lock); + list_for_each_safe(ptr, next, + &cal_type[i]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + cal_block->map_data.q6map_handle = 0; + } + mutex_unlock(&cal_type[i]->lock); + } +done: + return; +} + + + +static int realloc_memory(struct cal_block_data *cal_block) +{ + int ret = 0; + + msm_audio_ion_free(cal_block->map_data.ion_client, + cal_block->map_data.ion_handle); + cal_block->map_data.ion_client = NULL; + cal_block->map_data.ion_handle = NULL; + cal_block->cal_data.size = 0; + + ret = cal_block_ion_alloc(cal_block); + if (ret < 0) + pr_err("%s: realloc_memory failed!\n", + __func__); + return ret; +} + +static int map_memory(struct cal_type_data *cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + + + if (cal_type->info.cal_util_callbacks.map_cal != NULL) { + if ((cal_block->map_data.ion_map_handle < 0) || + (cal_block->map_data.map_size <= 0) || + (cal_block->map_data.q6map_handle != 0)) { + goto done; + } + + pr_debug("%s: cal type %d call map\n", + __func__, cal_type->info.reg.cal_type); + ret = cal_type->info.cal_util_callbacks. + map_cal(cal_type->info.reg.cal_type, cal_block); + if (ret < 0) { + pr_err("%s: map_cal failed, cal type %d, ret = %d!\n", + __func__, cal_type->info.reg.cal_type, + ret); + goto done; + } + } +done: + return ret; +} + +static int unmap_memory(struct cal_type_data *cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + + if (cal_type->info.cal_util_callbacks.unmap_cal != NULL) { + if ((cal_block->map_data.ion_map_handle < 0) || + (cal_block->map_data.map_size <= 0) || + (cal_block->map_data.q6map_handle == 0)) { + goto done; + } + pr_debug("%s: cal type %d call unmap\n", + __func__, cal_type->info.reg.cal_type); + ret = cal_type->info.cal_util_callbacks. + unmap_cal(cal_type->info.reg.cal_type, cal_block); + if (ret < 0) { + pr_err("%s: unmap_cal failed, cal type %d, ret = %d!\n", + __func__, cal_type->info.reg.cal_type, + ret); + goto done; + } + } +done: + return ret; +} + +int cal_utils_alloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info) +{ + int ret = 0; + struct cal_block_data *cal_block; + struct audio_cal_type_alloc *alloc_data = data; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", + __func__); + ret = -EINVAL; + goto done; + } + if (data_size < sizeof(struct audio_cal_type_alloc)) { + pr_err("%s: data_size of %zd does not equal alloc struct size of %zd!\n", + __func__, data_size, + sizeof(struct audio_cal_type_alloc)); + ret = -EINVAL; + goto done; + } + if ((client_info_size > 0) && (client_info == NULL)) { + pr_err("%s: User info pointer is NULL but size is %zd!\n", + __func__, client_info_size); + ret = -EINVAL; + goto done; + } + + if (alloc_data->cal_data.mem_handle < 0) { + pr_err("%s: mem_handle %d invalid!\n", + __func__, alloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto done; + } + + mutex_lock(&cal_type->lock); + + cal_block = get_matching_cal_block(cal_type, + data); + if (cal_block != NULL) { + ret = unmap_memory(cal_type, cal_block); + if (ret < 0) + goto err; + ret = realloc_memory(cal_block); + if (ret < 0) + goto err; + } else { + cal_block = create_cal_block(cal_type, + (struct audio_cal_type_basic *)alloc_data, + client_info_size, client_info); + if (cal_block == NULL) { + pr_err("%s: create_cal_block failed for %d!\n", + __func__, alloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto err; + } + } + + ret = map_memory(cal_type, cal_block); + if (ret < 0) + goto err; +err: + mutex_unlock(&cal_type->lock); +done: + return ret; +} + +int cal_utils_dealloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type) +{ + int ret = 0; + struct cal_block_data *cal_block; + struct audio_cal_type_dealloc *dealloc_data = data; + + pr_debug("%s\n", __func__); + + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (data_size < sizeof(struct audio_cal_type_dealloc)) { + pr_err("%s: data_size of %zd does not equal struct size of %zd!\n", + __func__, data_size, + sizeof(struct audio_cal_type_dealloc)); + ret = -EINVAL; + goto done; + } + + if ((dealloc_data->cal_data.mem_handle == -1) && + (dealloc_data->cal_hdr.buffer_number == ALL_CAL_BLOCKS)) { + destroy_all_cal_blocks(cal_type); + goto done; + } + + if (dealloc_data->cal_data.mem_handle < 0) { + pr_err("%s: mem_handle %d invalid!\n", + __func__, dealloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto done; + } + + mutex_lock(&cal_type->lock); + cal_block = get_matching_cal_block( + cal_type, + data); + if (cal_block == NULL) { + pr_err("%s: allocation does not exist for %d!\n", + __func__, dealloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto err; + } + + ret = unmap_memory(cal_type, cal_block); + if (ret < 0) + goto err; + + delete_cal_block(cal_block); +err: + mutex_unlock(&cal_type->lock); +done: + return ret; +} + +int cal_utils_set_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info) +{ + int ret = 0; + struct cal_block_data *cal_block; + struct audio_cal_type_basic *basic_data = data; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", + __func__); + ret = -EINVAL; + goto done; + } + + if ((client_info_size > 0) && (client_info == NULL)) { + pr_err("%s: User info pointer is NULL but size is %zd!\n", + __func__, client_info_size); + ret = -EINVAL; + goto done; + } + + if ((data_size > get_user_cal_type_size( + cal_type->info.reg.cal_type)) || (data_size < 0)) { + pr_err("%s: cal_type %d, data_size of %zd is invalid, expecting %zd!\n", + __func__, cal_type->info.reg.cal_type, data_size, + get_user_cal_type_size(cal_type->info.reg.cal_type)); + ret = -EINVAL; + goto done; + } + + mutex_lock(&cal_type->lock); + cal_block = get_matching_cal_block( + cal_type, + data); + if (cal_block == NULL) { + if (basic_data->cal_data.mem_handle > 0) { + pr_err("%s: allocation does not exist for %d!\n", + __func__, basic_data->cal_data.mem_handle); + ret = -EINVAL; + goto err; + } else { + cal_block = create_cal_block( + cal_type, + basic_data, + client_info_size, client_info); + if (cal_block == NULL) { + pr_err("%s: create_cal_block failed for cal type %d!\n", + __func__, + cal_type->info.reg.cal_type); + ret = -EINVAL; + goto err; + } + } + } + + ret = map_memory(cal_type, cal_block); + if (ret < 0) + goto err; + + cal_block->cal_data.size = basic_data->cal_data.cal_size; + + if (client_info_size > 0) { + memcpy(cal_block->client_info, + client_info, + client_info_size); + } + + memcpy(cal_block->cal_info, + ((uint8_t *)data + sizeof(struct audio_cal_type_basic)), + data_size - sizeof(struct audio_cal_type_basic)); + +err: + mutex_unlock(&cal_type->lock); +done: + return ret; +} diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c new file mode 100644 index 000000000000..808a0e4b72d1 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/audio_calibration.c @@ -0,0 +1,636 @@ +/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct audio_cal_client_info { + struct list_head list; + struct audio_cal_callbacks *callbacks; +}; + +struct audio_cal_info { + struct mutex common_lock; + struct mutex cal_mutex[MAX_CAL_TYPES]; + struct list_head client_info[MAX_CAL_TYPES]; + int ref_count; +}; + +static struct audio_cal_info audio_cal; + + +static bool callbacks_are_equal(struct audio_cal_callbacks *callback1, + struct audio_cal_callbacks *callback2) +{ + bool ret = true; + struct audio_cal_callbacks *call1 = callback1; + struct audio_cal_callbacks *call2 = callback2; + + pr_debug("%s\n", __func__); + + if ((call1 == NULL) && (call2 == NULL)) + ret = true; + else if ((call1 == NULL) || (call2 == NULL)) + ret = false; + else if ((call1->alloc != call2->alloc) || + (call1->dealloc != call2->dealloc) || + (call1->pre_cal != call2->pre_cal) || + (call1->set_cal != call2->set_cal) || + (call1->get_cal != call2->get_cal) || + (call1->post_cal != call2->post_cal)) + ret = false; + return ret; +} + +int audio_cal_deregister(int num_cal_types, + struct audio_cal_reg *reg_data) +{ + int ret = 0; + int i = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s\n", __func__); + + if (reg_data == NULL) { + pr_err("%s: reg_data is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + ret = -EINVAL; + goto done; + } + + for (; i < num_cal_types; i++) { + if ((reg_data[i].cal_type < 0) || + (reg_data[i].cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d at index %d is Invalid!\n", + __func__, reg_data[i].cal_type, i); + ret = -EINVAL; + continue; + } + + mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + list_for_each_safe(ptr, next, + &audio_cal.client_info[reg_data[i].cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + if (callbacks_are_equal(client_info_node->callbacks, + ®_data[i].callbacks)) { + list_del(&client_info_node->list); + kfree(client_info_node->callbacks); + client_info_node->callbacks = NULL; + kfree(client_info_node); + client_info_node = NULL; + break; + } + } + mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + } +done: + return ret; +} + + +int audio_cal_register(int num_cal_types, + struct audio_cal_reg *reg_data) +{ + int ret = 0; + int i = 0; + struct audio_cal_client_info *client_info_node = NULL; + struct audio_cal_callbacks *callback_node = NULL; + + pr_debug("%s\n", __func__); + + if (reg_data == NULL) { + pr_err("%s: callbacks are NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + ret = -EINVAL; + goto done; + } + + for (; i < num_cal_types; i++) { + if ((reg_data[i].cal_type < 0) || + (reg_data[i].cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d at index %d is Invalid!\n", + __func__, reg_data[i].cal_type, i); + ret = -EINVAL; + goto err; + } + + client_info_node = kmalloc(sizeof(*client_info_node), + GFP_KERNEL); + if (client_info_node == NULL) { + ret = -ENOMEM; + goto err; + } + INIT_LIST_HEAD(&client_info_node->list); + + callback_node = kmalloc(sizeof(*callback_node), + GFP_KERNEL); + if (callback_node == NULL) { + ret = -ENOMEM; + goto err; + } + + memcpy(callback_node, ®_data[i].callbacks, + sizeof(*callback_node)); + client_info_node->callbacks = callback_node; + + mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + list_add_tail(&client_info_node->list, + &audio_cal.client_info[reg_data[i].cal_type]); + mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + } +done: + return ret; +err: + audio_cal_deregister(num_cal_types, reg_data); + return ret; +} + +static int call_allocs(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->alloc == NULL) + continue; + + ret2 = client_info_node->callbacks-> + alloc(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: alloc failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_deallocs(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->dealloc == NULL) + continue; + + ret2 = client_info_node->callbacks-> + dealloc(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: dealloc failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_pre_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->pre_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + pre_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: pre_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_post_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->post_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + post_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: post_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_set_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->set_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + set_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: set_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_get_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->get_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + get_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: get_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int audio_cal_open(struct inode *inode, struct file *f) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&audio_cal.common_lock); + audio_cal.ref_count++; + mutex_unlock(&audio_cal.common_lock); + + return ret; +} + +static void dealloc_all_clients(void) +{ + int i = 0; + struct audio_cal_type_dealloc dealloc_data; + + pr_debug("%s\n", __func__); + + dealloc_data.cal_hdr.version = VERSION_0_0; + dealloc_data.cal_hdr.buffer_number = ALL_CAL_BLOCKS; + dealloc_data.cal_data.mem_handle = -1; + + for (; i < MAX_CAL_TYPES; i++) + call_deallocs(i, sizeof(dealloc_data), &dealloc_data); +} + +static int audio_cal_release(struct inode *inode, struct file *f) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&audio_cal.common_lock); + audio_cal.ref_count--; + if (audio_cal.ref_count <= 0) { + audio_cal.ref_count = 0; + dealloc_all_clients(); + } + mutex_unlock(&audio_cal.common_lock); + + return ret; +} + +static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd, + void __user *arg) +{ + int ret = 0; + int32_t size; + struct audio_cal_basic *data = NULL; + + pr_debug("%s\n", __func__); + + switch (cmd) { + case AUDIO_ALLOCATE_CALIBRATION: + case AUDIO_DEALLOCATE_CALIBRATION: + case AUDIO_PREPARE_CALIBRATION: + case AUDIO_SET_CALIBRATION: + case AUDIO_GET_CALIBRATION: + case AUDIO_POST_CALIBRATION: + break; + default: + pr_err("%s: ioctl not found!\n", __func__); + ret = -EFAULT; + goto done; + } + + if (copy_from_user(&size, (void *)arg, sizeof(size))) { + pr_err("%s: Could not copy size value from user\n", __func__); + ret = -EFAULT; + goto done; + } else if ((size < sizeof(struct audio_cal_basic)) + || (size > MAX_IOCTL_CMD_SIZE)) { + pr_err("%s: Invalid size sent to driver: %d, max size is %d, min size is %zd\n", + __func__, size, MAX_IOCTL_CMD_SIZE, + sizeof(struct audio_cal_basic)); + ret = -EINVAL; + goto done; + } + + data = kmalloc(size, GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + goto done; + } else if (copy_from_user(data, (void *)arg, size)) { + pr_err("%s: Could not copy data from user\n", + __func__); + ret = -EFAULT; + goto done; + } else if ((data->hdr.cal_type < 0) || + (data->hdr.cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d is Invalid!\n", + __func__, data->hdr.cal_type); + ret = -EINVAL; + goto done; + } else if ((data->hdr.cal_type_size < + sizeof(struct audio_cal_type_basic)) || + (data->hdr.cal_type_size > + get_user_cal_type_size(data->hdr.cal_type))) { + pr_err("%s: cal type size %d is Invalid! Max is %zd!\n", + __func__, data->hdr.cal_type_size, + get_user_cal_type_size(data->hdr.cal_type)); + ret = -EINVAL; + goto done; + } else if (data->cal_type.cal_hdr.buffer_number < 0) { + pr_err("%s: cal type %d Invalid buffer number %d!\n", + __func__, data->hdr.cal_type, + data->cal_type.cal_hdr.buffer_number); + ret = -EINVAL; + goto done; + } + + + mutex_lock(&audio_cal.cal_mutex[data->hdr.cal_type]); + + switch (cmd) { + case AUDIO_ALLOCATE_CALIBRATION: + ret = call_allocs(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_DEALLOCATE_CALIBRATION: + ret = call_deallocs(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_PREPARE_CALIBRATION: + ret = call_pre_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_SET_CALIBRATION: + ret = call_set_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_GET_CALIBRATION: + ret = call_get_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_POST_CALIBRATION: + ret = call_post_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + } + + if (cmd == AUDIO_GET_CALIBRATION) { + if (data->hdr.cal_type_size == 0) + goto unlock; + if (data == NULL) + goto unlock; + if ((sizeof(data->hdr) + data->hdr.cal_type_size) > size) { + pr_err("%s: header size %zd plus cal type size %d are greater than data buffer size %d\n", + __func__, sizeof(data->hdr), + data->hdr.cal_type_size, size); + ret = -EFAULT; + goto unlock; + } else if (copy_to_user((void *)arg, data, + sizeof(data->hdr) + data->hdr.cal_type_size)) { + pr_err("%s: Could not copy cal type to user\n", + __func__); + ret = -EFAULT; + goto unlock; + } + } + +unlock: + mutex_unlock(&audio_cal.cal_mutex[data->hdr.cal_type]); +done: + kfree(data); + return ret; +} + +static long audio_cal_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + return audio_cal_shared_ioctl(f, cmd, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT + +#define AUDIO_ALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 200, compat_uptr_t) +#define AUDIO_DEALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 201, compat_uptr_t) +#define AUDIO_PREPARE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 202, compat_uptr_t) +#define AUDIO_SET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 203, compat_uptr_t) +#define AUDIO_GET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 204, compat_uptr_t) +#define AUDIO_POST_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 205, compat_uptr_t) + +static long audio_cal_compat_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + unsigned int cmd64; + int ret = 0; + + switch (cmd) { + case AUDIO_ALLOCATE_CALIBRATION32: + cmd64 = AUDIO_ALLOCATE_CALIBRATION; + break; + case AUDIO_DEALLOCATE_CALIBRATION32: + cmd64 = AUDIO_DEALLOCATE_CALIBRATION; + break; + case AUDIO_PREPARE_CALIBRATION32: + cmd64 = AUDIO_PREPARE_CALIBRATION; + break; + case AUDIO_SET_CALIBRATION32: + cmd64 = AUDIO_SET_CALIBRATION; + break; + case AUDIO_GET_CALIBRATION32: + cmd64 = AUDIO_GET_CALIBRATION; + break; + case AUDIO_POST_CALIBRATION32: + cmd64 = AUDIO_POST_CALIBRATION; + break; + default: + pr_err("%s: ioctl not found!\n", __func__); + ret = -EFAULT; + goto done; + } + + ret = audio_cal_shared_ioctl(f, cmd64, compat_ptr(arg)); +done: + return ret; +} +#endif + +static const struct file_operations audio_cal_fops = { + .owner = THIS_MODULE, + .open = audio_cal_open, + .release = audio_cal_release, + .unlocked_ioctl = audio_cal_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_cal_compat_ioctl, +#endif +}; + +struct miscdevice audio_cal_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_cal", + .fops = &audio_cal_fops, +}; + +static int __init audio_cal_init(void) +{ + int i = 0; + + pr_debug("%s\n", __func__); + + memset(&audio_cal, 0, sizeof(audio_cal)); + mutex_init(&audio_cal.common_lock); + for (; i < MAX_CAL_TYPES; i++) { + INIT_LIST_HEAD(&audio_cal.client_info[i]); + mutex_init(&audio_cal.cal_mutex[i]); + } + + return misc_register(&audio_cal_misc); +} + +static void __exit audio_cal_exit(void) +{ + int i = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node; + + for (; i < MAX_CAL_TYPES; i++) { + list_for_each_safe(ptr, next, + &audio_cal.client_info[i]) { + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + list_del(&client_info_node->list); + kfree(client_info_node->callbacks); + client_info_node->callbacks = NULL; + kfree(client_info_node); + client_info_node = NULL; + } + } +} + +subsys_initcall(audio_cal_init); +module_exit(audio_cal_exit); + +MODULE_DESCRIPTION("SoC QDSP6v2 Audio Calibration driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/audio_slimslave.c b/sound/soc/msm/qdsp6v2/audio_slimslave.c new file mode 100644 index 000000000000..e9ecfd5b2e44 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/audio_slimslave.c @@ -0,0 +1,177 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct slim_device *slim; +static int vote_count; +struct mutex suspend_lock; +bool suspend; + +static int audio_slim_open(struct inode *inode, struct file *file) +{ + pr_debug("%s:\n", __func__); + + if (vote_count) { + pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count); + pm_runtime_mark_last_busy(slim->dev.parent); + pm_runtime_put(slim->dev.parent); + vote_count--; + } + return 0; +}; + +static int audio_slim_release(struct inode *inode, struct file *file) +{ + pr_debug("%s:\n", __func__); + + if (vote_count) { + pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count); + pm_runtime_mark_last_busy(slim->dev.parent); + pm_runtime_put(slim->dev.parent); + vote_count--; + } else { + pr_debug("%s: vote: vote_count=%d\n", __func__, vote_count); + pm_runtime_get_sync(slim->dev.parent); + vote_count++; + } + return 0; +}; + +static long audio_slim_ioctl(struct file *file, unsigned int cmd, + unsigned long u_arg) +{ + switch (cmd) { + case AUDIO_SLIMSLAVE_VOTE: + mutex_lock(&suspend_lock); + if (!vote_count && !suspend) { + pr_debug("%s:AUDIO_SLIMSLAVE_VOTE\n", __func__); + pm_runtime_get_sync(slim->dev.parent); + vote_count++; + } else { + pr_err("%s:Invalid vote: vote_count=%d suspend=%d\n", + __func__, vote_count, suspend); + } + mutex_unlock(&suspend_lock); + break; + case AUDIO_SLIMSLAVE_UNVOTE: + mutex_lock(&suspend_lock); + if (vote_count && !suspend) { + pr_debug("%s:AUDIO_SLIMSLAVE_UNVOTE\n", __func__); + pm_runtime_mark_last_busy(slim->dev.parent); + pm_runtime_put(slim->dev.parent); + vote_count--; + } else { + pr_err("%s:Invalid unvote: vote_count=%d suspend=%d\n", + __func__, vote_count, suspend); + } + mutex_unlock(&suspend_lock); + break; + default: + pr_debug("%s: Invalid ioctl cmd: %d\n", __func__, cmd); + break; + } + return 0; +} + +static const struct file_operations audio_slimslave_fops = { + .open = audio_slim_open, + .unlocked_ioctl = audio_slim_ioctl, + .release = audio_slim_release, +}; + +struct miscdevice audio_slimslave_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AUDIO_SLIMSLAVE_IOCTL_NAME, + .fops = &audio_slimslave_fops, +}; + +static int audio_slimslave_probe(struct slim_device *audio_slim) +{ + pr_debug("%s:\n", __func__); + + mutex_init(&suspend_lock); + suspend = false; + slim = audio_slim; + misc_register(&audio_slimslave_misc); + return 0; +} + +static int audio_slimslave_remove(struct slim_device *audio_slim) +{ + pr_debug("%s:\n", __func__); + + misc_deregister(&audio_slimslave_misc); + return 0; +} + +static int audio_slimslave_resume(struct slim_device *audio_slim) +{ + pr_debug("%s:\n", __func__); + + mutex_lock(&suspend_lock); + suspend = false; + mutex_unlock(&suspend_lock); + return 0; +} + +static int audio_slimslave_suspend(struct slim_device *audio_slim, + pm_message_t pmesg) +{ + pr_debug("%s:\n", __func__); + + mutex_lock(&suspend_lock); + suspend = true; + mutex_unlock(&suspend_lock); + return 0; +} + +static const struct slim_device_id audio_slimslave_dt_match[] = { + {"audio-slimslave", 0}, + {} +}; + +static struct slim_driver audio_slimslave_driver = { + .driver = { + .name = "audio-slimslave", + .owner = THIS_MODULE, + }, + .probe = audio_slimslave_probe, + .remove = audio_slimslave_remove, + .id_table = audio_slimslave_dt_match, + .resume = audio_slimslave_resume, + .suspend = audio_slimslave_suspend, +}; + +static int __init audio_slimslave_init(void) +{ + return slim_driver_register(&audio_slimslave_driver); +} +module_init(audio_slimslave_init); + +static void __exit audio_slimslave_exit(void) +{ + +} +module_exit(audio_slimslave_exit); + +/* Module information */ +MODULE_DESCRIPTION("Audio side Slimbus slave driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c new file mode 100644 index 000000000000..225f9781b60b --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c @@ -0,0 +1,1399 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ENABLE_CMD_SIZE 32 + +#define GET_NEXT(ptr, upper_limit, rc) \ +({ \ + if (((ptr) + 1) > (upper_limit)) { \ + pr_err("%s: param list out of boundary\n", __func__); \ + (rc) = -EINVAL; \ + } \ + ((rc) == 0) ? *(ptr)++ : -EINVAL; \ +}) + +#define CHECK_PARAM_LEN(len, max_len, tag, rc) \ +do { \ + if ((len) > (max_len)) { \ + pr_err("%s: params length overflows\n", (tag)); \ + (rc) = -EINVAL; \ + } \ +} while (0) + + +bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module, + int topology) +{ + switch (effect_module) { + case VIRTUALIZER_MODULE: + case REVERB_MODULE: + case BASS_BOOST_MODULE: + case PBE_MODULE: + case EQ_MODULE: + switch (topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS: + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: + return true; + default: + return false; + } + case DTS_EAGLE_MODULE: + switch (topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX: + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: + return true; + default: + return false; + } + case SOFT_VOLUME2_MODULE: + case DTS_EAGLE_MODULE_ENABLE: + switch (topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: + return true; + default: + return false; + } + default: + return false; + } +} + +int msm_audio_effects_enable_extn(struct audio_client *ac, + struct msm_nt_eff_all_config *effects, + bool flag) +{ + uint32_t updt_params[MAX_ENABLE_CMD_SIZE] = {0}; + uint32_t params_length; + int rc = 0; + + pr_debug("%s\n", __func__); + if (!ac) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params_length = 0; + updt_params[0] = AUDPROC_MODULE_ID_VIRTUALIZER; + updt_params[1] = AUDPROC_PARAM_ID_ENABLE; + updt_params[2] = VIRTUALIZER_ENABLE_PARAM_SZ; + updt_params[3] = flag; + params_length += COMMAND_PAYLOAD_SZ + VIRTUALIZER_ENABLE_PARAM_SZ; + if (effects->virtualizer.enable_flag) + q6asm_send_audio_effects_params(ac, (char *)&updt_params[0], + params_length); + memset(updt_params, 0, MAX_ENABLE_CMD_SIZE); + params_length = 0; + updt_params[0] = AUDPROC_MODULE_ID_BASS_BOOST; + updt_params[1] = AUDPROC_PARAM_ID_ENABLE; + updt_params[2] = BASS_BOOST_ENABLE_PARAM_SZ; + updt_params[3] = flag; + params_length += COMMAND_PAYLOAD_SZ + BASS_BOOST_ENABLE_PARAM_SZ; + if (effects->bass_boost.enable_flag) + q6asm_send_audio_effects_params(ac, (char *)&updt_params[0], + params_length); + memset(updt_params, 0, MAX_ENABLE_CMD_SIZE); + params_length = 0; + updt_params[0] = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + updt_params[1] = AUDPROC_PARAM_ID_ENABLE; + updt_params[2] = EQ_ENABLE_PARAM_SZ; + updt_params[3] = flag; + params_length += COMMAND_PAYLOAD_SZ + EQ_ENABLE_PARAM_SZ; + if (effects->equalizer.enable_flag) + q6asm_send_audio_effects_params(ac, (char *)&updt_params[0], + params_length); + return rc; +} + +int msm_audio_effects_virtualizer_handler(struct audio_client *ac, + struct virtualizer_params *virtualizer, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int *updt_params, i, prev_enable_flag; + uint32_t params_length = (MAX_INBAND_PARAM_SZ); + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(params_length, GFP_KERNEL); + if (!params) + return -ENOMEM; + + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (int *)params; + params_length = 0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case VIRTUALIZER_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("VIRT ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = virtualizer->enable_flag; + virtualizer->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s:VIRT ENABLE prev:%d, new:%d\n", __func__, + prev_enable_flag, virtualizer->enable_flag); + if (prev_enable_flag != virtualizer->enable_flag) { + params_length += COMMAND_PAYLOAD_SZ + + VIRTUALIZER_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "VIRT ENABLE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_VIRTUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE; + *updt_params++ = + VIRTUALIZER_ENABLE_PARAM_SZ; + *updt_params++ = + virtualizer->enable_flag; + } + break; + case VIRTUALIZER_STRENGTH: + if (length != 1 || index_offset != 0) { + pr_err("VIRT STRENGTH:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + virtualizer->strength = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: VIRT STRENGTH val: %d\n", + __func__, virtualizer->strength); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + VIRTUALIZER_STRENGTH_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "VIRT STRENGTH", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_VIRTUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH; + *updt_params++ = + VIRTUALIZER_STRENGTH_PARAM_SZ; + *updt_params++ = + virtualizer->strength; + } + break; + case VIRTUALIZER_OUT_TYPE: + if (length != 1 || index_offset != 0) { + pr_err("VIRT OUT_TYPE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + virtualizer->out_type = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: VIRT OUT_TYPE val:%d\n", + __func__, virtualizer->out_type); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + VIRTUALIZER_OUT_TYPE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "VIRT OUT_TYPE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_VIRTUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE; + *updt_params++ = + VIRTUALIZER_OUT_TYPE_PARAM_SZ; + *updt_params++ = + virtualizer->out_type; + } + break; + case VIRTUALIZER_GAIN_ADJUST: + if (length != 1 || index_offset != 0) { + pr_err("VIRT GAIN_ADJUST: invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + virtualizer->gain_adjust = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: VIRT GAIN_ADJUST val:%d\n", + __func__, virtualizer->gain_adjust); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + VIRTUALIZER_GAIN_ADJUST_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "VIRT GAIN_ADJUST", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_VIRTUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST; + *updt_params++ = + VIRTUALIZER_GAIN_ADJUST_PARAM_SZ; + *updt_params++ = + virtualizer->gain_adjust; + } + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + break; + } + } + if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + q6asm_send_audio_effects_params(ac, params, + params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} + +int msm_audio_effects_reverb_handler(struct audio_client *ac, + struct reverb_params *reverb, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int *updt_params, i, prev_enable_flag; + uint32_t params_length = (MAX_INBAND_PARAM_SZ); + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(params_length, GFP_KERNEL); + if (!params) + return -ENOMEM; + + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (int *)params; + params_length = 0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case REVERB_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = reverb->enable_flag; + reverb->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s:REVERB_ENABLE prev:%d,new:%d\n", __func__, + prev_enable_flag, reverb->enable_flag); + if (prev_enable_flag != reverb->enable_flag) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_ENABLE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_ENABLE; + *updt_params++ = + REVERB_ENABLE_PARAM_SZ; + *updt_params++ = + reverb->enable_flag; + } + break; + case REVERB_MODE: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_MODE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->mode = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_MODE val:%d\n", + __func__, reverb->mode); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_MODE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_MODE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_MODE; + *updt_params++ = + REVERB_MODE_PARAM_SZ; + *updt_params++ = + reverb->mode; + } + break; + case REVERB_PRESET: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_PRESET:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->preset = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_PRESET val:%d\n", + __func__, reverb->preset); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_PRESET_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_PRESET", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_PRESET; + *updt_params++ = + REVERB_PRESET_PARAM_SZ; + *updt_params++ = + reverb->preset; + } + break; + case REVERB_WET_MIX: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_WET_MIX:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->wet_mix = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_WET_MIX val:%d\n", + __func__, reverb->wet_mix); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_WET_MIX_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_WET_MIX", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_WET_MIX; + *updt_params++ = + REVERB_WET_MIX_PARAM_SZ; + *updt_params++ = + reverb->wet_mix; + } + break; + case REVERB_GAIN_ADJUST: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_GAIN_ADJUST:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->gain_adjust = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_GAIN_ADJUST val:%d\n", + __func__, reverb->gain_adjust); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_GAIN_ADJUST_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_GAIN_ADJUST", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST; + *updt_params++ = + REVERB_GAIN_ADJUST_PARAM_SZ; + *updt_params++ = + reverb->gain_adjust; + } + break; + case REVERB_ROOM_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_ROOM_LEVEL:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->room_level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_ROOM_LEVEL val:%d\n", + __func__, reverb->room_level); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_ROOM_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_ROOM_LEVEL", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL; + *updt_params++ = + REVERB_ROOM_LEVEL_PARAM_SZ; + *updt_params++ = + reverb->room_level; + } + break; + case REVERB_ROOM_HF_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_ROOM_HF_LEVEL:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->room_hf_level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_ROOM_HF_LEVEL val%d\n", + __func__, reverb->room_hf_level); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_ROOM_HF_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_ROOM_HF_LEVEL", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL; + *updt_params++ = + REVERB_ROOM_HF_LEVEL_PARAM_SZ; + *updt_params++ = + reverb->room_hf_level; + } + break; + case REVERB_DECAY_TIME: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DECAY_TIME:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->decay_time = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DECAY_TIME val:%d\n", + __func__, reverb->decay_time); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_DECAY_TIME_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_DECAY_TIME", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_DECAY_TIME; + *updt_params++ = + REVERB_DECAY_TIME_PARAM_SZ; + *updt_params++ = + reverb->decay_time; + } + break; + case REVERB_DECAY_HF_RATIO: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DECAY_HF_RATIOinvalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->decay_hf_ratio = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DECAY_HF_RATIO val%d\n", + __func__, reverb->decay_hf_ratio); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_DECAY_HF_RATIO_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_DECAY_HF_RATIO", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO; + *updt_params++ = + REVERB_DECAY_HF_RATIO_PARAM_SZ; + *updt_params++ = + reverb->decay_hf_ratio; + } + break; + case REVERB_REFLECTIONS_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_REFLECTION_LVLinvalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->reflections_level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_REFLECTIONS_LEVEL val:%d\n", + __func__, reverb->reflections_level); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_REFLECTIONS_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_REFLECTIONS_LEVEL", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL; + *updt_params++ = + REVERB_REFLECTIONS_LEVEL_PARAM_SZ; + *updt_params++ = + reverb->reflections_level; + } + break; + case REVERB_REFLECTIONS_DELAY: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_REFLECTION_DLYinvalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->reflections_delay = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_REFLECTIONS_DELAY val:%d\n", + __func__, reverb->reflections_delay); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_REFLECTIONS_DELAY_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_REFLECTIONS_DELAY", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY; + *updt_params++ = + REVERB_REFLECTIONS_DELAY_PARAM_SZ; + *updt_params++ = + reverb->reflections_delay; + } + break; + case REVERB_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_LEVEL:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_LEVEL val:%d\n", + __func__, reverb->level); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_LEVEL", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_LEVEL; + *updt_params++ = + REVERB_LEVEL_PARAM_SZ; + *updt_params++ = + reverb->level; + } + break; + case REVERB_DELAY: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DELAY:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->delay = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s:REVERB_DELAY val:%d\n", + __func__, reverb->delay); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_DELAY_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_DELAY", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_DELAY; + *updt_params++ = + REVERB_DELAY_PARAM_SZ; + *updt_params++ = + reverb->delay; + } + break; + case REVERB_DIFFUSION: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DIFFUSION:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->diffusion = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DIFFUSION val:%d\n", + __func__, reverb->diffusion); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_DIFFUSION_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_DIFFUSION", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_DIFFUSION; + *updt_params++ = + REVERB_DIFFUSION_PARAM_SZ; + *updt_params++ = + reverb->diffusion; + } + break; + case REVERB_DENSITY: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DENSITY:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->density = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DENSITY val:%d\n", + __func__, reverb->density); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + REVERB_DENSITY_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "REVERB_DENSITY", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_REVERB; + *updt_params++ = + AUDPROC_PARAM_ID_REVERB_DENSITY; + *updt_params++ = + REVERB_DENSITY_PARAM_SZ; + *updt_params++ = + reverb->density; + } + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + break; + } + } + if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + q6asm_send_audio_effects_params(ac, params, + params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} + +int msm_audio_effects_bass_boost_handler(struct audio_client *ac, + struct bass_boost_params *bass_boost, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int *updt_params, i, prev_enable_flag; + uint32_t params_length = (MAX_INBAND_PARAM_SZ); + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(params_length, GFP_KERNEL); + if (!params) + return -ENOMEM; + + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (int *)params; + params_length = 0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case BASS_BOOST_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("BASS_BOOST_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = bass_boost->enable_flag; + bass_boost->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: BASS_BOOST_ENABLE prev:%d new:%d\n", + __func__, prev_enable_flag, + bass_boost->enable_flag); + if (prev_enable_flag != bass_boost->enable_flag) { + params_length += COMMAND_PAYLOAD_SZ + + BASS_BOOST_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "BASS_BOOST_ENABLE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_BASS_BOOST; + *updt_params++ = + AUDPROC_PARAM_ID_BASS_BOOST_ENABLE; + *updt_params++ = + BASS_BOOST_ENABLE_PARAM_SZ; + *updt_params++ = + bass_boost->enable_flag; + } + break; + case BASS_BOOST_MODE: + if (length != 1 || index_offset != 0) { + pr_err("BASS_BOOST_MODE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + bass_boost->mode = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: BASS_BOOST_MODE val:%d\n", + __func__, bass_boost->mode); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + BASS_BOOST_MODE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "BASS_BOOST_MODE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_BASS_BOOST; + *updt_params++ = + AUDPROC_PARAM_ID_BASS_BOOST_MODE; + *updt_params++ = + BASS_BOOST_MODE_PARAM_SZ; + *updt_params++ = + bass_boost->mode; + } + break; + case BASS_BOOST_STRENGTH: + if (length != 1 || index_offset != 0) { + pr_err("BASS_BOOST_STRENGTH:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + bass_boost->strength = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: BASS_BOOST_STRENGTH val:%d\n", + __func__, bass_boost->strength); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + BASS_BOOST_STRENGTH_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "BASS_BOOST_STRENGTH", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_BASS_BOOST; + *updt_params++ = + AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH; + *updt_params++ = + BASS_BOOST_STRENGTH_PARAM_SZ; + *updt_params++ = + bass_boost->strength; + } + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + break; + } + } + if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + q6asm_send_audio_effects_params(ac, params, + params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} + +int msm_audio_effects_pbe_handler(struct audio_client *ac, + struct pbe_params *pbe, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int *updt_params, i, j, prev_enable_flag; + uint32_t params_length = (MAX_INBAND_PARAM_SZ); + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(params_length, GFP_KERNEL); + if (!params) + return -ENOMEM; + + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (int *)params; + params_length = 0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case PBE_ENABLE: + pr_debug("%s: PBE_ENABLE\n", __func__); + if (length != 1 || index_offset != 0) { + pr_err("no valid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = pbe->enable_flag; + pbe->enable_flag = + GET_NEXT(values, param_max_offset, rc); + if (prev_enable_flag != pbe->enable_flag) { + params_length += COMMAND_PAYLOAD_SZ + + PBE_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "PBE_ENABLE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_PBE; + *updt_params++ = + AUDPROC_PARAM_ID_PBE_ENABLE; + *updt_params++ = + PBE_ENABLE_PARAM_SZ; + *updt_params++ = + pbe->enable_flag; + } + break; + case PBE_CONFIG: + pr_debug("%s: PBE_PARAM length %u\n", __func__, length); + if (length > sizeof(struct pbe_config_t) || + length < PBE_CONFIG_PARAM_LEN || + index_offset != 0) { + pr_err("no valid params, len %d\n", length); + rc = -EINVAL; + goto invalid_config; + } + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + length; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "PBE_PARAM", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_PBE; + *updt_params++ = + AUDPROC_PARAM_ID_PBE_PARAM_CONFIG; + *updt_params++ = + length; + for (j = 0; j < length; ) { + j += sizeof(*updt_params); + *updt_params++ = + GET_NEXT( + values, + param_max_offset, + rc); + } + } + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + break; + } + } + if (params_length && (rc == 0)) + q6asm_send_audio_effects_params(ac, params, + params_length); +invalid_config: + kfree(params); + return rc; +} + +int msm_audio_effects_popless_eq_handler(struct audio_client *ac, + struct eq_params *eq, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int *updt_params, i, prev_enable_flag; + uint32_t params_length = (MAX_INBAND_PARAM_SZ); + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(params_length, GFP_KERNEL); + if (!params) + return -ENOMEM; + + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (int *)params; + params_length = 0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + uint32_t idx; + int j; + + switch (command_id) { + case EQ_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("EQ_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = eq->enable_flag; + eq->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: EQ_ENABLE prev:%d new:%d\n", __func__, + prev_enable_flag, eq->enable_flag); + if (prev_enable_flag != eq->enable_flag) { + params_length += COMMAND_PAYLOAD_SZ + + EQ_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "EQ_ENABLE", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_EQ_ENABLE; + *updt_params++ = + EQ_ENABLE_PARAM_SZ; + *updt_params++ = + eq->enable_flag; + } + break; + case EQ_CONFIG: + if (length < EQ_CONFIG_PARAM_LEN || index_offset != 0) { + pr_err("EQ_CONFIG:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + pr_debug("%s: EQ_CONFIG bands:%d, pgain:%d, pset:%d\n", + __func__, eq->config.num_bands, + eq->config.eq_pregain, eq->config.preset_id); + for (idx = 0; idx < MAX_EQ_BANDS; idx++) + eq->per_band_cfg[idx].band_idx = -1; + eq->config.eq_pregain = + GET_NEXT(values, param_max_offset, rc); + eq->config.preset_id = + GET_NEXT(values, param_max_offset, rc); + eq->config.num_bands = + GET_NEXT(values, param_max_offset, rc); + if (eq->config.num_bands > MAX_EQ_BANDS) { + pr_err("EQ_CONFIG:invalid num of bands\n"); + rc = -EINVAL; + goto invalid_config; + } + if (eq->config.num_bands && + (((length - EQ_CONFIG_PARAM_LEN)/ + EQ_CONFIG_PER_BAND_PARAM_LEN) + != eq->config.num_bands)) { + pr_err("EQ_CONFIG:invalid length per band\n"); + rc = -EINVAL; + goto invalid_config; + } + for (j = 0; j < eq->config.num_bands; j++) { + idx = GET_NEXT(values, param_max_offset, rc); + if (idx >= MAX_EQ_BANDS) { + pr_err("EQ_CONFIG:invalid band index\n"); + rc = -EINVAL; + goto invalid_config; + } + eq->per_band_cfg[idx].band_idx = idx; + eq->per_band_cfg[idx].filter_type = + GET_NEXT(values, param_max_offset, rc); + eq->per_band_cfg[idx].freq_millihertz = + GET_NEXT(values, param_max_offset, rc); + eq->per_band_cfg[idx].gain_millibels = + GET_NEXT(values, param_max_offset, rc); + eq->per_band_cfg[idx].quality_factor = + GET_NEXT(values, param_max_offset, rc); + } + if (command_config_state == CONFIG_SET) { + int config_param_length = EQ_CONFIG_PARAM_SZ + + (EQ_CONFIG_PER_BAND_PARAM_SZ* + eq->config.num_bands); + params_length += COMMAND_PAYLOAD_SZ + + config_param_length; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "EQ_CONFIG", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_EQ_CONFIG; + *updt_params++ = + config_param_length; + *updt_params++ = + eq->config.eq_pregain; + *updt_params++ = + eq->config.preset_id; + *updt_params++ = + eq->config.num_bands; + for (idx = 0; idx < MAX_EQ_BANDS; idx++) { + if (eq->per_band_cfg[idx].band_idx < 0) + continue; + *updt_params++ = + eq->per_band_cfg[idx].filter_type; + *updt_params++ = + eq->per_band_cfg[idx].freq_millihertz; + *updt_params++ = + eq->per_band_cfg[idx].gain_millibels; + *updt_params++ = + eq->per_band_cfg[idx].quality_factor; + *updt_params++ = + eq->per_band_cfg[idx].band_idx; + } + } + break; + case EQ_BAND_INDEX: + if (length != 1 || index_offset != 0) { + pr_err("EQ_BAND_INDEX:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + idx = GET_NEXT(values, param_max_offset, rc); + if (idx > MAX_EQ_BANDS) { + pr_err("EQ_BAND_INDEX:invalid band index\n"); + rc = -EINVAL; + goto invalid_config; + } + eq->band_index = idx; + pr_debug("%s: EQ_BAND_INDEX val:%d\n", + __func__, eq->band_index); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + EQ_BAND_INDEX_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "EQ_BAND_INDEX", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_EQ_BAND_INDEX; + *updt_params++ = + EQ_BAND_INDEX_PARAM_SZ; + *updt_params++ = + eq->band_index; + } + break; + case EQ_SINGLE_BAND_FREQ: + if (length != 1 || index_offset != 0) { + pr_err("EQ_SINGLE_BAND_FREQ:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + if (eq->band_index > MAX_EQ_BANDS) { + pr_err("EQ_SINGLE_BAND_FREQ:invalid index\n"); + break; + } + eq->freq_millihertz = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: EQ_SINGLE_BAND_FREQ idx:%d, val:%d\n", + __func__, eq->band_index, eq->freq_millihertz); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + EQ_SINGLE_BAND_FREQ_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "EQ_SINGLE_BAND_FREQ", rc); + if (rc != 0) + break; + *updt_params++ = + AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + *updt_params++ = + AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ; + *updt_params++ = + EQ_SINGLE_BAND_FREQ_PARAM_SZ; + *updt_params++ = + eq->freq_millihertz; + } + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + break; + } + } + if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + q6asm_send_audio_effects_params(ac, params, + params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} + +static int __msm_audio_effects_volume_handler(struct audio_client *ac, + struct soft_volume_params *vol, + long *values, + int instance) +{ + int devices; + int num_commands; + char *params = NULL; + int *updt_params, i; + uint32_t params_length = (MAX_INBAND_PARAM_SZ); + long *param_max_offset; + int rc = 0; + + pr_debug("%s: instance: %d\n", __func__, instance); + if (!values) { + pr_err("%s: set audio effects failed, no valid data\n", + __func__); + return -EINVAL; + } + param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + devices = GET_NEXT(values, param_max_offset, rc); + num_commands = GET_NEXT(values, param_max_offset, rc); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(params_length, GFP_KERNEL); + if (!params) + return -ENOMEM; + + updt_params = (int *)params; + params_length = 0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case SOFT_VOLUME_GAIN_2CH: + case SOFT_VOLUME2_GAIN_2CH: + if (length != 2 || index_offset != 0) { + pr_err("VOLUME_GAIN_2CH: invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + vol->left_gain = GET_NEXT(values, param_max_offset, rc); + vol->right_gain = + GET_NEXT(values, param_max_offset, rc); + vol->master_gain = 0x2000; + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_2CH_PARAM_SZ; + params_length += COMMAND_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "VOLUME/VOLUME2_GAIN_2CH", + rc); + if (rc != 0) + break; + if (instance == SOFT_VOLUME_INSTANCE_2) + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL2; + else + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL; + *updt_params++ = + ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN; + *updt_params++ = + SOFT_VOLUME_GAIN_2CH_PARAM_SZ; + *updt_params++ = + (vol->left_gain << 16) | + vol->right_gain; + if (instance == SOFT_VOLUME_INSTANCE_2) + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL2; + else + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL; + *updt_params++ = + ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN; + *updt_params++ = + SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + *updt_params++ = + vol->master_gain; + } + break; + case SOFT_VOLUME_GAIN_MASTER: + case SOFT_VOLUME2_GAIN_MASTER: + if (length != 1 || index_offset != 0) { + pr_err("VOLUME_GAIN_MASTER: invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + vol->left_gain = 0x2000; + vol->right_gain = 0x2000; + vol->master_gain = + GET_NEXT(values, param_max_offset, rc); + if (command_config_state == CONFIG_SET) { + params_length += COMMAND_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_2CH_PARAM_SZ; + params_length += COMMAND_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + CHECK_PARAM_LEN(params_length, + MAX_INBAND_PARAM_SZ, + "VOLUME/VOLUME2_GAIN_MASTER", + rc); + if (rc != 0) + break; + if (instance == SOFT_VOLUME_INSTANCE_2) + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL2; + else + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL; + *updt_params++ = + ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN; + *updt_params++ = + SOFT_VOLUME_GAIN_2CH_PARAM_SZ; + *updt_params++ = + (vol->left_gain << 16) | + vol->right_gain; + if (instance == SOFT_VOLUME_INSTANCE_2) + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL2; + else + *updt_params++ = + ASM_MODULE_ID_VOL_CTRL; + *updt_params++ = + ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN; + *updt_params++ = + SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + *updt_params++ = + vol->master_gain; + } + break; + default: + pr_err("%s: Invalid command id: %d to set config\n", + __func__, command_id); + break; + } + } + if (params_length && (rc == 0)) + q6asm_send_audio_effects_params(ac, params, + params_length); +invalid_config: + kfree(params); + return rc; +} + +int msm_audio_effects_volume_handler(struct audio_client *ac, + struct soft_volume_params *vol, + long *values) +{ + return __msm_audio_effects_volume_handler(ac, vol, values, + SOFT_VOLUME_INSTANCE_1); +} + +int msm_audio_effects_volume_handler_v2(struct audio_client *ac, + struct soft_volume_params *vol, + long *values, int instance) +{ + return __msm_audio_effects_volume_handler(ac, vol, values, instance); +} diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c new file mode 100644 index 000000000000..6c78f858570e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c @@ -0,0 +1,1714 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "msm-compr-q6-v2.h" +#include "msm-pcm-routing-v2.h" +#include + +#define COMPRE_CAPTURE_NUM_PERIODS 16 +/* Allocate the worst case frame size for compressed audio */ +#define COMPRE_CAPTURE_HEADER_SIZE (sizeof(struct snd_compr_audio_info)) +/* Changing period size to 4032. 4032 will make sure COMPRE_CAPTURE_PERIOD_SIZE + * is 4096 with meta data size of 64 and MAX_NUM_FRAMES_PER_BUFFER 1 + */ +#define COMPRE_CAPTURE_MAX_FRAME_SIZE (4032) +#define COMPRE_CAPTURE_PERIOD_SIZE ((COMPRE_CAPTURE_MAX_FRAME_SIZE + \ + COMPRE_CAPTURE_HEADER_SIZE) * \ + MAX_NUM_FRAMES_PER_BUFFER) +#define COMPRE_OUTPUT_METADATA_SIZE (sizeof(struct output_meta_data_st)) +#define COMPRESSED_LR_VOL_MAX_STEPS 0x20002000 + +#define MAX_AC3_PARAM_SIZE (18*2*sizeof(int)) +#define AMR_WB_BAND_MODE 8 +#define AMR_WB_DTX_MODE 0 + + +const DECLARE_TLV_DB_LINEAR(compr_rx_vol_gain, 0, + COMPRESSED_LR_VOL_MAX_STEPS); + +static struct audio_locks the_locks; + +static struct snd_pcm_hardware msm_compr_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = + COMPRE_CAPTURE_PERIOD_SIZE * COMPRE_CAPTURE_NUM_PERIODS, + .period_bytes_min = COMPRE_CAPTURE_PERIOD_SIZE, + .period_bytes_max = COMPRE_CAPTURE_PERIOD_SIZE, + .periods_min = COMPRE_CAPTURE_NUM_PERIODS, + .periods_max = COMPRE_CAPTURE_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_compr_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 128 * 1024, + .period_bytes_max = 256 * 1024, + .periods_min = 4, + .periods_max = 8, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +/* Add supported codecs for compress capture path */ +static uint32_t supported_compr_capture_codecs[] = { + SND_AUDIOCODEC_AMRWB +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static bool msm_compr_capture_codecs(uint32_t req_codec) +{ + int i; + + pr_debug("%s req_codec:%d\n", __func__, req_codec); + if (req_codec == 0) + return false; + for (i = 0; i < ARRAY_SIZE(supported_compr_capture_codecs); i++) { + if (req_codec == supported_compr_capture_codecs[i]) + return true; + } + return false; +} + +static void compr_event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct compr_audio *compr = priv; + struct msm_audio *prtd = &compr->prtd; + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_aio_write_param param; + struct audio_aio_read_param read_param; + struct audio_buffer *buf = NULL; + phys_addr_t temp; + struct output_meta_data_st output_meta_data; + uint32_t *ptrmem = (uint32_t *)payload; + int i = 0; + int time_stamp_flag = 0; + int buffer_length = 0; + int stop_playback = 0; + + pr_debug("%s opcode =%08x\n", __func__, opcode); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: { + uint32_t *ptrmem = (uint32_t *)¶m; + + pr_debug("ASM_DATA_EVENT_WRITE_DONE\n"); + pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem); + prtd->pcm_irq_pos += prtd->pcm_count; + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + else + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + atomic_inc(&prtd->out_count); + wake_up(&the_locks.write_wait); + if (!atomic_read(&prtd->start)) { + atomic_set(&prtd->pending_buffer, 1); + break; + } + atomic_set(&prtd->pending_buffer, 0); + + /* + * check for underrun + */ + snd_pcm_stream_lock_irq(substream); + if (runtime->status->hw_ptr >= runtime->control->appl_ptr) { + runtime->render_flag |= SNDRV_RENDER_STOPPED; + stop_playback = 1; + } + snd_pcm_stream_unlock_irq(substream); + + if (stop_playback) { + pr_err("underrun! render stopped\n"); + break; + } + + buf = prtd->audio_client->port[IN].buf; + pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n", + __func__, prtd->pcm_count, prtd->out_head); + temp = buf[0].phys + (prtd->out_head * prtd->pcm_count); + pr_debug("%s:writing buffer[%d] from 0x%pK\n", + __func__, prtd->out_head, &temp); + + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + time_stamp_flag = SET_TIMESTAMP; + else + time_stamp_flag = NO_TIMESTAMP; + memcpy(&output_meta_data, (char *)(buf->data + + prtd->out_head * prtd->pcm_count), + COMPRE_OUTPUT_METADATA_SIZE); + + buffer_length = output_meta_data.frame_size; + pr_debug("meta_data_length: %d, frame_length: %d\n", + output_meta_data.meta_data_length, + output_meta_data.frame_size); + pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n", + output_meta_data.timestamp_msw, + output_meta_data.timestamp_lsw); + if (buffer_length == 0) { + pr_debug("Received a zero length buffer-break out"); + break; + } + param.paddr = temp + output_meta_data.meta_data_length; + param.len = buffer_length; + param.msw_ts = output_meta_data.timestamp_msw; + param.lsw_ts = output_meta_data.timestamp_lsw; + param.flags = time_stamp_flag; + param.uid = prtd->session_id; + for (i = 0; i < sizeof(struct audio_aio_write_param)/4; + i++, ++ptrmem) + pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem); + if (q6asm_async_write(prtd->audio_client, + ¶m) < 0) + pr_err("%s:q6asm_async_write failed\n", + __func__); + else + prtd->out_head = + (prtd->out_head + 1) & (runtime->periods - 1); + break; + } + case ASM_DATA_EVENT_RENDERED_EOS: + pr_debug("ASM_DATA_CMDRSP_EOS\n"); + if (atomic_read(&prtd->eos)) { + pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); + prtd->cmd_ack = 1; + wake_up(&the_locks.eos_wait); + atomic_set(&prtd->eos, 0); + } + break; + case ASM_DATA_EVENT_READ_DONE_V2: { + pr_debug("ASM_DATA_EVENT_READ_DONE\n"); + pr_debug("buf = %pK, data = 0x%X, *data = %pK,\n" + "prtd->pcm_irq_pos = %d\n", + prtd->audio_client->port[OUT].buf, + *(uint32_t *)prtd->audio_client->port[OUT].buf->data, + prtd->audio_client->port[OUT].buf->data, + prtd->pcm_irq_pos); + + memcpy(prtd->audio_client->port[OUT].buf->data + + prtd->pcm_irq_pos, (ptrmem + READDONE_IDX_SIZE), + COMPRE_CAPTURE_HEADER_SIZE); + pr_debug("buf = %pK, updated data = 0x%X, *data = %pK\n", + prtd->audio_client->port[OUT].buf, + *(uint32_t *)(prtd->audio_client->port[OUT].buf->data + + prtd->pcm_irq_pos), + prtd->audio_client->port[OUT].buf->data); + if (!atomic_read(&prtd->start)) + break; + pr_debug("frame size=%d, buffer = 0x%X\n", + ptrmem[READDONE_IDX_SIZE], + ptrmem[READDONE_IDX_BUFADD_LSW]); + if (ptrmem[READDONE_IDX_SIZE] > COMPRE_CAPTURE_MAX_FRAME_SIZE) { + pr_err("Frame length exceeded the max length"); + break; + } + buf = prtd->audio_client->port[OUT].buf; + + pr_debug("pcm_irq_pos=%d, buf[0].phys = 0x%pK\n", + prtd->pcm_irq_pos, &buf[0].phys); + read_param.len = prtd->pcm_count - COMPRE_CAPTURE_HEADER_SIZE; + read_param.paddr = buf[0].phys + + prtd->pcm_irq_pos + COMPRE_CAPTURE_HEADER_SIZE; + prtd->pcm_irq_pos += prtd->pcm_count; + + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + + q6asm_async_read(prtd->audio_client, &read_param); + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: { + if (substream->stream + != SNDRV_PCM_STREAM_PLAYBACK) { + atomic_set(&prtd->start, 1); + break; + } + if (!atomic_read(&prtd->pending_buffer)) + break; + pr_debug("%s: writing %d bytes of buffer[%d] to dsp\n", + __func__, prtd->pcm_count, prtd->out_head); + buf = prtd->audio_client->port[IN].buf; + pr_debug("%s: writing buffer[%d] from 0x%pK head %d count %d\n", + __func__, prtd->out_head, &buf[0].phys, + prtd->pcm_count, prtd->out_head); + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + time_stamp_flag = SET_TIMESTAMP; + else + time_stamp_flag = NO_TIMESTAMP; + memcpy(&output_meta_data, (char *)(buf->data + + prtd->out_head * prtd->pcm_count), + COMPRE_OUTPUT_METADATA_SIZE); + buffer_length = output_meta_data.frame_size; + pr_debug("meta_data_length: %d, frame_length: %d\n", + output_meta_data.meta_data_length, + output_meta_data.frame_size); + pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n", + output_meta_data.timestamp_msw, + output_meta_data.timestamp_lsw); + param.paddr = buf[prtd->out_head].phys + + output_meta_data.meta_data_length; + param.len = buffer_length; + param.msw_ts = output_meta_data.timestamp_msw; + param.lsw_ts = output_meta_data.timestamp_lsw; + param.flags = time_stamp_flag; + param.uid = prtd->session_id; + param.metadata_len = COMPRE_OUTPUT_METADATA_SIZE; + if (q6asm_async_write(prtd->audio_client, + ¶m) < 0) + pr_err("%s:q6asm_async_write failed\n", + __func__); + else + prtd->out_head = + (prtd->out_head + 1) + & (runtime->periods - 1); + atomic_set(&prtd->pending_buffer, 0); + } + break; + case ASM_STREAM_CMD_FLUSH: + pr_debug("ASM_STREAM_CMD_FLUSH\n"); + prtd->cmd_ack = 1; + wake_up(&the_locks.flush_wait); + break; + default: + break; + } + break; + } + default: + pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + break; + } +} + +static int msm_compr_send_ddp_cfg(struct audio_client *ac, + struct snd_dec_ddp *ddp) +{ + int i, rc; + + pr_debug("%s\n", __func__); + + if (ddp->params_length / 2 > SND_DEC_DDP_MAX_PARAMS) { + pr_err("%s: Invalid number of params %u, max allowed %u\n", + __func__, ddp->params_length / 2, + SND_DEC_DDP_MAX_PARAMS); + return -EINVAL; + } + + for (i = 0; i < ddp->params_length/2; i++) { + rc = q6asm_ds1_set_endp_params(ac, ddp->params_id[i], + ddp->params_value[i]); + if (rc) { + pr_err("sending params_id: %d failed\n", + ddp->params_id[i]); + return rc; + } + } + return 0; +} + +static int msm_compr_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = &compr->prtd; + struct snd_pcm_hw_params *params; + struct asm_aac_cfg aac_cfg; + uint16_t bits_per_sample = 16; + int ret; + + struct asm_softpause_params softpause = { + .enable = SOFT_PAUSE_ENABLE, + .period = SOFT_PAUSE_PERIOD, + .step = SOFT_PAUSE_STEP, + .rampingcurve = SOFT_PAUSE_CURVE_LINEAR, + }; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + + pr_debug("%s\n", __func__); + + params = &soc_prtd->dpcm[substream->stream].hw_params; + if (runtime->format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + + ret = q6asm_open_write_v2(prtd->audio_client, + compr->codec, bits_per_sample); + if (ret < 0) { + pr_err("%s: Session out open failed\n", + __func__); + return -ENOMEM; + } + msm_pcm_routing_reg_phy_stream( + soc_prtd->dai_link->be_id, + prtd->audio_client->perf_mode, + prtd->session_id, + substream->stream); + /* + * the number of channels are required to call volume api + * accoridngly. So, get channels from hw params + */ + if ((params_channels(params) > 0) && + (params_periods(params) <= runtime->hw.channels_max)) + prtd->channel_mode = params_channels(params); + + ret = q6asm_set_softpause(prtd->audio_client, &softpause); + if (ret < 0) + pr_err("%s: Send SoftPause Param failed ret=%d\n", + __func__, ret); + ret = q6asm_set_softvolume(prtd->audio_client, &softvol); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + + ret = q6asm_set_io_mode(prtd->audio_client, + (COMPRESSED_IO | ASYNC_IO_MODE)); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -ENOMEM; + } + + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + prtd->samp_rate = runtime->rate; + prtd->channel_mode = runtime->channels; + prtd->out_head = 0; + atomic_set(&prtd->out_count, runtime->periods); + + if (prtd->enabled) + return 0; + + switch (compr->info.codec_param.codec.id) { + case SND_AUDIOCODEC_MP3: + /* No media format block for mp3 */ + break; + case SND_AUDIOCODEC_AAC: + pr_debug("%s: SND_AUDIOCODEC_AAC\n", __func__); + memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg)); + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + aac_cfg.format = 0x03; + aac_cfg.ch_cfg = runtime->channels; + aac_cfg.sample_rate = runtime->rate; + ret = q6asm_media_format_block_aac(prtd->audio_client, + &aac_cfg); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case SND_AUDIOCODEC_AC3: { + struct snd_dec_ddp *ddp = + &compr->info.codec_param.codec.options.ddp; + pr_debug("%s: SND_AUDIOCODEC_AC3\n", __func__); + ret = msm_compr_send_ddp_cfg(prtd->audio_client, ddp); + if (ret < 0) + pr_err("%s: DDP CMD CFG failed\n", __func__); + break; + } + case SND_AUDIOCODEC_EAC3: { + struct snd_dec_ddp *ddp = + &compr->info.codec_param.codec.options.ddp; + pr_debug("%s: SND_AUDIOCODEC_EAC3\n", __func__); + ret = msm_compr_send_ddp_cfg(prtd->audio_client, ddp); + if (ret < 0) + pr_err("%s: DDP CMD CFG failed\n", __func__); + break; + } + default: + return -EINVAL; + } + + prtd->enabled = 1; + prtd->cmd_ack = 0; + prtd->cmd_interrupt = 0; + + return 0; +} + +static int msm_compr_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + struct audio_buffer *buf = prtd->audio_client->port[OUT].buf; + struct snd_codec *codec = &compr->info.codec_param.codec; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct audio_aio_read_param read_param; + uint16_t bits_per_sample = 16; + int ret = 0; + int i; + + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + + if (runtime->format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + + if (!msm_compr_capture_codecs( + compr->info.codec_param.codec.id)) { + /* + * request codec invalid or not supported, + * use default compress format + */ + compr->info.codec_param.codec.id = + SND_AUDIOCODEC_AMRWB; + } + switch (compr->info.codec_param.codec.id) { + case SND_AUDIOCODEC_AMRWB: + pr_debug("q6asm_open_read(FORMAT_AMRWB)\n"); + ret = q6asm_open_read(prtd->audio_client, + FORMAT_AMRWB); + if (ret < 0) { + pr_err("%s: compressed Session out open failed\n", + __func__); + return -ENOMEM; + } + pr_debug("msm_pcm_routing_reg_phy_stream\n"); + msm_pcm_routing_reg_phy_stream( + soc_prtd->dai_link->be_id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream); + break; + default: + pr_debug("q6asm_open_read_compressed(COMPRESSED_META_DATA_MODE)\n"); + /* + * ret = q6asm_open_read_compressed(prtd->audio_client, + * MAX_NUM_FRAMES_PER_BUFFER, + * COMPRESSED_META_DATA_MODE); + */ + ret = -EINVAL; + break; + } + + if (ret < 0) { + pr_err("%s: compressed Session out open failed\n", + __func__); + return -ENOMEM; + } + + ret = q6asm_set_io_mode(prtd->audio_client, + (COMPRESSED_IO | ASYNC_IO_MODE)); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -ENOMEM; + } + + if (!msm_compr_capture_codecs(codec->id)) { + /* + * request codec invalid or not supported, + * use default compress format + */ + codec->id = SND_AUDIOCODEC_AMRWB; + } + /* rate and channels are sent to audio driver */ + prtd->samp_rate = runtime->rate; + prtd->channel_mode = runtime->channels; + + if (prtd->enabled) + return ret; + read_param.len = prtd->pcm_count; + + switch (codec->id) { + case SND_AUDIOCODEC_AMRWB: + pr_debug("SND_AUDIOCODEC_AMRWB\n"); + ret = q6asm_enc_cfg_blk_amrwb(prtd->audio_client, + MAX_NUM_FRAMES_PER_BUFFER, + /* + * use fixed band mode and dtx mode + * band mode - 23.85 kbps + */ + AMR_WB_BAND_MODE, + /* dtx mode - disable */ + AMR_WB_DTX_MODE); + if (ret < 0) + pr_err("%s: CMD Format block failed: %d\n", + __func__, ret); + break; + default: + pr_debug("No config for codec %d\n", codec->id); + } + pr_debug("%s: Samp_rate = %d, Channel = %d, pcm_size = %d,\n" + "pcm_count = %d, periods = %d\n", + __func__, prtd->samp_rate, prtd->channel_mode, + prtd->pcm_size, prtd->pcm_count, runtime->periods); + + for (i = 0; i < runtime->periods; i++) { + read_param.uid = i; + switch (codec->id) { + case SND_AUDIOCODEC_AMRWB: + read_param.len = prtd->pcm_count + - COMPRE_CAPTURE_HEADER_SIZE; + read_param.paddr = buf[i].phys + + COMPRE_CAPTURE_HEADER_SIZE; + pr_debug("Push buffer [%d] to DSP, paddr: %pK, vaddr: %pK\n", + i, &read_param.paddr, + buf[i].data); + q6asm_async_read(prtd->audio_client, &read_param); + break; + default: + read_param.paddr = buf[i].phys; + /* q6asm_async_read_compressed(prtd->audio_client, + * &read_param); + */ + pr_debug("%s: To add support for read compressed\n", + __func__); + ret = -EINVAL; + break; + } + } + prtd->periods = runtime->periods; + + prtd->enabled = 1; + + return ret; +} + +static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + + pr_debug("%s\n", __func__); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->pcm_irq_pos = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (!msm_compr_capture_codecs( + compr->info.codec_param.codec.id)) { + /* + * request codec invalid or not supported, + * use default compress format + */ + compr->info.codec_param.codec.id = + SND_AUDIOCODEC_AMRWB; + } + switch (compr->info.codec_param.codec.id) { + case SND_AUDIOCODEC_AMRWB: + break; + default: + msm_pcm_routing_reg_psthr_stream( + soc_prtd->dai_link->be_id, + prtd->session_id, substream->stream); + break; + } + } + atomic_set(&prtd->pending_buffer, 1); + /* fallthrough */ + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: Trigger start\n", __func__); + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + atomic_set(&prtd->start, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + switch (compr->info.codec_param.codec.id) { + case SND_AUDIOCODEC_AMRWB: + break; + default: + msm_pcm_routing_reg_psthr_stream( + soc_prtd->dai_link->be_id, + prtd->session_id, substream->stream); + break; + } + } + atomic_set(&prtd->start, 0); + runtime->render_flag &= ~SNDRV_RENDER_STOPPED; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n"); + q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->start, 0); + runtime->render_flag &= ~SNDRV_RENDER_STOPPED; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void populate_codec_list(struct compr_audio *compr, + struct snd_pcm_runtime *runtime) +{ + pr_debug("%s\n", __func__); + /* MP3 Block */ + compr->info.compr_cap.num_codecs = 5; + compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min; + compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max; + compr->info.compr_cap.min_fragments = runtime->hw.periods_min; + compr->info.compr_cap.max_fragments = runtime->hw.periods_max; + compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; + compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; + compr->info.compr_cap.codecs[2] = SND_AUDIOCODEC_AC3; + compr->info.compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3; + compr->info.compr_cap.codecs[4] = SND_AUDIOCODEC_AMRWB; + /* Add new codecs here */ +} + +static int msm_compr_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr; + struct msm_audio *prtd; + int ret = 0; + + pr_debug("%s\n", __func__); + compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL); + if (compr == NULL) { + pr_err("Failed to allocate memory for msm_audio\n"); + return -ENOMEM; + } + prtd = &compr->prtd; + prtd->substream = substream; + runtime->render_flag = SNDRV_DMA_MODE; + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)compr_event_handler, compr); + if (!prtd->audio_client) { + pr_info("%s: Could not allocate memory\n", __func__); + kfree(prtd); + return -ENOMEM; + } + + prtd->audio_client->perf_mode = false; + pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session); + + prtd->session_id = prtd->audio_client->session; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw = msm_compr_hardware_playback; + prtd->cmd_ack = 1; + } else { + runtime->hw = msm_compr_hardware_capture; + } + + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + prtd->dsp_cnt = 0; + atomic_set(&prtd->pending_buffer, 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + compr->codec = FORMAT_MP3; + populate_codec_list(compr, runtime); + runtime->private_data = compr; + atomic_set(&prtd->eos, 0); + return 0; +} + +static int compressed_set_volume(struct msm_audio *prtd, uint32_t volume) +{ + int rc = 0; + int avg_vol = 0; + int lgain = (volume >> 16) & 0xFFFF; + int rgain = volume & 0xFFFF; + + if (prtd && prtd->audio_client) { + pr_debug("%s: channels %d volume 0x%x\n", __func__, + prtd->channel_mode, volume); + if ((prtd->channel_mode == 2) && + (lgain != rgain)) { + pr_debug("%s: call q6asm_set_lrgain\n", __func__); + rc = q6asm_set_lrgain(prtd->audio_client, lgain, rgain); + } else { + avg_vol = (lgain + rgain)/2; + pr_debug("%s: call q6asm_set_volume\n", __func__); + rc = q6asm_set_volume(prtd->audio_client, avg_vol); + } + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + } + } + return rc; +} + +static int msm_compr_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + int dir = 0; + + pr_debug("%s\n", __func__); + + dir = IN; + atomic_set(&prtd->pending_buffer, 0); + + prtd->pcm_irq_pos = 0; + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_audio_client_buf_free_contiguous(dir, + prtd->audio_client); + msm_pcm_routing_dereg_phy_stream( + soc_prtd->dai_link->be_id, + SNDRV_PCM_STREAM_PLAYBACK); + q6asm_audio_client_free(prtd->audio_client); + kfree(prtd); + return 0; +} + +static int msm_compr_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + int dir = OUT; + + pr_debug("%s\n", __func__); + atomic_set(&prtd->pending_buffer, 0); + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_audio_client_buf_free_contiguous(dir, + prtd->audio_client); + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, + SNDRV_PCM_STREAM_CAPTURE); + q6asm_audio_client_free(prtd->audio_client); + kfree(prtd); + return 0; +} + +static int msm_compr_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_compr_playback_close(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_compr_capture_close(substream); + return ret; +} + +static int msm_compr_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_compr_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_compr_capture_prepare(substream); + return ret; +} + +static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + + if (prtd->pcm_irq_pos >= prtd->pcm_size) + prtd->pcm_irq_pos = 0; + + pr_debug("%s: pcm_irq_pos = %d, pcm_size = %d, sample_bits = %d,\n" + "frame_bits = %d\n", __func__, prtd->pcm_irq_pos, + prtd->pcm_size, runtime->sample_bits, + runtime->frame_bits); + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static int msm_compr_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + struct audio_port_data *apd = ac->port; + struct audio_buffer *ab; + int dir = -1; + + prtd->mmap_flag = 1; + runtime->render_flag = SNDRV_NON_DMA_MODE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ab = &(apd[dir].buf[0]); + + return msm_audio_ion_mmap(ab, vma); +} + +static int msm_compr_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct audio_buffer *buf; + int dir, ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + /* Modifying kernel hardware params based on userspace config */ + if (params_periods(params) > 0 && + (params_periods(params) != runtime->hw.periods_max)) { + runtime->hw.periods_max = params_periods(params); + } + if (params_period_bytes(params) > 0 && + (params_period_bytes(params) != runtime->hw.period_bytes_min)) { + runtime->hw.period_bytes_min = params_period_bytes(params); + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_min * runtime->hw.periods_max; + pr_debug("allocate %zd buffers each of size %d\n", + runtime->hw.period_bytes_min, + runtime->hw.periods_max); + ret = q6asm_audio_client_buf_alloc_contiguous(dir, + prtd->audio_client, + runtime->hw.period_bytes_min, + runtime->hw.periods_max); + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", + ret); + return -ENOMEM; + } + buf = prtd->audio_client->port[dir].buf; + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf[0].data; + dma_buf->addr = buf[0].phys; + dma_buf->bytes = runtime->hw.buffer_bytes_max; + + pr_debug("%s: buf[%pK]dma_buf->area[%pK]dma_buf->addr[%pK]\n" + "dma_buf->bytes[%zd]\n", __func__, + (void *)buf, (void *)dma_buf->area, + &dma_buf->addr, dma_buf->bytes); + if (!dma_buf->area) + return -ENOMEM; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static int msm_compr_ioctl_shared(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + uint64_t timestamp; + uint64_t temp; + + switch (cmd) { + case SNDRV_COMPRESS_TSTAMP: { + struct snd_compr_tstamp *tstamp; + + pr_debug("SNDRV_COMPRESS_TSTAMP\n"); + tstamp = arg; + memset(tstamp, 0x0, sizeof(*tstamp)); + rc = q6asm_get_session_time(prtd->audio_client, ×tamp); + if (rc < 0) { + pr_err("%s: Get Session Time return value =%lld\n", + __func__, timestamp); + return -EAGAIN; + } + temp = (timestamp * 2 * runtime->channels); + temp = temp * (runtime->rate/1000); + temp = div_u64(temp, 1000); + tstamp->sampling_rate = runtime->rate; + tstamp->timestamp = timestamp; + pr_debug("%s: bytes_consumed:,timestamp = %lld,\n", + __func__, + tstamp->timestamp); + return 0; + } + case SNDRV_COMPRESS_GET_CAPS: { + struct snd_compr_caps *caps; + + caps = arg; + memset(caps, 0, sizeof(*caps)); + pr_debug("SNDRV_COMPRESS_GET_CAPS\n"); + memcpy(caps, &compr->info.compr_cap, sizeof(*caps)); + return 0; + } + case SNDRV_COMPRESS_SET_PARAMS: + pr_debug("SNDRV_COMPRESS_SET_PARAMS:\n"); + memcpy(&compr->info.codec_param, (void *) arg, + sizeof(struct snd_compr_params)); + switch (compr->info.codec_param.codec.id) { + case SND_AUDIOCODEC_MP3: + /* For MP3 we dont need any other parameter */ + pr_debug("SND_AUDIOCODEC_MP3\n"); + compr->codec = FORMAT_MP3; + break; + case SND_AUDIOCODEC_AAC: + pr_debug("SND_AUDIOCODEC_AAC\n"); + compr->codec = FORMAT_MPEG4_AAC; + break; + case SND_AUDIOCODEC_AC3: { + char params_value[MAX_AC3_PARAM_SIZE]; + int *params_value_data = (int *)params_value; + /* 36 is the max param length for ddp */ + int i; + struct snd_dec_ddp *ddp = + &compr->info.codec_param.codec.options.ddp; + uint32_t params_length = 0; + + memset(params_value, 0, MAX_AC3_PARAM_SIZE); + /* check integer overflow */ + if (ddp->params_length > UINT_MAX/sizeof(int)) { + pr_err("%s: Integer overflow ddp->params_length %d\n", + __func__, ddp->params_length); + return -EINVAL; + } + params_length = ddp->params_length*sizeof(int); + if (params_length > MAX_AC3_PARAM_SIZE) { + /*MAX is 36*sizeof(int) this should not happen*/ + pr_err("%s: params_length(%d) is greater than %zd\n", + __func__, params_length, MAX_AC3_PARAM_SIZE); + return -EINVAL; + } + pr_debug("SND_AUDIOCODEC_AC3\n"); + compr->codec = FORMAT_AC3; + pr_debug("params_length: %d\n", ddp->params_length); + for (i = 0; i < params_length/sizeof(int); i++) + pr_debug("params_value[%d]: %x\n", i, + params_value_data[i]); + for (i = 0; i < ddp->params_length/2; i++) { + ddp->params_id[i] = params_value_data[2*i]; + ddp->params_value[i] = params_value_data[2*i+1]; + } + if (atomic_read(&prtd->start)) { + rc = msm_compr_send_ddp_cfg(prtd->audio_client, + ddp); + if (rc < 0) + pr_err("%s: DDP CMD CFG failed\n", + __func__); + } + break; + } + case SND_AUDIOCODEC_EAC3: { + char params_value[MAX_AC3_PARAM_SIZE]; + int *params_value_data = (int *)params_value; + /* 36 is the max param length for ddp */ + int i; + struct snd_dec_ddp *ddp = + &compr->info.codec_param.codec.options.ddp; + uint32_t params_length = 0; + + memset(params_value, 0, MAX_AC3_PARAM_SIZE); + /* check integer overflow */ + if (ddp->params_length > UINT_MAX/sizeof(int)) { + pr_err("%s: Integer overflow ddp->params_length %d\n", + __func__, ddp->params_length); + return -EINVAL; + } + params_length = ddp->params_length*sizeof(int); + if (params_length > MAX_AC3_PARAM_SIZE) { + /*MAX is 36*sizeof(int) this should not happen*/ + pr_err("%s: params_length(%d) is greater than %zd\n", + __func__, params_length, MAX_AC3_PARAM_SIZE); + return -EINVAL; + } + pr_debug("SND_AUDIOCODEC_EAC3\n"); + compr->codec = FORMAT_EAC3; + pr_debug("params_length: %d\n", ddp->params_length); + for (i = 0; i < ddp->params_length; i++) + pr_debug("params_value[%d]: %x\n", i, + params_value_data[i]); + for (i = 0; i < ddp->params_length/2; i++) { + ddp->params_id[i] = params_value_data[2*i]; + ddp->params_value[i] = params_value_data[2*i+1]; + } + if (atomic_read(&prtd->start)) { + rc = msm_compr_send_ddp_cfg(prtd->audio_client, + ddp); + if (rc < 0) + pr_err("%s: DDP CMD CFG failed\n", + __func__); + } + break; + } + default: + pr_debug("FORMAT_LINEAR_PCM\n"); + compr->codec = FORMAT_LINEAR_PCM; + break; + } + return 0; + case SNDRV_PCM_IOCTL1_RESET: + pr_debug("SNDRV_PCM_IOCTL1_RESET\n"); + /* Flush only when session is started during CAPTURE, + * while PLAYBACK has no such restriction. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + atomic_read(&prtd->start))) { + if (atomic_read(&prtd->eos)) { + prtd->cmd_interrupt = 1; + wake_up(&the_locks.eos_wait); + atomic_set(&prtd->eos, 0); + } + + /* A unlikely race condition possible with FLUSH + * DRAIN if ack is set by flush and reset by drain + */ + prtd->cmd_ack = 0; + rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH); + if (rc < 0) { + pr_err("%s: flush cmd failed rc=%d\n", + __func__, rc); + return rc; + } + rc = wait_event_timeout(the_locks.flush_wait, + prtd->cmd_ack, 5 * HZ); + if (!rc) + pr_err("Flush cmd timeout\n"); + prtd->pcm_irq_pos = 0; + } + break; + case SNDRV_COMPRESS_DRAIN: + pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__); + if (atomic_read(&prtd->pending_buffer)) { + pr_debug("%s: no pending writes, drain would block\n", + __func__); + return -EWOULDBLOCK; + } + + atomic_set(&prtd->eos, 1); + atomic_set(&prtd->pending_buffer, 0); + prtd->cmd_ack = 0; + q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + /* Wait indefinitely for DRAIN. Flush can also signal this*/ + rc = wait_event_interruptible(the_locks.eos_wait, + (prtd->cmd_ack || prtd->cmd_interrupt)); + + if (rc < 0) + pr_err("EOS cmd interrupted\n"); + pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait\n", __func__); + + if (prtd->cmd_interrupt) + rc = -EINTR; + + prtd->cmd_interrupt = 0; + return rc; + default: + break; + } + return snd_pcm_lib_ioctl(substream, cmd, arg); +} +#ifdef CONFIG_COMPAT +struct snd_enc_wma32 { + u32 super_block_align; /* WMA Type-specific data */ + u32 encodeopt1; + u32 encodeopt2; +}; + +struct snd_enc_vorbis32 { + s32 quality; + u32 managed; + u32 max_bit_rate; + u32 min_bit_rate; + u32 downmix; +}; + +struct snd_enc_real32 { + u32 quant_bits; + u32 start_region; + u32 num_regions; +}; + +struct snd_enc_flac32 { + u32 num; + u32 gain; +}; + +struct snd_enc_generic32 { + u32 bw; /* encoder bandwidth */ + s32 reserved[15]; +}; +struct snd_dec_ddp32 { + u32 params_length; + u32 params_id[18]; + u32 params_value[18]; +}; + +union snd_codec_options32 { + struct snd_enc_wma32 wma; + struct snd_enc_vorbis32 vorbis; + struct snd_enc_real32 real; + struct snd_enc_flac32 flac; + struct snd_enc_generic32 generic; + struct snd_dec_ddp32 ddp; +}; + +struct snd_codec32 { + u32 id; + u32 ch_in; + u32 ch_out; + u32 sample_rate; + u32 bit_rate; + u32 rate_control; + u32 profile; + u32 level; + u32 ch_mode; + u32 format; + u32 align; + union snd_codec_options32 options; + u32 reserved[3]; +}; + +struct snd_compressed_buffer32 { + u32 fragment_size; + u32 fragments; +}; + +struct snd_compr_params32 { + struct snd_compressed_buffer32 buffer; + struct snd_codec32 codec; + u8 no_wake_mode; +}; + +struct snd_compr_caps32 { + u32 num_codecs; + u32 direction; + u32 min_fragment_size; + u32 max_fragment_size; + u32 min_fragments; + u32 max_fragments; + u32 codecs[MAX_NUM_CODECS]; + u32 reserved[11]; +}; +struct snd_compr_tstamp32 { + u32 byte_offset; + u32 copied_total; + compat_ulong_t pcm_frames; + compat_ulong_t pcm_io_frames; + u32 sampling_rate; + compat_u64 timestamp; +}; +enum { + SNDRV_COMPRESS_TSTAMP32 = _IOR('C', 0x20, struct snd_compr_tstamp32), + SNDRV_COMPRESS_GET_CAPS32 = _IOWR('C', 0x10, struct snd_compr_caps32), + SNDRV_COMPRESS_SET_PARAMS32 = + _IOW('C', 0x12, struct snd_compr_params32), +}; +static int msm_compr_compat_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + + switch (cmd) { + case SNDRV_COMPRESS_TSTAMP32: { + struct snd_compr_tstamp tstamp; + struct snd_compr_tstamp32 tstamp32; + + memset(&tstamp, 0, sizeof(tstamp)); + memset(&tstamp32, 0, sizeof(tstamp32)); + cmd = SNDRV_COMPRESS_TSTAMP; + err = msm_compr_ioctl_shared(substream, cmd, &tstamp); + if (err) { + pr_err("%s: COMPRESS_TSTAMP failed rc %d\n", + __func__, err); + goto bail_out; + } + tstamp32.byte_offset = tstamp.byte_offset; + tstamp32.copied_total = tstamp.copied_total; + tstamp32.pcm_frames = tstamp.pcm_frames; + tstamp32.pcm_io_frames = tstamp.pcm_io_frames; + tstamp32.sampling_rate = tstamp.sampling_rate; + tstamp32.timestamp = tstamp.timestamp; + if (copy_to_user(arg, &tstamp32, sizeof(tstamp32))) { + pr_err("%s: copytouser failed COMPRESS_TSTAMP32\n", + __func__); + err = -EFAULT; + } + break; + } + case SNDRV_COMPRESS_GET_CAPS32: { + struct snd_compr_caps caps; + struct snd_compr_caps32 caps32; + u32 i; + + memset(&caps, 0, sizeof(caps)); + memset(&caps32, 0, sizeof(caps32)); + cmd = SNDRV_COMPRESS_GET_CAPS; + err = msm_compr_ioctl_shared(substream, cmd, &caps); + if (err) { + pr_err("%s: GET_CAPS failed rc %d\n", + __func__, err); + goto bail_out; + } + pr_debug("SNDRV_COMPRESS_GET_CAPS_32\n"); + if (!err && caps.num_codecs >= MAX_NUM_CODECS) { + pr_err("%s: Invalid number of codecs\n", __func__); + err = -EINVAL; + goto bail_out; + } + caps32.direction = caps.direction; + caps32.max_fragment_size = caps.max_fragment_size; + caps32.max_fragments = caps.max_fragments; + caps32.min_fragment_size = caps.min_fragment_size; + caps32.num_codecs = caps.num_codecs; + for (i = 0; i < caps.num_codecs; i++) + caps32.codecs[i] = caps.codecs[i]; + if (copy_to_user(arg, &caps32, sizeof(caps32))) { + pr_err("%s: copytouser failed COMPRESS_GETCAPS32\n", + __func__); + err = -EFAULT; + } + break; + } + case SNDRV_COMPRESS_SET_PARAMS32: { + struct snd_compr_params32 params32; + struct snd_compr_params params; + + memset(¶ms32, 0, sizeof(params32)); + memset(¶ms, 0, sizeof(params)); + cmd = SNDRV_COMPRESS_SET_PARAMS; + if (copy_from_user(¶ms32, arg, sizeof(params32))) { + pr_err("%s: copyfromuser failed SET_PARAMS32\n", + __func__); + err = -EFAULT; + goto bail_out; + } + params.no_wake_mode = params32.no_wake_mode; + params.codec.id = params32.codec.id; + params.codec.ch_in = params32.codec.ch_in; + params.codec.ch_out = params32.codec.ch_out; + params.codec.sample_rate = params32.codec.sample_rate; + params.codec.bit_rate = params32.codec.bit_rate; + params.codec.rate_control = params32.codec.rate_control; + params.codec.profile = params32.codec.profile; + params.codec.level = params32.codec.level; + params.codec.ch_mode = params32.codec.ch_mode; + params.codec.format = params32.codec.format; + params.codec.align = params32.codec.align; + + switch (params.codec.id) { + case SND_AUDIOCODEC_WMA: + case SND_AUDIOCODEC_WMA_PRO: + params.codec.options.wma.encodeopt1 = + params32.codec.options.wma.encodeopt1; + params.codec.options.wma.encodeopt2 = + params32.codec.options.wma.encodeopt2; + params.codec.options.wma.super_block_align = + params32.codec.options.wma.super_block_align; + break; + case SND_AUDIOCODEC_VORBIS: + params.codec.options.vorbis.downmix = + params32.codec.options.vorbis.downmix; + params.codec.options.vorbis.managed = + params32.codec.options.vorbis.managed; + params.codec.options.vorbis.max_bit_rate = + params32.codec.options.vorbis.max_bit_rate; + params.codec.options.vorbis.min_bit_rate = + params32.codec.options.vorbis.min_bit_rate; + params.codec.options.vorbis.quality = + params32.codec.options.vorbis.quality; + break; + case SND_AUDIOCODEC_REAL: + params.codec.options.real.num_regions = + params32.codec.options.real.num_regions; + params.codec.options.real.quant_bits = + params32.codec.options.real.quant_bits; + params.codec.options.real.start_region = + params32.codec.options.real.start_region; + break; + case SND_AUDIOCODEC_FLAC: + params.codec.options.flac.gain = + params32.codec.options.flac.gain; + params.codec.options.flac.num = + params32.codec.options.flac.num; + break; + case SND_AUDIOCODEC_DTS: + case SND_AUDIOCODEC_DTS_PASS_THROUGH: + case SND_AUDIOCODEC_DTS_LBR: + case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH: + case SND_AUDIOCODEC_DTS_TRANSCODE_LOOPBACK: + break; + case SND_AUDIOCODEC_AC3: + case SND_AUDIOCODEC_EAC3: + params.codec.options.ddp.params_length = + params32.codec.options.ddp.params_length; + memcpy(params.codec.options.ddp.params_value, + params32.codec.options.ddp.params_value, + sizeof(params32.codec.options.ddp.params_value)); + memcpy(params.codec.options.ddp.params_id, + params32.codec.options.ddp.params_id, + sizeof(params32.codec.options.ddp.params_id)); + break; + default: + params.codec.options.generic.bw = + params32.codec.options.generic.bw; + break; + } + if (!err) + err = msm_compr_ioctl_shared(substream, cmd, ¶ms); + break; + } + default: + err = msm_compr_ioctl_shared(substream, cmd, arg); + } +bail_out: + return err; + +} +#endif +static int msm_compr_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + + if (!substream) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + pr_debug("%s called with cmd = %d\n", __func__, cmd); + switch (cmd) { + case SNDRV_COMPRESS_TSTAMP: { + struct snd_compr_tstamp tstamp; + + if (!arg) { + pr_err("%s: Invalid params Tstamp\n", __func__); + return -EINVAL; + } + err = msm_compr_ioctl_shared(substream, cmd, &tstamp); + if (err) + pr_err("%s: COMPRESS_TSTAMP failed rc %d\n", + __func__, err); + if (!err && copy_to_user(arg, &tstamp, sizeof(tstamp))) { + pr_err("%s: copytouser failed COMPRESS_TSTAMP\n", + __func__); + err = -EFAULT; + } + break; + } + case SNDRV_COMPRESS_GET_CAPS: { + struct snd_compr_caps cap; + + if (!arg) { + pr_err("%s: Invalid params getcaps\n", __func__); + return -EINVAL; + } + pr_debug("SNDRV_COMPRESS_GET_CAPS\n"); + err = msm_compr_ioctl_shared(substream, cmd, &cap); + if (err) + pr_err("%s: GET_CAPS failed rc %d\n", + __func__, err); + if (!err && copy_to_user(arg, &cap, sizeof(cap))) { + pr_err("%s: copytouser failed GET_CAPS\n", + __func__); + err = -EFAULT; + } + break; + } + case SNDRV_COMPRESS_SET_PARAMS: { + struct snd_compr_params params; + + if (!arg) { + pr_err("%s: Invalid params setparam\n", __func__); + return -EINVAL; + } + if (copy_from_user(¶ms, arg, + sizeof(struct snd_compr_params))) { + pr_err("%s: SET_PARAMS\n", __func__); + return -EFAULT; + } + err = msm_compr_ioctl_shared(substream, cmd, ¶ms); + if (err) + pr_err("%s: SET_PARAMS failed rc %d\n", + __func__, err); + break; + } + default: + err = msm_compr_ioctl_shared(substream, cmd, arg); + } + return err; +} + +static int msm_compr_restart(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + struct audio_aio_write_param param; + struct audio_buffer *buf = NULL; + struct output_meta_data_st output_meta_data; + int time_stamp_flag = 0; + int buffer_length = 0; + + pr_debug("%s, trigger restart\n", __func__); + + if (runtime->render_flag & SNDRV_RENDER_STOPPED) { + buf = prtd->audio_client->port[IN].buf; + pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n", + __func__, prtd->pcm_count, prtd->out_head); + pr_debug("%s:writing buffer[%d] from 0x%08x\n", + __func__, prtd->out_head, + ((unsigned int)buf[0].phys + + (prtd->out_head * prtd->pcm_count))); + + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + time_stamp_flag = SET_TIMESTAMP; + else + time_stamp_flag = NO_TIMESTAMP; + memcpy(&output_meta_data, (char *)(buf->data + + prtd->out_head * prtd->pcm_count), + COMPRE_OUTPUT_METADATA_SIZE); + + buffer_length = output_meta_data.frame_size; + pr_debug("meta_data_length: %d, frame_length: %d\n", + output_meta_data.meta_data_length, + output_meta_data.frame_size); + pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n", + output_meta_data.timestamp_msw, + output_meta_data.timestamp_lsw); + + param.paddr = (unsigned long)buf[0].phys + + (prtd->out_head * prtd->pcm_count) + + output_meta_data.meta_data_length; + param.len = buffer_length; + param.msw_ts = output_meta_data.timestamp_msw; + param.lsw_ts = output_meta_data.timestamp_lsw; + param.flags = time_stamp_flag; + param.uid = prtd->session_id; + if (q6asm_async_write(prtd->audio_client, + ¶m) < 0) + pr_err("%s:q6asm_async_write failed\n", + __func__); + else + prtd->out_head = + (prtd->out_head + 1) & (runtime->periods - 1); + + runtime->render_flag &= ~SNDRV_RENDER_STOPPED; + return 0; + } + return 0; +} + +static int msm_compr_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : %x\n", __func__, volume); + if (!substream) + return -ENODEV; + if (!substream->runtime) + return 0; + prtd = substream->runtime->private_data; + if (prtd) + rc = compressed_set_volume(prtd, volume); + + return rc; +} + +static int msm_compr_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + + pr_debug("%s\n", __func__); + if (!substream) + return -ENODEV; + if (!substream->runtime) + return 0; + prtd = substream->runtime->private_data; + if (prtd) + ucontrol->value.integer.value[0] = prtd->volume; + return 0; +} + +static int msm_compr_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + + dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, rtd->dai_link->be_id, + &volume_info); + if (ret < 0) + return ret; + kctl = volume_info->kctl; + kctl->put = msm_compr_volume_ctl_put; + kctl->get = msm_compr_volume_ctl_get; + kctl->tlv.p = compr_rx_vol_gain; + return 0; +} + +static struct snd_pcm_ops msm_compr_ops = { + .open = msm_compr_open, + .hw_params = msm_compr_hw_params, + .close = msm_compr_close, + .ioctl = msm_compr_ioctl, + .prepare = msm_compr_prepare, + .trigger = msm_compr_trigger, + .pointer = msm_compr_pointer, + .mmap = msm_compr_mmap, + .restart = msm_compr_restart, +#ifdef CONFIG_COMPAT + .compat_ioctl = msm_compr_compat_ioctl, +#endif +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_compr_add_controls(rtd); + if (ret) + pr_err("%s, kctl add failed\n", __func__); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_compr_ops, + .pcm_new = msm_asoc_pcm_new, +}; + +static int msm_compr_probe(struct platform_device *pdev) +{ + + dev_info(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_compr_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_compr_dt_match[] = { + {.compatible = "qcom,msm-compr-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_compr_dt_match); + +static struct platform_driver msm_compr_driver = { + .driver = { + .name = "msm-compr-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_compr_dt_match, + }, + .probe = msm_compr_probe, + .remove = msm_compr_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + init_waitqueue_head(&the_locks.enable_wait); + init_waitqueue_head(&the_locks.eos_wait); + init_waitqueue_head(&the_locks.write_wait); + init_waitqueue_head(&the_locks.read_wait); + init_waitqueue_head(&the_locks.flush_wait); + + return platform_driver_register(&msm_compr_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_compr_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h new file mode 100644 index 000000000000..d6e3ec6956b1 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_COMPR_H +#define _MSM_COMPR_H +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" + +struct compr_info { + struct snd_compr_caps compr_cap; + struct snd_compr_codec_caps codec_caps; + struct snd_compr_params codec_param; +}; + +struct compr_audio { + struct msm_audio prtd; + struct compr_info info; + uint32_t codec; +}; + +#endif /*_MSM_COMPR_H*/ diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 1ceb006aa65d..61605dff73f7 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -55,6 +55,10 @@ #define DSP_NUM_OUTPUT_FRAME_BUFFERED 2 #define FLAC_BLK_SIZE_LIMIT 65535 +/* Timestamp mode payload offsets */ +#define TS_LSW_OFFSET 6 +#define TS_MSW_OFFSET 7 + /* decoder parameter length */ #define DDP_DEC_MAX_NUM_PARAM 18 @@ -68,12 +72,6 @@ const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, COMPRESSED_LR_VOL_MAX_STEPS); -/* - * LSB 8 bits is used as stream id for some DSP - * commands for compressed playback. - */ -#define STREAM_ID_FROM_TOKEN(i) (i & 0xFF) - /* Stream id switches between 1 and 2 */ #define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1) @@ -102,7 +100,7 @@ struct msm_compr_gapless_state { static unsigned int supported_sample_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, - 88200, 96000, 176400, 192000 + 88200, 96000, 176400, 192000, 352800, 384000, 2822400, 5644800 }; struct msm_compr_pdata { @@ -110,6 +108,7 @@ struct msm_compr_pdata { uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */ struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX]; bool use_dsp_gapless_mode; + bool use_legacy_api; /* indicates use older asm apis*/ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX]; struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; }; @@ -132,6 +131,13 @@ struct msm_compr_audio { uint64_t bytes_received; /* from userspace */ uint64_t bytes_sent; /* to DSP */ + uint64_t received_total; /* bytes received from DSP */ + uint64_t bytes_copied; /* to userspace */ + uint64_t bytes_read; /* from DSP */ + uint32_t bytes_read_offset; /* bytes read offset */ + + uint32_t ts_header_offset; /* holds the timestamp header offset */ + int32_t first_buffer; int32_t last_buffer; int32_t partial_drain_delay; @@ -174,7 +180,9 @@ struct msm_compr_audio { spinlock_t lock; }; -const u32 compr_codecs[] = {SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3}; +const u32 compr_codecs[] = { + SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS, + SND_AUDIOCODEC_DSD}; struct query_audio_effect { uint32_t mod_id; @@ -211,7 +219,7 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream, uint32_t volume_l, uint32_t volume_r) { struct msm_compr_audio *prtd; - int rc = 0, i; + int rc = 0; uint32_t avg_vol, gain_list[VOLUME_CONTROL_MAX_CHANNELS]; uint32_t num_channels; struct snd_soc_pcm_runtime *rtd; @@ -255,9 +263,7 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream, * */ avg_vol = (volume_l + volume_r) / 2; - for (i = 0; i < prtd->num_channels; i++) - gain_list[i] = avg_vol; - + rc = q6asm_set_volume(prtd->audio_client, avg_vol); } else { gain_list[0] = volume_l; gain_list[1] = volume_r; @@ -267,11 +273,10 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream, num_channels = 3; use_default = true; } + rc = q6asm_set_multich_gain(prtd->audio_client, num_channels, + gain_list, chmap, use_default); } - rc = q6asm_set_multich_gain(prtd->audio_client, num_channels, - gain_list, chmap, use_default); - if (rc < 0) pr_err("%s: Send vol gain command failed rc=%d\n", __func__, rc); @@ -370,6 +375,53 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) return 0; } +static int msm_compr_read_buffer(struct msm_compr_audio *prtd) +{ + int buffer_length; + uint64_t bytes_available; + uint64_t buffer_sent; + struct audio_aio_read_param param; + int ret; + + if (!atomic_read(&prtd->start)) { + pr_err("%s: stream is not in started state\n", __func__); + return -EINVAL; + } + + buffer_length = prtd->codec_param.buffer.fragment_size - + prtd->ts_header_offset; + bytes_available = prtd->received_total - prtd->bytes_copied; + buffer_sent = prtd->bytes_read - prtd->bytes_copied; + if (buffer_sent + buffer_length + prtd->ts_header_offset + > prtd->buffer_size) { + pr_debug(" %s : Buffer is Full bytes_available: %llu\n", + __func__, bytes_available); + return 0; + } + + memset(¶m, 0x0, sizeof(struct audio_aio_read_param)); + param.paddr = prtd->buffer_paddr + prtd->bytes_read_offset + + prtd->ts_header_offset; + param.len = buffer_length; + param.uid = buffer_length; + param.flags = prtd->codec_param.codec.flags; + + pr_debug("%s: reading %d bytes from DSP byte_offset = %llu\n", + __func__, buffer_length, prtd->bytes_read); + ret = q6asm_async_read(prtd->audio_client, ¶m); + if (ret < 0) { + pr_err("%s: q6asm_async_read failed - %d\n", + __func__, ret); + return ret; + } + prtd->bytes_read += buffer_length; + prtd->bytes_read_offset += buffer_length; + if (prtd->bytes_read_offset >= prtd->buffer_size) + prtd->bytes_read_offset -= prtd->buffer_size; + + return 0; +} + static void compr_event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) { @@ -382,6 +434,8 @@ static void compr_event_handler(uint32_t opcode, int stream_id; uint32_t stream_index; unsigned long flags; + uint64_t read_size; + uint32_t *buff_addr; if (!prtd) { pr_err("%s: prtd is NULL\n", __func__); @@ -390,6 +444,12 @@ static void compr_event_handler(uint32_t opcode, cstream = prtd->cstream; ac = prtd->audio_client; + /* + * Token for rest of the compressed commands use to set + * session id, stream id, dir etc. + */ + stream_id = q6asm_get_stream_id_from_token(token); + pr_debug("%s opcode =%08x\n", __func__, opcode); switch (opcode) { case ASM_DATA_EVENT_WRITE_DONE_V2: @@ -417,6 +477,12 @@ static void compr_event_handler(uint32_t opcode, prtd->byte_offset, token); } + /* + * Token for WRITE command represents the amount of data + * written to ADSP in the last write, update offset and + * total copied data accordingly. + */ + prtd->byte_offset += token; prtd->copied_total += token; if (prtd->byte_offset >= prtd->buffer_size) @@ -455,11 +521,53 @@ static void compr_event_handler(uint32_t opcode, spin_unlock_irqrestore(&prtd->lock, flags); break; + + case ASM_DATA_EVENT_READ_DONE_V2: + spin_lock_irqsave(&prtd->lock, flags); + + pr_debug("ASM_DATA_EVENT_READ_DONE_V2 offset %d, length %d\n", + prtd->byte_offset, payload[4]); + + if (prtd->ts_header_offset) { + /* Update the header for received buffer */ + buff_addr = prtd->buffer + prtd->byte_offset; + /* Write the length of the buffer */ + *buff_addr = prtd->codec_param.buffer.fragment_size + - prtd->ts_header_offset; + buff_addr++; + /* Write the offset */ + *buff_addr = prtd->ts_header_offset; + buff_addr++; + /* Write the TS LSW */ + *buff_addr = payload[TS_LSW_OFFSET]; + buff_addr++; + /* Write the TS MSW */ + *buff_addr = payload[TS_MSW_OFFSET]; + } + /* Always assume read_size is same as fragment_size */ + read_size = prtd->codec_param.buffer.fragment_size; + prtd->byte_offset += read_size; + prtd->received_total += read_size; + if (prtd->byte_offset >= prtd->buffer_size) + prtd->byte_offset -= prtd->buffer_size; + + snd_compr_fragment_elapsed(cstream); + + if (!atomic_read(&prtd->start)) { + pr_debug("read_done received while not started, treat as xrun"); + atomic_set(&prtd->xrun, 1); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + msm_compr_read_buffer(prtd); + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case ASM_DATA_EVENT_RENDERED_EOS: spin_lock_irqsave(&prtd->lock, flags); pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n", - __func__, token, STREAM_ID_FROM_TOKEN(token)); - stream_id = STREAM_ID_FROM_TOKEN(token); + __func__, token, stream_id); if (atomic_read(&prtd->eos) && !prtd->gapless_state.set_next_stream_id) { pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); @@ -509,6 +617,14 @@ static void compr_event_handler(uint32_t opcode, /* FIXME: A state is a better way, dealing with this */ spin_lock_irqsave(&prtd->lock, flags); + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + atomic_set(&prtd->start, 1); + msm_compr_read_buffer(prtd); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + if (!prtd->bytes_sent) { bytes_available = prtd->bytes_received - prtd->copied_total; @@ -547,25 +663,25 @@ static void compr_event_handler(uint32_t opcode, case ASM_STREAM_CMD_FLUSH: pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__); pr_debug("token 0x%x, stream id %d\n", token, - STREAM_ID_FROM_TOKEN(token)); + stream_id); prtd->cmd_ack = 1; break; case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:", __func__); pr_debug("token 0x%x, stream id = %d\n", token, - STREAM_ID_FROM_TOKEN(token)); + stream_id); break; case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:", __func__); pr_debug("token = 0x%x, stream id = %d\n", token, - STREAM_ID_FROM_TOKEN(token)); + stream_id); break; case ASM_STREAM_CMD_CLOSE: pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__); pr_debug("token 0x%x, stream id %d\n", token, - STREAM_ID_FROM_TOKEN(token)); + stream_id); /* * wakeup wait for stream avail on stream 3 * after stream 1 ends. @@ -597,8 +713,12 @@ static void compr_event_handler(uint32_t opcode, pr_err("%s: Received reset events CB, move to error state", __func__); spin_lock_irqsave(&prtd->lock, flags); - snd_compr_fragment_elapsed(cstream); + /* + * Since ADSP is down, let this driver pretend that it copied + * all the bytes received, so that next write will be triggered + */ prtd->copied_total = prtd->bytes_received; + snd_compr_fragment_elapsed(cstream); atomic_set(&prtd->error, 1); wake_up(&prtd->drain_wait); if (atomic_cmpxchg(&prtd->eos, 1, 0)) { @@ -623,7 +743,8 @@ static int msm_compr_get_partial_drain_delay(int frame_sz, int sample_rate) delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ? delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0; - pr_debug("%s: partial drain delay %d\n", __func__, delay_time_ms); + pr_debug("%s: frame_sz %d, sample_rate %d, partial drain delay %d\n", + __func__, frame_sz, sample_rate, delay_time_ms); return delay_time_ms; } @@ -639,7 +760,7 @@ static void populate_codec_list(struct msm_compr_audio *prtd) COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; prtd->compr_cap.max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; - prtd->compr_cap.num_codecs = 12; + prtd->compr_cap.num_codecs = 14; prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3; @@ -652,6 +773,8 @@ static void populate_codec_list(struct msm_compr_audio *prtd) prtd->compr_cap.codecs[9] = SND_AUDIOCODEC_VORBIS; prtd->compr_cap.codecs[10] = SND_AUDIOCODEC_ALAC; prtd->compr_cap.codecs[11] = SND_AUDIOCODEC_APE; + prtd->compr_cap.codecs[12] = SND_AUDIOCODEC_DTS; + prtd->compr_cap.codecs[13] = SND_AUDIOCODEC_DSD; } static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, @@ -670,12 +793,14 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, struct asm_vorbis_cfg vorbis_cfg; struct asm_alac_cfg alac_cfg; struct asm_ape_cfg ape_cfg; + struct asm_dsd_cfg dsd_cfg; union snd_codec_options *codec_options; int ret = 0; - uint16_t bit_width = 16; + uint16_t bit_width; bool use_default_chmap = true; char *chmap = NULL; + uint16_t sample_word_size; pr_debug("%s: use_gapless_codec_options %d\n", __func__, use_gapless_codec_options); @@ -699,15 +824,36 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, chmap = pdata->ch_map[rtd->dai_link->be_id]->channel_map; } - if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + + switch (prtd->codec_param.codec.format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bit_width = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: bit_width = 24; - ret = q6asm_media_format_block_pcm_format_support_v2( + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_width = 16; + sample_word_size = 16; + break; + } + ret = q6asm_media_format_block_pcm_format_support_v4( prtd->audio_client, prtd->sample_rate, prtd->num_channels, bit_width, stream_id, use_default_chmap, - chmap); + chmap, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_err("%s: CMD Format block failed\n", __func__); @@ -861,7 +1007,24 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, pr_err("%s: CMD Format block failed ret %d\n", __func__, ret); break; - + case FORMAT_DTS: + pr_debug("SND_AUDIOCODEC_DTS\n"); + /* no media format block needed */ + break; + case FORMAT_DSD: + pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__); + memset(&dsd_cfg, 0x0, sizeof(struct asm_dsd_cfg)); + dsd_cfg.num_channels = prtd->num_channels; + dsd_cfg.dsd_data_rate = prtd->sample_rate; + dsd_cfg.num_version = 0; + dsd_cfg.is_bitwise_big_endian = 1; + dsd_cfg.dsd_channel_block_size = 1; + ret = q6asm_media_format_block_dsd(prtd->audio_client, + &dsd_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD DSD Format block failed ret %d\n", + __func__, ret); + break; default: pr_debug("%s, unsupported format, skip", __func__); break; @@ -912,7 +1075,8 @@ static int msm_compr_init_pp_params(struct snd_compr_stream *cstream, return ret; } -static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) +static int msm_compr_configure_dsp_for_playback + (struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; @@ -934,7 +1098,14 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) }; pr_debug("%s: stream_id %d\n", __func__, ac->stream_id); - if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + stream_index = STREAM_ARRAY_INDEX(ac->stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) { + pr_err("%s: Invalid stream index:%d", __func__, stream_index); + return -EINVAL; + } + + if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) || + (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE)) bits_per_sample = 24; else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) bits_per_sample = 32; @@ -947,6 +1118,8 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) __func__, ret, prtd->compr_passthr); return ret; } + prtd->gapless_state.stream_opened[stream_index] = 1; + ret = msm_pcm_routing_reg_phy_compr_stream( soc_prtd->dai_link->be_id, ac->perf_mode, @@ -961,7 +1134,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) } else { pr_debug("%s: stream_id %d bits_per_sample %d\n", __func__, ac->stream_id, bits_per_sample); - ret = q6asm_stream_open_write_v2(ac, + ret = q6asm_stream_open_write_v4(ac, prtd->codec, bits_per_sample, ac->stream_id, prtd->gapless_state.use_dsp_gapless_mode); @@ -970,6 +1143,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) __func__, ret, prtd->compr_passthr); return -ENOMEM; } + prtd->gapless_state.stream_opened[stream_index] = 1; pr_debug("%s: be_id %d\n", __func__, soc_prtd->dai_link->be_id); ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, @@ -986,31 +1160,30 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) if (ret < 0) pr_err("%s : Set Volume failed : %d", __func__, ret); - ret = q6asm_send_cal(ac); - if (ret < 0) - pr_debug("%s : Send cal failed : %d", __func__, ret); + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s : Don't send cal and PP params for compress path", + __func__); + } else { + ret = q6asm_send_cal(ac); + if (ret < 0) + pr_debug("%s : Send cal failed : %d", __func__, ret); - ret = q6asm_set_softpause(ac, &softpause); - if (ret < 0) - pr_err("%s: Send SoftPause Param failed ret=%d\n", - __func__, ret); - ret = q6asm_set_softvolume(ac, &softvol); - if (ret < 0) - pr_err("%s: Send SoftVolume Param failed ret=%d\n", - __func__, ret); + ret = q6asm_set_softpause(ac, &softpause); + if (ret < 0) + pr_err("%s: Send SoftPause Param failed ret=%d\n", + __func__, ret); + ret = q6asm_set_softvolume(ac, &softvol); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + } ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE)); if (ret < 0) { pr_err("%s: Set IO mode failed\n", __func__); return -EINVAL; } - stream_index = STREAM_ARRAY_INDEX(ac->stream_id); - if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) { - pr_err("%s: Invalid stream index:%d", __func__, stream_index); - return -EINVAL; - } - prtd->gapless_state.stream_opened[stream_index] = 1; runtime->fragments = prtd->codec_param.buffer.fragments; runtime->fragment_size = prtd->codec_param.buffer.fragment_size; pr_debug("allocate %d buffers each of size %d\n", @@ -1040,7 +1213,108 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) return ret; } -static int msm_compr_open(struct snd_compr_stream *cstream) +static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; + uint16_t bits_per_sample; + uint16_t sample_word_size; + int dir = OUT, ret = 0; + struct audio_client *ac = prtd->audio_client; + uint32_t stream_index; + + switch (prtd->codec_param.codec.format) { + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + + pr_debug("%s: stream_id %d bits_per_sample %d\n", + __func__, ac->stream_id, bits_per_sample); + + ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM, + bits_per_sample); + if (ret < 0) { + pr_err("%s: q6asm_open_read failed:%d\n", __func__, ret); + return ret; + } + + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) { + pr_err("%s: stream reg failed:%d\n", __func__, ret); + return ret; + } + + ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE)); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -EINVAL; + } + + stream_index = STREAM_ARRAY_INDEX(ac->stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) { + pr_err("%s: Invalid stream index:%d", __func__, stream_index); + return -EINVAL; + } + + runtime->fragments = prtd->codec_param.buffer.fragments; + runtime->fragment_size = prtd->codec_param.buffer.fragment_size; + pr_debug("%s: allocate %d buffers each of size %d\n", + __func__, runtime->fragments, + runtime->fragment_size); + ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac, + runtime->fragment_size, + runtime->fragments); + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret); + return -ENOMEM; + } + + prtd->byte_offset = 0; + prtd->received_total = 0; + prtd->app_pointer = 0; + prtd->bytes_copied = 0; + prtd->bytes_read = 0; + prtd->bytes_read_offset = 0; + prtd->buffer = ac->port[dir].buf[0].data; + prtd->buffer_paddr = ac->port[dir].buf[0].phys; + prtd->buffer_size = runtime->fragments * runtime->fragment_size; + + /* Bit-0 of flags represent timestamp mode */ + if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG) + prtd->ts_header_offset = sizeof(struct snd_codec_metadata); + else + prtd->ts_header_offset = 0; + + pr_debug("%s: sample_rate = %d channels = %d bps = %d sample_word_size = %d\n", + __func__, prtd->sample_rate, prtd->num_channels, + bits_per_sample, sample_word_size); + ret = q6asm_enc_cfg_blk_pcm_format_support_v3(prtd->audio_client, + prtd->sample_rate, prtd->num_channels, + bits_per_sample, sample_word_size); + + return ret; +} + +static int msm_compr_playback_open(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -1076,20 +1350,6 @@ static int msm_compr_open(struct snd_compr_stream *cstream) kfree(prtd); return -ENOMEM; } - prtd->audio_client = q6asm_audio_client_alloc( - (app_cb)compr_event_handler, prtd); - if (!prtd->audio_client) { - pr_err("%s: Could not allocate memory for client\n", __func__); - kfree(pdata->audio_effects[rtd->dai_link->be_id]); - kfree(pdata->dec_params[rtd->dai_link->be_id]); - pdata->cstream[rtd->dai_link->be_id] = NULL; - kfree(prtd); - return -ENOMEM; - } - - pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); - prtd->audio_client->perf_mode = false; - prtd->session_id = prtd->audio_client->session; prtd->codec = FORMAT_MP3; prtd->bytes_received = 0; prtd->bytes_sent = 0; @@ -1128,11 +1388,91 @@ static int msm_compr_open(struct snd_compr_stream *cstream) runtime->private_data = prtd; populate_codec_list(prtd); + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)compr_event_handler, prtd); + if (!prtd->audio_client) { + pr_err("%s: Could not allocate memory for client\n", __func__); + kfree(pdata->audio_effects[rtd->dai_link->be_id]); + kfree(pdata->dec_params[rtd->dai_link->be_id]); + pdata->cstream[rtd->dai_link->be_id] = NULL; + runtime->private_data = NULL; + kfree(prtd); + return -ENOMEM; + } + pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); + prtd->audio_client->perf_mode = false; + prtd->session_id = prtd->audio_client->session; return 0; } -static int msm_compr_free(struct snd_compr_stream *cstream) +static int msm_compr_capture_open(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_audio *prtd; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + + pr_debug("%s\n", __func__); + prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL); + if (prtd == NULL) { + pr_err("Failed to allocate memory for msm_compr_audio\n"); + return -ENOMEM; + } + + runtime->private_data = NULL; + prtd->cstream = cstream; + pdata->cstream[rtd->dai_link->be_id] = cstream; + + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)compr_event_handler, prtd); + if (!prtd->audio_client) { + pr_err("%s: Could not allocate memory for client\n", __func__); + pdata->cstream[rtd->dai_link->be_id] = NULL; + kfree(prtd); + return -ENOMEM; + } + pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); + prtd->audio_client->perf_mode = false; + prtd->session_id = prtd->audio_client->session; + prtd->codec = FORMAT_LINEAR_PCM; + prtd->bytes_copied = 0; + prtd->bytes_read = 0; + prtd->bytes_read_offset = 0; + prtd->received_total = 0; + prtd->byte_offset = 0; + prtd->sample_rate = 48000; + prtd->num_channels = 2; + prtd->first_buffer = 0; + + spin_lock_init(&prtd->lock); + + atomic_set(&prtd->eos, 0); + atomic_set(&prtd->start, 0); + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 0); + atomic_set(&prtd->close, 0); + atomic_set(&prtd->wait_on_close, 0); + atomic_set(&prtd->error, 0); + + runtime->private_data = prtd; + + return 0; +} + +static int msm_compr_open(struct snd_compr_stream *cstream) +{ + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_playback_open(cstream); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_capture_open(cstream); + return ret; +} + +static int msm_compr_playback_free(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime; struct msm_compr_audio *prtd; @@ -1227,6 +1567,76 @@ static int msm_compr_free(struct snd_compr_stream *cstream) return 0; } +static int msm_compr_capture_free(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime; + struct msm_compr_audio *prtd; + struct snd_soc_pcm_runtime *soc_prtd; + struct msm_compr_pdata *pdata; + struct audio_client *ac; + int dir = OUT, stream_id; + unsigned long flags; + uint32_t stream_index; + + if (!cstream) { + pr_err("%s cstream is null\n", __func__); + return 0; + } + runtime = cstream->runtime; + soc_prtd = cstream->private_data; + if (!runtime || !soc_prtd || !(soc_prtd->platform)) { + pr_err("%s runtime or soc_prtd or platform is null\n", + __func__); + return 0; + } + prtd = runtime->private_data; + if (!prtd) { + pr_err("%s prtd is null\n", __func__); + return 0; + } + pdata = snd_soc_platform_get_drvdata(soc_prtd->platform); + ac = prtd->audio_client; + if (!pdata || !ac) { + pr_err("%s pdata or ac is null\n", __func__); + return 0; + } + + spin_lock_irqsave(&prtd->lock, flags); + stream_id = ac->stream_id; + + stream_index = STREAM_ARRAY_INDEX(stream_id); + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0)) { + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug("close stream %d", stream_id); + q6asm_stream_cmd(ac, CMD_CLOSE, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } + spin_unlock_irqrestore(&prtd->lock, flags); + + pdata->cstream[soc_prtd->dai_link->be_id] = NULL; + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, + SNDRV_PCM_STREAM_CAPTURE); + + q6asm_audio_client_buf_free_contiguous(dir, ac); + + q6asm_audio_client_free(ac); + + kfree(prtd); + + return 0; +} + +static int msm_compr_free(struct snd_compr_stream *cstream) +{ + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_playback_free(cstream); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_capture_free(cstream); + return ret; +} + static bool msm_compr_validate_codec_compr(__u32 codec_id) { int32_t i; @@ -1257,8 +1667,14 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, if (i == num_rates) return -EINVAL; - if (prtd->codec_param.codec.compr_passthr >= 0 && - prtd->codec_param.codec.compr_passthr <= 2) + memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params)); + /* ToDo: remove duplicates */ + prtd->num_channels = prtd->codec_param.codec.ch_in; + prtd->sample_rate = prtd->codec_param.codec.sample_rate; + pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate); + + if (prtd->codec_param.codec.compr_passthr >= LEGACY_PCM && + prtd->codec_param.codec.compr_passthr <= COMPRESSED_PASSTHROUGH_DSD) prtd->compr_passthr = prtd->codec_param.codec.compr_passthr; else prtd->compr_passthr = LEGACY_PCM; @@ -1363,6 +1779,18 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, break; } + case SND_AUDIOCODEC_DTS: { + pr_debug("%s: SND_AUDIOCODEC_DTS\n", __func__); + prtd->codec = FORMAT_DTS; + break; + } + + case SND_AUDIOCODEC_DSD: { + pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__); + prtd->codec = FORMAT_DSD; + break; + } + default: pr_err("codec not supported, id =%d\n", params->codec.id); return -EINVAL; @@ -1374,13 +1802,10 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, prtd->partial_drain_delay = msm_compr_get_partial_drain_delay(frame_sz, prtd->sample_rate); - memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params)); - - /* ToDo: remove duplicates */ - prtd->num_channels = prtd->codec_param.codec.ch_in; - prtd->sample_rate = prtd->codec_param.codec.sample_rate; - pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate); - ret = msm_compr_configure_dsp(cstream); + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_configure_dsp_for_playback(cstream); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_configure_dsp_for_capture(cstream); return ret; } @@ -1471,11 +1896,6 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) uint32_t stream_index; uint16_t bits_per_sample = 16; - if (cstream->direction != SND_COMPRESS_PLAYBACK) { - pr_err("%s: Unsupported stream type\n", __func__); - return -EINVAL; - } - spin_lock_irqsave(&prtd->lock, flags); if (atomic_read(&prtd->error)) { pr_err("%s Got RESET EVENTS notification, return immediately", @@ -1490,17 +1910,30 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__); atomic_set(&prtd->start, 1); - /* set volume for the stream before RUN */ - rc = msm_compr_set_volume(cstream, volume[0], volume[1]); - if (rc) - pr_err("%s : Set Volume failed : %d\n", - __func__, rc); - - rc = msm_compr_init_pp_params(cstream, ac); - if (rc) - pr_err("%s : init PP params failed : %d\n", - __func__, rc); - + /* + * compr_set_volume and compr_init_pp_params + * are used to configure ASM volume hence not + * needed for compress passthrough playback. + * + * compress passthrough volume is controlled in + * ADM by adm_send_compressed_device_mute() + */ + if (prtd->compr_passthr == LEGACY_PCM && + cstream->direction == SND_COMPRESS_PLAYBACK) { + /* set volume for the stream before RUN */ + rc = msm_compr_set_volume(cstream, + volume[0], volume[1]); + if (rc) + pr_err("%s : Set Volume failed : %d\n", + __func__, rc); + + rc = msm_compr_init_pp_params(cstream, ac); + if (rc) + pr_err("%s : init PP params failed : %d\n", + __func__, rc); + } else { + msm_compr_read_buffer(prtd); + } /* issue RUN command for the stream */ q6asm_run_nowait(prtd->audio_client, 0, 0, 0); break; @@ -1510,6 +1943,18 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) prtd->gapless_state.gapless_transition); stream_id = ac->stream_id; atomic_set(&prtd->start, 0); + if (cstream->direction == SND_COMPRESS_CAPTURE) { + q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->xrun, 0); + prtd->received_total = 0; + prtd->bytes_copied = 0; + prtd->bytes_read = 0; + prtd->bytes_read_offset = 0; + prtd->byte_offset = 0; + prtd->app_pointer = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } if (prtd->next_stream) { pr_debug("%s: interrupt next track wait queues\n", __func__); @@ -1519,8 +1964,15 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) } if (atomic_read(&prtd->eos)) { pr_debug("%s: interrupt eos wait queues", __func__); - prtd->cmd_interrupt = 1; - wake_up(&prtd->eos_wait); + /* + * Gapless playback does not wait for eos, do not set + * cmd_int and do not wake up eos_wait during gapless + * transition + */ + if (!prtd->gapless_state.gapless_transition) { + prtd->cmd_interrupt = 1; + wake_up(&prtd->eos_wait); + } atomic_set(&prtd->eos, 0); } if (atomic_read(&prtd->drain)) { @@ -1629,7 +2081,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) if (prtd->last_buffer) { pr_debug("%s: last buffer drain\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); - if (rc) { + if (rc || !atomic_read(&prtd->start)) { spin_unlock_irqrestore(&prtd->lock, flags); break; @@ -1649,7 +2101,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) /* wait for the zero length buffer to be returned */ pr_debug("%s: zero length buffer drain\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); - if (rc) { + if (rc || !atomic_read(&prtd->start)) { spin_unlock_irqrestore(&prtd->lock, flags); break; } @@ -1683,7 +2135,12 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) prtd->app_pointer = 0; prtd->first_buffer = 1; prtd->last_buffer = 0; - prtd->gapless_state.gapless_transition = 1; + /* + * Set gapless transition flag only if EOS hasn't been + * acknowledged already. + */ + if (atomic_read(&prtd->eos)) + prtd->gapless_state.gapless_transition = 1; prtd->marker_timestamp = 0; /* @@ -1707,6 +2164,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) * stream can be used for gapless playback */ prtd->gapless_state.set_next_stream_id = false; + prtd->gapless_state.gapless_transition = 0; pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id); prtd->eos_ack = 0; @@ -1754,8 +2212,14 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) /* * Cache this time as last known time */ - q6asm_get_session_time(prtd->audio_client, - &prtd->marker_timestamp); + if (pdata->use_legacy_api) + q6asm_get_session_time_legacy( + prtd->audio_client, + &prtd->marker_timestamp); + else + q6asm_get_session_time(prtd->audio_client, + &prtd->marker_timestamp); + spin_lock_irqsave(&prtd->lock, flags); /* * Don't reset these as these vars map to @@ -1851,7 +2315,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: open_write stream_id %d bits_per_sample %d", __func__, stream_id, bits_per_sample); - rc = q6asm_stream_open_write_v2(prtd->audio_client, + rc = q6asm_stream_open_write_v4(prtd->audio_client, prtd->codec, bits_per_sample, stream_id, prtd->gapless_state.use_dsp_gapless_mode); @@ -1882,57 +2346,77 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) } static int msm_compr_pointer(struct snd_compr_stream *cstream, - struct snd_compr_tstamp *arg) + struct snd_compr_tstamp *arg) { struct snd_compr_runtime *runtime = cstream->runtime; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct msm_compr_audio *prtd = runtime->private_data; + struct msm_compr_pdata *pdata = NULL; struct snd_compr_tstamp tstamp; uint64_t timestamp = 0; int rc = 0, first_buffer; unsigned long flags; uint32_t gapless_transition; + pdata = snd_soc_platform_get_drvdata(rtd->platform); pr_debug("%s\n", __func__); memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp)); spin_lock_irqsave(&prtd->lock, flags); tstamp.sampling_rate = prtd->sample_rate; tstamp.byte_offset = prtd->byte_offset; - tstamp.copied_total = prtd->copied_total; + if (cstream->direction == SND_COMPRESS_PLAYBACK) + tstamp.copied_total = prtd->copied_total; + else if (cstream->direction == SND_COMPRESS_CAPTURE) + tstamp.copied_total = prtd->received_total; first_buffer = prtd->first_buffer; if (atomic_read(&prtd->error)) { - pr_err("%s Got RESET EVENTS notification, return error", + pr_err("%s Got RESET EVENTS notification, return error\n", __func__); + if (cstream->direction == SND_COMPRESS_PLAYBACK) + runtime->total_bytes_transferred = tstamp.copied_total; + else + runtime->total_bytes_available = tstamp.copied_total; tstamp.pcm_io_frames = 0; memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp)); spin_unlock_irqrestore(&prtd->lock, flags); return -ENETRESET; } + if (cstream->direction == SND_COMPRESS_PLAYBACK) { - gapless_transition = prtd->gapless_state.gapless_transition; - spin_unlock_irqrestore(&prtd->lock, flags); - - /* - * Query timestamp from DSP if some data is with it. - * This prevents timeouts. - */ - if (!first_buffer || gapless_transition) { + gapless_transition = prtd->gapless_state.gapless_transition; + spin_unlock_irqrestore(&prtd->lock, flags); if (gapless_transition) pr_debug("%s session time in gapless transition", - __func__); + __func__); + /* + *- Do not query if no buffer has been given. + *- Do not query on a gapless transition. + * Playback for the 2nd stream can start (thus returning time + * starting from 0) before the driver knows about EOS of first + * stream. + */ + if (!first_buffer || gapless_transition) { - rc = q6asm_get_session_time(prtd->audio_client, ×tamp); - if (rc < 0) { - pr_err("%s: Get Session Time return value =%lld\n", - __func__, timestamp); - if (atomic_read(&prtd->error)) - return -ENETRESET; + if (pdata->use_legacy_api) + rc = q6asm_get_session_time_legacy( + prtd->audio_client, &prtd->marker_timestamp); else - return -EAGAIN; + rc = q6asm_get_session_time( + prtd->audio_client, &prtd->marker_timestamp); + if (rc < 0) { + pr_err("%s: Get Session Time return =%lld\n", + __func__, timestamp); + if (atomic_read(&prtd->error)) + return -ENETRESET; + else + return -EAGAIN; + } } } else { - timestamp = prtd->marker_timestamp; + spin_unlock_irqrestore(&prtd->lock, flags); } + timestamp = prtd->marker_timestamp; /* DSP returns timestamp in usec */ pr_debug("%s: timestamp = %lld usec\n", __func__, timestamp); @@ -1991,8 +2475,8 @@ static int msm_compr_ack(struct snd_compr_stream *cstream, return 0; } -static int msm_compr_copy(struct snd_compr_stream *cstream, - char __user *buf, size_t count) +static int msm_compr_playback_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; @@ -2055,6 +2539,60 @@ static int msm_compr_copy(struct snd_compr_stream *cstream, return count; } +static int msm_compr_capture_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + void *source; + unsigned long flags; + + pr_debug("%s: count = %zd\n", __func__, count); + if (!prtd->buffer) { + pr_err("%s: Buffer is not allocated yet ??", __func__); + return 0; + } + + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification", __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return -ENETRESET; + } + + source = prtd->buffer + prtd->app_pointer; + /* check if we have requested amount of data to copy to user*/ + if (count <= prtd->received_total - prtd->bytes_copied) { + spin_unlock_irqrestore(&prtd->lock, flags); + if (copy_to_user(buf, source, count)) { + pr_err("copy_to_user failed"); + return -EFAULT; + } + spin_lock_irqsave(&prtd->lock, flags); + prtd->app_pointer += count; + if (prtd->app_pointer >= prtd->buffer_size) + prtd->app_pointer -= prtd->buffer_size; + prtd->bytes_copied += count; + } + msm_compr_read_buffer(prtd); + + spin_unlock_irqrestore(&prtd->lock, flags); + return count; +} + +static int msm_compr_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + int ret = 0; + + pr_debug(" In %s\n", __func__); + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_playback_copy(cstream, buf, count); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_capture_copy(cstream, buf, count); + return ret; +} + static int msm_compr_get_caps(struct snd_compr_stream *cstream, struct snd_compr_caps *arg) { @@ -2067,7 +2605,7 @@ static int msm_compr_get_caps(struct snd_compr_stream *cstream, memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps)); } else { ret = -EINVAL; - pr_err("%s: arg (0x%p), prtd (0x%p)\n", __func__, arg, prtd); + pr_err("%s: arg (0x%pK), prtd (0x%pK)\n", __func__, arg, prtd); } return ret; @@ -2123,6 +2661,10 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, break; case SND_AUDIOCODEC_APE: break; + case SND_AUDIOCODEC_DTS: + break; + case SND_AUDIOCODEC_DSD: + break; default: pr_err("%s: Unsupported audio codec %d\n", __func__, codec->codec); @@ -2600,6 +3142,8 @@ static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol, case FORMAT_VORBIS: case FORMAT_ALAC: case FORMAT_APE: + case FORMAT_DTS: + case FORMAT_DSD: pr_debug("%s: no runtime parameters for codec: %d\n", __func__, prtd->codec); break; @@ -2646,7 +3190,7 @@ static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol, return 0; } -static int msm_compr_app_type_cfg_put(struct snd_kcontrol *kcontrol, +static int msm_compr_playback_app_type_cfg_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u64 fe_id = kcontrol->private_value; @@ -2665,20 +3209,111 @@ static int msm_compr_app_type_cfg_put(struct snd_kcontrol *kcontrol, acdb_dev_id = ucontrol->value.integer.value[1]; if (ucontrol->value.integer.value[2] != 0) sample_rate = ucontrol->value.integer.value[2]; - pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d\n", - __func__, app_type, acdb_dev_id, sample_rate); + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n", + __func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_RX); msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, - acdb_dev_id, sample_rate); + acdb_dev_id, sample_rate, SESSION_TYPE_RX); return 0; } -static int msm_compr_app_type_cfg_get(struct snd_kcontrol *kcontrol, +static int msm_compr_playback_app_type_cfg_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + u64 fe_id = kcontrol->private_value; + int ret = 0; + int app_type; + int acdb_dev_id; + int sample_rate; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_RX, + &app_type, &acdb_dev_id, &sample_rate); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = app_type; + ucontrol->value.integer.value[1] = acdb_dev_id; + ucontrol->value.integer.value[2] = sample_rate; + pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, SESSION_TYPE_RX, + app_type, acdb_dev_id, sample_rate); +done: + return ret; +} + +static int msm_compr_capture_app_type_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int app_type; + int acdb_dev_id; + int sample_rate = 48000; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + return -EINVAL; + } + + app_type = ucontrol->value.integer.value[0]; + acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n", + __func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_TX); + msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, + acdb_dev_id, sample_rate, SESSION_TYPE_TX); + return 0; } +static int msm_compr_capture_app_type_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int ret = 0; + int app_type; + int acdb_dev_id; + int sample_rate; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_TX, + &app_type, &acdb_dev_id, &sample_rate); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = app_type; + ucontrol->value.integer.value[1] = acdb_dev_id; + ucontrol->value.integer.value[2] = sample_rate; + pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, SESSION_TYPE_TX, + app_type, acdb_dev_id, sample_rate); +done: + return ret; +} + static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2773,6 +3408,8 @@ static int msm_compr_probe(struct snd_soc_platform *platform) { struct msm_compr_pdata *pdata; int i; + int rc; + const char *qdsp_version; pr_debug("%s\n", __func__); pdata = (struct msm_compr_pdata *) @@ -2794,6 +3431,17 @@ static int msm_compr_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, msm_compr_gapless_controls, ARRAY_SIZE(msm_compr_gapless_controls)); + rc = of_property_read_string(platform->dev->of_node, + "qcom,adsp-version", &qdsp_version); + if (!rc) { + if (!strcmp(qdsp_version, "MDSP 1.2")) + pdata->use_legacy_api = true; + else + pdata->use_legacy_api = false; + } else + pdata->use_legacy_api = false; + + pr_debug("%s: use legacy api %d\n", __func__, pdata->use_legacy_api); /* * use_dsp_gapless_mode part of platform data(pdata) is updated from HAL * through a mixer control before compress driver is opened. The mixer @@ -2818,7 +3466,7 @@ static int msm_compr_audio_effects_config_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 128; + uinfo->count = MAX_PP_PARAMS_SZ; uinfo->value.integer.min = 0; uinfo->value.integer.max = 0xFFFFFFFF; return 0; @@ -3049,7 +3697,8 @@ static int msm_compr_add_dec_runtime_params_control( static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) { - const char *mixer_ctl_name = "Audio Stream"; + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; const char *deviceNo = "NN"; const char *suffix = "App Type Cfg"; char *mixer_str = NULL; @@ -3060,8 +3709,8 @@ static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) .name = "?", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = msm_compr_app_type_cfg_info, - .put = msm_compr_app_type_cfg_put, - .get = msm_compr_app_type_cfg_get, + .put = msm_compr_playback_app_type_cfg_put, + .get = msm_compr_playback_app_type_cfg_get, .private_value = 0, } }; @@ -3072,24 +3721,45 @@ static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) } pr_debug("%s: added new compr FE ctl with name %s, id %d, cpu dai %s, device no %d\n", - __func__, rtd->dai_link->name, rtd->dai_link->be_id, - rtd->dai_link->cpu_dai_name, rtd->pcm->device); + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) + ctl_len = strlen(playback_mixer_ctl_name) + 1 + strlen(deviceNo) + + 1 + strlen(suffix) + 1; + else + ctl_len = strlen(capture_mixer_ctl_name) + 1 + strlen(deviceNo) + + 1 + strlen(suffix) + 1; - ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + - strlen(suffix) + 1; mixer_str = kzalloc(ctl_len, GFP_KERNEL); if (!mixer_str) return 0; - snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, - rtd->pcm->device, suffix); + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) + snprintf(mixer_str, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + else + snprintf(mixer_str, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + fe_app_type_cfg_control[0].name = mixer_str; fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id; + + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) { + fe_app_type_cfg_control[0].put = + msm_compr_playback_app_type_cfg_put; + fe_app_type_cfg_control[0].get = + msm_compr_playback_app_type_cfg_get; + } else { + fe_app_type_cfg_control[0].put = + msm_compr_capture_app_type_cfg_put; + fe_app_type_cfg_control[0].get = + msm_compr_capture_app_type_cfg_get; + } pr_debug("Registering new mixer ctl %s", mixer_str); snd_soc_add_platform_controls(rtd->platform, - fe_app_type_cfg_control, - ARRAY_SIZE(fe_app_type_cfg_control)); + fe_app_type_cfg_control, + ARRAY_SIZE(fe_app_type_cfg_control)); kfree(mixer_str); return 0; } diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c new file mode 100644 index 000000000000..d4118b0dd2de --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c @@ -0,0 +1,488 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HDMI_RX_CA_MAX 0x32 + +enum { + STATUS_PORT_STARTED, /* track if AFE port has started */ + STATUS_MAX +}; + +struct msm_ext_disp_ca { + bool set_ca; + u32 ca; +}; + +struct msm_dai_q6_hdmi_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + u32 rate; + u32 channels; + struct msm_ext_disp_ca ca; + union afe_port_config port_config; +}; + +static int msm_dai_q6_ext_disp_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + dai_data->port_config.hdmi_multi_ch.datatype = value; + pr_debug("%s: value = %d\n", __func__, value); + + return 0; +} + +static int msm_dai_q6_ext_disp_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + dai_data->port_config.hdmi_multi_ch.datatype; + pr_debug("%s: value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dai_q6_ext_disp_ca_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + dai_data->ca.ca = ucontrol->value.integer.value[0]; + dai_data->ca.set_ca = true; + pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca); + return 0; +} + +static int msm_dai_q6_ext_disp_ca_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = dai_data->ca.ca; + pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca); + return 0; +} + +/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command + * 0: linear PCM + * 1: non-linear PCM + */ +static const char * const hdmi_format[] = { + "LPCM", + "Compr" +}; + +static const struct soc_enum hdmi_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, hdmi_format), +}; + +static const struct snd_kcontrol_new hdmi_config_controls[] = { + SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0], + msm_dai_q6_ext_disp_format_get, + msm_dai_q6_ext_disp_format_put), + SOC_SINGLE_MULTI_EXT("HDMI RX CA", SND_SOC_NOPM, 0, + HDMI_RX_CA_MAX, 0, 1, + msm_dai_q6_ext_disp_ca_get, + msm_dai_q6_ext_disp_ca_put), +}; + +static const struct snd_kcontrol_new display_port_config_controls[] = { + SOC_ENUM_EXT("Display Port RX Format", hdmi_config_enum[0], + msm_dai_q6_ext_disp_format_get, + msm_dai_q6_ext_disp_format_put), + SOC_SINGLE_MULTI_EXT("Display Port RX CA", SND_SOC_NOPM, 0, + HDMI_RX_CA_MAX, 0, 1, + msm_dai_q6_ext_disp_ca_get, + msm_dai_q6_ext_disp_ca_put), +}; + +/* Current implementation assumes hw_param is called once + * This may not be the case but what to do when ADM and AFE + * port are already opened and parameter changes + */ +static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + dai_data->port_config.hdmi_multi_ch.reserved = 0; + dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1; + dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->port_config.hdmi_multi_ch.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dai_data->port_config.hdmi_multi_ch.bit_width = 24; + break; + } + + /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/ + switch (dai_data->channels) { + case 2: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0; + break; + case 3: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x02; + break; + case 4: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x06; + break; + case 5: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0A; + break; + case 6: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0B; + break; + case 7: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x12; + break; + case 8: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x13; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", + dai_data->channels); + return -EINVAL; + } + dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u\n" + "num_ch = %u channel_allocation = %u datatype = %d\n", __func__, + dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version, + dai_data->port_config.hdmi_multi_ch.sample_rate, + dai_data->port_config.hdmi_multi_ch.bit_width, + dai_data->channels, + dai_data->port_config.hdmi_multi_ch.channel_allocation, + dai_data->port_config.hdmi_multi_ch.datatype); + + return 0; +} + + +static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_info("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + return; + } + + rc = afe_close(dai->id); /* can block */ + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + + pr_debug("%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); +} + + +static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (dai_data->ca.set_ca) + dai_data->port_config.hdmi_multi_ch.channel_allocation = + dai_data->ca.ca; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_port_start(dai->id, &dai_data->port_config, + dai_data->rate); + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to open AFE port %x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + + return rc; +} + +static inline void msm_dai_q6_hdmi_set_dai_id(struct snd_soc_dai *dai) +{ + if (!dai->driver->id) { + dev_warn(dai->dev, "DAI driver id is not set\n"); + return; + } + dai->id = dai->driver->id; +} + +static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data; + const struct snd_kcontrol_new *kcontrol; + int rc = 0; + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai || !dai->driver) { + pr_err("%s: dai or dai->driver is NULL\n", __func__); + return -EINVAL; + } + dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data), + GFP_KERNEL); + + if (!dai_data) { + dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n", + dai->id); + rc = -ENOMEM; + } else + dev_set_drvdata(dai->dev, dai_data); + + msm_dai_q6_hdmi_set_dai_id(dai); + + if (dai->driver->id == HDMI_RX) { + kcontrol = &hdmi_config_controls[0]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &hdmi_config_controls[1]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + } else if (dai->driver->id == DISPLAY_PORT_RX) { + kcontrol = &display_port_config_controls[0]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &display_port_config_controls[1]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + } else { + dev_err(dai->dev, "%s: Invalid id:%d\n", + __func__, dai->driver->id); + kfree(dai_data); + dev_set_drvdata(dai->dev, NULL); + return -EINVAL; + } + + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (!rc) { + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + } + return rc; +} + +static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data; + int rc; + + dai_data = dev_get_drvdata(dai->dev); + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(dai->id); /* can block */ + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + kfree(dai_data); + + return 0; +} + +static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = { + .prepare = msm_dai_q6_hdmi_prepare, + .hw_params = msm_dai_q6_hdmi_hw_params, + .shutdown = msm_dai_q6_hdmi_shutdown, +}; + +static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = { + .playback = { + .stream_name = "HDMI Playback", + .aif_name = "HDMI", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &msm_dai_q6_hdmi_ops, + .id = HDMI_RX, + .probe = msm_dai_q6_hdmi_dai_probe, + .remove = msm_dai_q6_hdmi_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = { + { + .playback = { + .stream_name = "Display Port Playback", + .aif_name = "DISPLAY_PORT", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &msm_dai_q6_hdmi_ops, + .id = DISPLAY_PORT_RX, + .probe = msm_dai_q6_hdmi_dai_probe, + .remove = msm_dai_q6_hdmi_dai_remove, + }, +}; + +static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = { + .name = "msm-dai-q6-hdmi", +}; + +/* To do: change to register DAIs as batch */ +static int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev) +{ + int rc, id; + const char *q6_dev_id = "qcom,msm-dai-q6-dev-id"; + + rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, q6_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (pdev->id) { + case HDMI_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_hdmi_q6_component, + &msm_dai_q6_hdmi_hdmi_rx_dai, 1); + break; + case DISPLAY_PORT_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_hdmi_q6_component, + &msm_dai_q6_display_port_rx_dai[0], + ARRAY_SIZE(msm_dai_q6_display_port_rx_dai)); + break; + default: + dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id); + rc = -ENODEV; + break; + } + return rc; +} + +static int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = { + {.compatible = "qcom,msm-dai-q6-hdmi"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match); + +static struct platform_driver msm_dai_q6_hdmi_driver = { + .probe = msm_dai_q6_hdmi_dev_probe, + .remove = msm_dai_q6_hdmi_dev_remove, + .driver = { + .name = "msm-dai-q6-hdmi", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_hdmi_dt_match, + }, +}; + +static int __init msm_dai_q6_hdmi_init(void) +{ + return platform_driver_register(&msm_dai_q6_hdmi_driver); +} +module_init(msm_dai_q6_hdmi_init); + +static void __exit msm_dai_q6_hdmi_exit(void) +{ + platform_driver_unregister(&msm_dai_q6_hdmi_driver); +} +module_exit(msm_dai_q6_hdmi_exit); + +/* Module information */ +MODULE_DESCRIPTION("MSM DSP HDMI DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c new file mode 100644 index 000000000000..e3e2e7e0816a --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -0,0 +1,7895 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1 +#define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2 +#define MSM_DAI_TERT_AUXPCM_DT_DEV_ID 3 +#define MSM_DAI_QUAT_AUXPCM_DT_DEV_ID 4 + + +#define spdif_clock_value(rate) (2*rate*32*2) +#define CHANNEL_STATUS_SIZE 24 +#define CHANNEL_STATUS_MASK_INIT 0x0 +#define CHANNEL_STATUS_MASK 0x4 +#define AFE_API_VERSION_CLOCK_SET 1 + +#define DAI_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +enum { + ENC_FMT_NONE, + ENC_FMT_SBC = ASM_MEDIA_FMT_SBC, + ENC_FMT_AAC_V2 = ASM_MEDIA_FMT_AAC_V2, + ENC_FMT_APTX = ASM_MEDIA_FMT_APTX, + ENC_FMT_APTX_HD = ASM_MEDIA_FMT_APTX_HD, +}; + +static const struct afe_clk_set lpass_clk_set_default = { + AFE_API_VERSION_CLOCK_SET, + Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT, + Q6AFE_LPASS_OSR_CLK_2_P048_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static const struct afe_clk_cfg lpass_clk_cfg_default = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_OSR_CLK_2_P048_MHZ, + 0, + Q6AFE_LPASS_CLK_SRC_INTERNAL, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + Q6AFE_LPASS_MODE_CLK1_VALID, + 0, +}; +enum { + STATUS_PORT_STARTED, /* track if AFE port has started */ + /* track AFE Tx port status for bi-directional transfers */ + STATUS_TX_PORT, + /* track AFE Rx port status for bi-directional transfers */ + STATUS_RX_PORT, + STATUS_MAX +}; + +enum { + RATE_8KHZ, + RATE_16KHZ, + RATE_MAX_NUM_OF_AUX_PCM_RATES, +}; + +enum { + IDX_PRIMARY_TDM_RX_0, + IDX_PRIMARY_TDM_RX_1, + IDX_PRIMARY_TDM_RX_2, + IDX_PRIMARY_TDM_RX_3, + IDX_PRIMARY_TDM_RX_4, + IDX_PRIMARY_TDM_RX_5, + IDX_PRIMARY_TDM_RX_6, + IDX_PRIMARY_TDM_RX_7, + IDX_PRIMARY_TDM_TX_0, + IDX_PRIMARY_TDM_TX_1, + IDX_PRIMARY_TDM_TX_2, + IDX_PRIMARY_TDM_TX_3, + IDX_PRIMARY_TDM_TX_4, + IDX_PRIMARY_TDM_TX_5, + IDX_PRIMARY_TDM_TX_6, + IDX_PRIMARY_TDM_TX_7, + IDX_SECONDARY_TDM_RX_0, + IDX_SECONDARY_TDM_RX_1, + IDX_SECONDARY_TDM_RX_2, + IDX_SECONDARY_TDM_RX_3, + IDX_SECONDARY_TDM_RX_4, + IDX_SECONDARY_TDM_RX_5, + IDX_SECONDARY_TDM_RX_6, + IDX_SECONDARY_TDM_RX_7, + IDX_SECONDARY_TDM_TX_0, + IDX_SECONDARY_TDM_TX_1, + IDX_SECONDARY_TDM_TX_2, + IDX_SECONDARY_TDM_TX_3, + IDX_SECONDARY_TDM_TX_4, + IDX_SECONDARY_TDM_TX_5, + IDX_SECONDARY_TDM_TX_6, + IDX_SECONDARY_TDM_TX_7, + IDX_TERTIARY_TDM_RX_0, + IDX_TERTIARY_TDM_RX_1, + IDX_TERTIARY_TDM_RX_2, + IDX_TERTIARY_TDM_RX_3, + IDX_TERTIARY_TDM_RX_4, + IDX_TERTIARY_TDM_RX_5, + IDX_TERTIARY_TDM_RX_6, + IDX_TERTIARY_TDM_RX_7, + IDX_TERTIARY_TDM_TX_0, + IDX_TERTIARY_TDM_TX_1, + IDX_TERTIARY_TDM_TX_2, + IDX_TERTIARY_TDM_TX_3, + IDX_TERTIARY_TDM_TX_4, + IDX_TERTIARY_TDM_TX_5, + IDX_TERTIARY_TDM_TX_6, + IDX_TERTIARY_TDM_TX_7, + IDX_QUATERNARY_TDM_RX_0, + IDX_QUATERNARY_TDM_RX_1, + IDX_QUATERNARY_TDM_RX_2, + IDX_QUATERNARY_TDM_RX_3, + IDX_QUATERNARY_TDM_RX_4, + IDX_QUATERNARY_TDM_RX_5, + IDX_QUATERNARY_TDM_RX_6, + IDX_QUATERNARY_TDM_RX_7, + IDX_QUATERNARY_TDM_TX_0, + IDX_QUATERNARY_TDM_TX_1, + IDX_QUATERNARY_TDM_TX_2, + IDX_QUATERNARY_TDM_TX_3, + IDX_QUATERNARY_TDM_TX_4, + IDX_QUATERNARY_TDM_TX_5, + IDX_QUATERNARY_TDM_TX_6, + IDX_QUATERNARY_TDM_TX_7, + IDX_TDM_MAX, +}; + +enum { + IDX_GROUP_PRIMARY_TDM_RX, + IDX_GROUP_PRIMARY_TDM_TX, + IDX_GROUP_SECONDARY_TDM_RX, + IDX_GROUP_SECONDARY_TDM_TX, + IDX_GROUP_TERTIARY_TDM_RX, + IDX_GROUP_TERTIARY_TDM_TX, + IDX_GROUP_QUATERNARY_TDM_RX, + IDX_GROUP_QUATERNARY_TDM_TX, + IDX_GROUP_TDM_MAX, +}; + +struct msm_dai_q6_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + DECLARE_BITMAP(hwfree_status, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + u32 cal_mode; + u32 afe_in_channels; + u16 afe_in_bitformat; + struct afe_enc_config enc_config; + union afe_port_config port_config; +}; + +struct msm_dai_q6_spdif_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + struct afe_spdif_port_config spdif_port; +}; + +struct msm_dai_q6_mi2s_dai_config { + u16 pdata_mi2s_lines; + struct msm_dai_q6_dai_data mi2s_dai_data; +}; + +struct msm_dai_q6_mi2s_dai_data { + struct msm_dai_q6_mi2s_dai_config tx_dai; + struct msm_dai_q6_mi2s_dai_config rx_dai; +}; + +struct msm_dai_q6_auxpcm_dai_data { + /* BITMAP to track Rx and Tx port usage count */ + DECLARE_BITMAP(auxpcm_port_status, STATUS_MAX); + struct mutex rlock; /* auxpcm dev resource lock */ + u16 rx_pid; /* AUXPCM RX AFE port ID */ + u16 tx_pid; /* AUXPCM TX AFE port ID */ + u16 afe_clk_ver; + struct afe_clk_cfg clk_cfg; /* hold LPASS clock configuration */ + struct afe_clk_set clk_set; /* hold LPASS clock configuration */ + struct msm_dai_q6_dai_data bdai_data; /* incoporate base DAI data */ +}; + +struct msm_dai_q6_tdm_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + struct afe_clk_set clk_set; /* hold LPASS clock config. */ + union afe_port_group_config group_cfg; /* hold tdm group config */ + struct afe_tdm_port_config port_cfg; /* hold tdm config */ +}; + +/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command + * 0: linear PCM + * 1: non-linear PCM + * 2: PCM data in IEC 60968 container + * 3: compressed data in IEC 60958 container + */ +static const char *const mi2s_format[] = { + "LPCM", + "Compr", + "LPCM-60958", + "Compr-60958" +}; + +static const struct soc_enum mi2s_config_enum[] = { + SOC_ENUM_SINGLE_EXT(4, mi2s_format), +}; + +static const char *const sb_format[] = { + "UNPACKED", + "PACKED_16B", + "DSD_DOP", +}; + +static const struct soc_enum sb_config_enum[] = { + SOC_ENUM_SINGLE_EXT(3, sb_format), +}; + +static const char *const tdm_data_format[] = { + "LPCM", + "Compr", +}; + +static const char *const tdm_header_type[] = { + "Invalid", + "Default", + "Entertainment", +}; + +static const struct soc_enum tdm_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, tdm_data_format), + SOC_ENUM_SINGLE_EXT(3, tdm_header_type), +}; + +static DEFINE_MUTEX(tdm_mutex); + +static atomic_t tdm_group_ref[IDX_GROUP_TDM_MAX]; + +/* cache of group cfg per parent node */ +static struct afe_param_id_group_device_tdm_cfg tdm_group_cfg = { + AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG, + AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX, + 0, + {AFE_PORT_ID_QUATERNARY_TDM_RX, + AFE_PORT_ID_QUATERNARY_TDM_RX_1, + AFE_PORT_ID_QUATERNARY_TDM_RX_2, + AFE_PORT_ID_QUATERNARY_TDM_RX_3, + AFE_PORT_ID_QUATERNARY_TDM_RX_4, + AFE_PORT_ID_QUATERNARY_TDM_RX_5, + AFE_PORT_ID_QUATERNARY_TDM_RX_6, + AFE_PORT_ID_QUATERNARY_TDM_RX_7}, + 8, + 48000, + 32, + 8, + 32, + 0xFF, +}; + +static struct afe_clk_set tdm_clk_set = { + AFE_API_VERSION_CLOCK_SET, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT, + Q6AFE_LPASS_IBIT_CLK_DISABLE, + Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +int msm_dai_q6_get_group_idx(u16 id) +{ + switch (id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_GROUP_PRIMARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_GROUP_PRIMARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_GROUP_SECONDARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_GROUP_SECONDARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_GROUP_TERTIARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_GROUP_TERTIARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_GROUP_QUATERNARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_GROUP_QUATERNARY_TDM_TX; + default: return -EINVAL; + } +} + +int msm_dai_q6_get_port_idx(u16 id) +{ + switch (id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + return IDX_PRIMARY_TDM_RX_0; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return IDX_PRIMARY_TDM_TX_0; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return IDX_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return IDX_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return IDX_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return IDX_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return IDX_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return IDX_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return IDX_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return IDX_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return IDX_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return IDX_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return IDX_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return IDX_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return IDX_SECONDARY_TDM_RX_0; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return IDX_SECONDARY_TDM_TX_0; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return IDX_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return IDX_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return IDX_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return IDX_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return IDX_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return IDX_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return IDX_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return IDX_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return IDX_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return IDX_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return IDX_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return IDX_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return IDX_TERTIARY_TDM_RX_0; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return IDX_TERTIARY_TDM_TX_0; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return IDX_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return IDX_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return IDX_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return IDX_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return IDX_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return IDX_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return IDX_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return IDX_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return IDX_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return IDX_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return IDX_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return IDX_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return IDX_QUATERNARY_TDM_RX_0; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return IDX_QUATERNARY_TDM_TX_0; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return IDX_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return IDX_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return IDX_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return IDX_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return IDX_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return IDX_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return IDX_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return IDX_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return IDX_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return IDX_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return IDX_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return IDX_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_QUATERNARY_TDM_TX_7; + default: return -EINVAL; + } +} + +static u16 msm_dai_q6_max_num_slot(int frame_rate) +{ + /* Max num of slots is bits per frame divided + * by bits per sample which is 16 + */ + switch (frame_rate) { + case AFE_PORT_PCM_BITS_PER_FRAME_8: + return 0; + case AFE_PORT_PCM_BITS_PER_FRAME_16: + return 1; + case AFE_PORT_PCM_BITS_PER_FRAME_32: + return 2; + case AFE_PORT_PCM_BITS_PER_FRAME_64: + return 4; + case AFE_PORT_PCM_BITS_PER_FRAME_128: + return 8; + case AFE_PORT_PCM_BITS_PER_FRAME_256: + return 16; + default: + pr_err("%s Invalid bits per frame %d\n", + __func__, frame_rate); + return 0; + } +} + +static int msm_dai_q6_dai_add_route(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->driver) { + pr_err("%s: Invalid params dai driver\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + return 0; +} + +static int msm_dai_q6_auxpcm_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data; + struct msm_dai_auxpcm_pdata *auxpcm_pdata = + (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data; + int rc = 0, slot_mapping_copy_len = 0; + + if (params_channels(params) != 1 || (params_rate(params) != 8000 && + params_rate(params) != 16000)) { + dev_err(dai->dev, "%s: invalid param chan %d rate %d\n", + __func__, params_channels(params), params_rate(params)); + return -EINVAL; + } + + mutex_lock(&aux_dai_data->rlock); + + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + /* AUXPCM DAI in use */ + if (dai_data->rate != params_rate(params)) { + dev_err(dai->dev, "%s: rate mismatch of running DAI\n", + __func__); + rc = -EINVAL; + } + mutex_unlock(&aux_dai_data->rlock); + return rc; + } + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + if (dai_data->rate == 8000) { + dai_data->port_config.pcm.pcm_cfg_minor_version = + AFE_API_VERSION_PCM_CONFIG; + dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode_8k.mode; + dai_data->port_config.pcm.sync_src = auxpcm_pdata->mode_8k.sync; + dai_data->port_config.pcm.frame_setting = + auxpcm_pdata->mode_8k.frame; + dai_data->port_config.pcm.quantype = + auxpcm_pdata->mode_8k.quant; + dai_data->port_config.pcm.ctrl_data_out_enable = + auxpcm_pdata->mode_8k.data; + dai_data->port_config.pcm.sample_rate = dai_data->rate; + dai_data->port_config.pcm.num_channels = dai_data->channels; + dai_data->port_config.pcm.bit_width = 16; + if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <= + auxpcm_pdata->mode_8k.num_slots) + slot_mapping_copy_len = + ARRAY_SIZE( + dai_data->port_config.pcm.slot_number_mapping) + * sizeof(uint16_t); + else + slot_mapping_copy_len = auxpcm_pdata->mode_8k.num_slots + * sizeof(uint16_t); + + if (auxpcm_pdata->mode_8k.slot_mapping) { + memcpy(dai_data->port_config.pcm.slot_number_mapping, + auxpcm_pdata->mode_8k.slot_mapping, + slot_mapping_copy_len); + } else { + dev_err(dai->dev, "%s 8khz slot mapping is NULL\n", + __func__); + mutex_unlock(&aux_dai_data->rlock); + return -EINVAL; + } + } else { + dai_data->port_config.pcm.pcm_cfg_minor_version = + AFE_API_VERSION_PCM_CONFIG; + dai_data->port_config.pcm.aux_mode = + auxpcm_pdata->mode_16k.mode; + dai_data->port_config.pcm.sync_src = + auxpcm_pdata->mode_16k.sync; + dai_data->port_config.pcm.frame_setting = + auxpcm_pdata->mode_16k.frame; + dai_data->port_config.pcm.quantype = + auxpcm_pdata->mode_16k.quant; + dai_data->port_config.pcm.ctrl_data_out_enable = + auxpcm_pdata->mode_16k.data; + dai_data->port_config.pcm.sample_rate = dai_data->rate; + dai_data->port_config.pcm.num_channels = dai_data->channels; + dai_data->port_config.pcm.bit_width = 16; + if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <= + auxpcm_pdata->mode_16k.num_slots) + slot_mapping_copy_len = + ARRAY_SIZE( + dai_data->port_config.pcm.slot_number_mapping) + * sizeof(uint16_t); + else + slot_mapping_copy_len = auxpcm_pdata->mode_16k.num_slots + * sizeof(uint16_t); + + if (auxpcm_pdata->mode_16k.slot_mapping) { + memcpy(dai_data->port_config.pcm.slot_number_mapping, + auxpcm_pdata->mode_16k.slot_mapping, + slot_mapping_copy_len); + } else { + dev_err(dai->dev, "%s 16khz slot mapping is NULL\n", + __func__); + mutex_unlock(&aux_dai_data->rlock); + return -EINVAL; + } + } + + dev_dbg(dai->dev, "%s: aux_mode 0x%x sync_src 0x%x frame_setting 0x%x\n", + __func__, dai_data->port_config.pcm.aux_mode, + dai_data->port_config.pcm.sync_src, + dai_data->port_config.pcm.frame_setting); + dev_dbg(dai->dev, "%s: qtype 0x%x dout 0x%x num_map[0] 0x%x\n" + "num_map[1] 0x%x num_map[2] 0x%x num_map[3] 0x%x\n", + __func__, dai_data->port_config.pcm.quantype, + dai_data->port_config.pcm.ctrl_data_out_enable, + dai_data->port_config.pcm.slot_number_mapping[0], + dai_data->port_config.pcm.slot_number_mapping[1], + dai_data->port_config.pcm.slot_number_mapping[2], + dai_data->port_config.pcm.slot_number_mapping[3]); + + mutex_unlock(&aux_dai_data->rlock); + return rc; +} + +static int msm_dai_q6_auxpcm_set_clk( + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data, + u16 port_id, bool enable) +{ + int rc; + + pr_debug("%s: afe_clk_ver: %d, port_id: %d, enable: %d\n", __func__, + aux_dai_data->afe_clk_ver, port_id, enable); + if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) { + aux_dai_data->clk_set.enable = enable; + rc = afe_set_lpass_clock_v2(port_id, + &aux_dai_data->clk_set); + } else { + if (!enable) + aux_dai_data->clk_cfg.clk_val1 = 0; + rc = afe_set_lpass_clock(port_id, + &aux_dai_data->clk_cfg); + } + return rc; +} + +static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data = + dev_get_drvdata(dai->dev); + + mutex_lock(&aux_dai_data->rlock); + + if (!(test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))) { + dev_dbg(dai->dev, "%s(): dai->id %d PCM ports already closed\n", + __func__, dai->id); + goto exit; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status)) + clear_bit(STATUS_TX_PORT, + aux_dai_data->auxpcm_port_status); + else { + dev_dbg(dai->dev, "%s: PCM_TX port already closed\n", + __func__); + goto exit; + } + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) + clear_bit(STATUS_RX_PORT, + aux_dai_data->auxpcm_port_status); + else { + dev_dbg(dai->dev, "%s: PCM_RX port already closed\n", + __func__); + goto exit; + } + } + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: cannot shutdown PCM ports\n", + __func__); + goto exit; + } + + dev_dbg(dai->dev, "%s: dai->id = %d closing PCM AFE ports\n", + __func__, dai->id); + + rc = afe_close(aux_dai_data->rx_pid); /* can block */ + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close PCM_RX AFE port\n"); + + rc = afe_close(aux_dai_data->tx_pid); + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AUX PCM TX port\n"); + + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false); + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false); +exit: + mutex_unlock(&aux_dai_data->rlock); +} + +static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data; + struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL; + int rc = 0; + u32 pcm_clk_rate; + + auxpcm_pdata = dai->dev->platform_data; + mutex_lock(&aux_dai_data->rlock); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (test_bit(STATUS_TX_PORT, + aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: PCM_TX port already ON\n", + __func__); + goto exit; + } else + set_bit(STATUS_TX_PORT, + aux_dai_data->auxpcm_port_status); + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (test_bit(STATUS_RX_PORT, + aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: PCM_RX port already ON\n", + __func__); + goto exit; + } else + set_bit(STATUS_RX_PORT, + aux_dai_data->auxpcm_port_status); + } + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) && + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: PCM ports already set\n", __func__); + goto exit; + } + + dev_dbg(dai->dev, "%s: dai->id:%d opening afe ports\n", + __func__, dai->id); + + rc = afe_q6_interface_prepare(); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "fail to open AFE APR\n"); + goto fail; + } + + /* + * For AUX PCM Interface the below sequence of clk + * settings and afe_open is a strict requirement. + * + * Also using afe_open instead of afe_port_start_nowait + * to make sure the port is open before deasserting the + * clock line. This is required because pcm register is + * not written before clock deassert. Hence the hw does + * not get updated with new setting if the below clock + * assert/deasset and afe_open sequence is not followed. + */ + + if (dai_data->rate == 8000) { + pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate; + } else if (dai_data->rate == 16000) { + pcm_clk_rate = (auxpcm_pdata->mode_16k.pcm_clk_rate); + } else { + dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__, + dai_data->rate); + rc = -EINVAL; + goto fail; + } + if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) { + memcpy(&aux_dai_data->clk_set, &lpass_clk_set_default, + sizeof(struct afe_clk_set)); + aux_dai_data->clk_set.clk_freq_in_hz = pcm_clk_rate; + + switch (dai->id) { + case MSM_DAI_PRI_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT; + break; + case MSM_DAI_SEC_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT; + break; + case MSM_DAI_TERT_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT; + break; + case MSM_DAI_QUAT_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT; + break; + default: + dev_err(dai->dev, "%s: AUXPCM id: %d not supported\n", + __func__, dai->id); + break; + } + } else { + memcpy(&aux_dai_data->clk_cfg, &lpass_clk_cfg_default, + sizeof(struct afe_clk_cfg)); + aux_dai_data->clk_cfg.clk_val1 = pcm_clk_rate; + } + + rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data, + aux_dai_data->rx_pid, true); + if (rc < 0) { + dev_err(dai->dev, + "%s:afe_set_lpass_clock on RX pcm_src_clk failed\n", + __func__); + goto fail; + } + + rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data, + aux_dai_data->tx_pid, true); + if (rc < 0) { + dev_err(dai->dev, + "%s:afe_set_lpass_clock on TX pcm_src_clk failed\n", + __func__); + goto fail; + } + + afe_open(aux_dai_data->rx_pid, &dai_data->port_config, dai_data->rate); + afe_open(aux_dai_data->tx_pid, &dai_data->port_config, dai_data->rate); + goto exit; + +fail: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status); + else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status); + +exit: + mutex_unlock(&aux_dai_data->rlock); + return rc; +} + +static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + int rc = 0; + + pr_debug("%s:port:%d cmd:%d\n", + __func__, dai->id, cmd); + + switch (cmd) { + + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* afe_open will be called from prepare */ + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + return 0; + + default: + pr_err("%s: cmd %d\n", __func__, cmd); + rc = -EINVAL; + } + + return rc; + +} + +static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data; + int rc; + + aux_dai_data = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: dai->id %d closing afe\n", + __func__, dai->id); + + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + rc = afe_close(aux_dai_data->rx_pid); /* can block */ + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AUXPCM RX AFE port\n"); + rc = afe_close(aux_dai_data->tx_pid); + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AUXPCM TX AFE port\n"); + clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status); + clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status); + } + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false); + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false); + return 0; +} + +static int msm_dai_q6_aux_pcm_probe(struct snd_soc_dai *dai) +{ + int rc = 0; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + if (!dai->driver->id) { + dev_warn(dai->dev, "DAI driver id is not set\n"); + return -EINVAL; + } + dai->id = dai->driver->id; + rc = msm_dai_q6_dai_add_route(dai); + return rc; +} + +static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = { + .prepare = msm_dai_q6_auxpcm_prepare, + .trigger = msm_dai_q6_auxpcm_trigger, + .hw_params = msm_dai_q6_auxpcm_hw_params, + .shutdown = msm_dai_q6_auxpcm_shutdown, +}; + +static const struct snd_soc_component_driver + msm_dai_q6_aux_pcm_dai_component = { + .name = "msm-auxpcm-dev", +}; + +static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai[] = { + { + .playback = { + .stream_name = "AUX PCM Playback", + .aif_name = "AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "AUX PCM Capture", + .aif_name = "AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID, + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Sec AUX PCM Playback", + .aif_name = "SEC_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Sec AUX PCM Capture", + .aif_name = "SEC_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID, + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Tert AUX PCM Playback", + .aif_name = "TERT_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Tert AUX PCM Capture", + .aif_name = "TERT_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID, + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Quat AUX PCM Playback", + .aif_name = "QUAT_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Quat AUX PCM Capture", + .aif_name = "QUAT_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID, + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, +}; + +static int msm_dai_q6_spdif_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->spdif_port.cfg.data_format = value; + pr_debug("%s: value = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_spdif_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->spdif_port.cfg.data_format; + return 0; +} + +static const char * const spdif_format[] = { + "LPCM", + "Compr" +}; + +static const struct soc_enum spdif_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, spdif_format), +}; + +static int msm_dai_q6_spdif_chstatus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + int ret = 0; + + dai_data->spdif_port.ch_status.status_type = + AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG; + memset(dai_data->spdif_port.ch_status.status_mask, + CHANNEL_STATUS_MASK_INIT, CHANNEL_STATUS_SIZE); + dai_data->spdif_port.ch_status.status_mask[0] = + CHANNEL_STATUS_MASK; + + memcpy(dai_data->spdif_port.ch_status.status_bits, + ucontrol->value.iec958.status, CHANNEL_STATUS_SIZE); + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: Port already started. Dynamic update\n", + __func__); + ret = afe_send_spdif_ch_status_cfg( + &dai_data->spdif_port.ch_status, + AFE_PORT_ID_SPDIF_RX); + } + return ret; +} + +static int msm_dai_q6_spdif_chstatus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + + memcpy(ucontrol->value.iec958.status, + dai_data->spdif_port.ch_status.status_bits, + CHANNEL_STATUS_SIZE); + return 0; +} + +static int msm_dai_q6_spdif_chstatus_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static const struct snd_kcontrol_new spdif_config_controls[] = { + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .info = msm_dai_q6_spdif_chstatus_info, + .get = msm_dai_q6_spdif_chstatus_get, + .put = msm_dai_q6_spdif_chstatus_put, + }, + SOC_ENUM_EXT("SPDIF RX Format", spdif_config_enum[0], + msm_dai_q6_spdif_format_get, + msm_dai_q6_spdif_format_put) +}; + + +static int msm_dai_q6_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai->id = AFE_PORT_ID_SPDIF_RX; + dai_data->channels = params_channels(params); + dai_data->spdif_port.cfg.num_channels = dai_data->channels; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->spdif_port.cfg.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->spdif_port.cfg.bit_width = 24; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->rate = params_rate(params); + dai_data->bitwidth = dai_data->spdif_port.cfg.bit_width; + dai_data->spdif_port.cfg.sample_rate = dai_data->rate; + dai_data->spdif_port.cfg.spdif_cfg_minor_version = + AFE_API_VERSION_SPDIF_CONFIG; + dev_dbg(dai->dev, " channel %d sample rate %d bit width %d\n", + dai_data->channels, dai_data->rate, + dai_data->spdif_port.cfg.bit_width); + dai_data->spdif_port.cfg.reserved = 0; + return 0; +} + +static void msm_dai_q6_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_info("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + return; + } + + rc = afe_close(dai->id); + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + + pr_debug("%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); +} + + +static int msm_dai_q6_spdif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: clk_config failed", __func__); + return rc; + } + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_spdif_port_start(dai->id, &dai_data->spdif_port, + dai_data->rate); + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + + return rc; +} + +static int msm_dai_q6_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data; + const struct snd_kcontrol_new *kcontrol; + int rc = 0; + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai) { + pr_err("%s: dai not found!!\n", __func__); + return -EINVAL; + } + dai_data = kzalloc(sizeof(struct msm_dai_q6_spdif_dai_data), + GFP_KERNEL); + + if (!dai_data) { + dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n", + AFE_PORT_ID_SPDIF_RX); + rc = -ENOMEM; + } else + dev_set_drvdata(dai->dev, dai_data); + + kcontrol = &spdif_config_controls[1]; + dapm = snd_soc_component_get_dapm(dai->component); + + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + memset(&intercon, 0, sizeof(intercon)); + if (!rc && dai && dai->driver) { + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + } + return rc; +} + +static int msm_dai_q6_spdif_dai_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data; + int rc; + + dai_data = dev_get_drvdata(dai->dev); + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(dai->id); /* can block */ + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + kfree(dai_data); + + return 0; +} + + +static struct snd_soc_dai_ops msm_dai_q6_spdif_ops = { + .prepare = msm_dai_q6_spdif_prepare, + .hw_params = msm_dai_q6_spdif_hw_params, + .shutdown = msm_dai_q6_spdif_shutdown, +}; + +static struct snd_soc_dai_driver msm_dai_q6_spdif_spdif_rx_dai = { + .playback = { + .stream_name = "SPDIF Playback", + .aif_name = "SPDIF_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_spdif_ops, + .probe = msm_dai_q6_spdif_dai_probe, + .remove = msm_dai_q6_spdif_dai_remove, +}; + +static const struct snd_soc_component_driver msm_dai_spdif_q6_component = { + .name = "msm-dai-q6-spdif", +}; + +static int msm_dai_q6_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + if (dai_data->enc_config.format != ENC_FMT_NONE) { + int bitwidth = 0; + + if (dai_data->afe_in_bitformat == + SNDRV_PCM_FORMAT_S24_LE) + bitwidth = 24; + else if (dai_data->afe_in_bitformat == + SNDRV_PCM_FORMAT_S16_LE) + bitwidth = 16; + pr_debug("%s: calling AFE_PORT_START_V2 with enc_format: %d\n", + __func__, dai_data->enc_config.format); + rc = afe_port_start_v2(dai->id, &dai_data->port_config, + dai_data->rate, + dai_data->afe_in_channels, + bitwidth, + &dai_data->enc_config); + if (rc < 0) + pr_err("%s: afe_port_start_v2 failed error: %d\n", + __func__, rc); + } else { + rc = afe_port_start(dai->id, &dai_data->port_config, + dai_data->rate); + } + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + return rc; +} + +static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + switch (dai_data->channels) { + case 2: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO; + break; + case 1: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO; + break; + default: + return -EINVAL; + pr_err("%s: err channels %d\n", + __func__, dai_data->channels); + break; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.i2s.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.i2s.bit_width = 24; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->rate = params_rate(params); + dai_data->port_config.i2s.sample_rate = dai_data->rate; + dai_data->port_config.i2s.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA; + dev_dbg(dai->dev, " channel %d sample rate %d entered\n", + dai_data->channels, dai_data->rate); + + dai_data->port_config.i2s.channel_mode = 1; + return 0; +} + +static u8 num_of_bits_set(u8 sd_line_mask) +{ + u8 num_bits_set = 0; + + while (sd_line_mask) { + num_bits_set++; + sd_line_mask = sd_line_mask & (sd_line_mask - 1); + } + return num_bits_set; +} + +static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct msm_i2s_data *i2s_pdata = + (struct msm_i2s_data *) dai->dev->platform_data; + + dai_data->channels = params_channels(params); + if (num_of_bits_set(i2s_pdata->sd_lines) == 1) { + switch (dai_data->channels) { + case 2: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO; + break; + case 1: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO; + break; + default: + pr_warn("%s: greater than stereo has not been validated %d", + __func__, dai_data->channels); + break; + } + } + dai_data->rate = params_rate(params); + dai_data->port_config.i2s.sample_rate = dai_data->rate; + dai_data->port_config.i2s.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA; + /* Q6 only supports 16 as now */ + dai_data->port_config.i2s.bit_width = 16; + dai_data->port_config.i2s.channel_mode = 1; + + return 0; +} + +static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.slim_sch.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.slim_sch.bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.slim_sch.bit_width = 32; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->port_config.slim_sch.sb_cfg_minor_version = + AFE_API_VERSION_SLIMBUS_CONFIG; + dai_data->port_config.slim_sch.sample_rate = dai_data->rate; + dai_data->port_config.slim_sch.num_channels = dai_data->channels; + + switch (dai->id) { + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + dai_data->port_config.slim_sch.slimbus_dev_id = + AFE_SLIMBUS_DEVICE_2; + break; + default: + dai_data->port_config.slim_sch.slimbus_dev_id = + AFE_SLIMBUS_DEVICE_1; + break; + } + + dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n" + "num_channel %hu shared_ch_mapping[0] %hu\n" + "slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n" + "sample_rate %d\n", __func__, + dai_data->port_config.slim_sch.slimbus_dev_id, + dai_data->port_config.slim_sch.bit_width, + dai_data->port_config.slim_sch.data_format, + dai_data->port_config.slim_sch.num_channels, + dai_data->port_config.slim_sch.shared_ch_mapping[0], + dai_data->port_config.slim_sch.shared_ch_mapping[1], + dai_data->port_config.slim_sch.shared_ch_mapping[2], + dai_data->rate); + + return 0; +} + +static int msm_dai_q6_usb_audio_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.usb_audio.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.usb_audio.bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.usb_audio.bit_width = 32; + break; + + default: + dev_err(dai->dev, "%s: invalid format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + dai_data->port_config.usb_audio.cfg_minor_version = + AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG; + dai_data->port_config.usb_audio.num_channels = dai_data->channels; + dai_data->port_config.usb_audio.sample_rate = dai_data->rate; + + dev_dbg(dai->dev, "%s: dev_id[0x%x] bit_wd[%hu] format[%hu]\n" + "num_channel %hu sample_rate %d\n", __func__, + dai_data->port_config.usb_audio.dev_token, + dai_data->port_config.usb_audio.bit_width, + dai_data->port_config.usb_audio.data_format, + dai_data->port_config.usb_audio.num_channels, + dai_data->port_config.usb_audio.sample_rate); + + return 0; +} + +static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + dev_dbg(dai->dev, "channels %d sample rate %d entered\n", + dai_data->channels, dai_data->rate); + + memset(&dai_data->port_config, 0, sizeof(dai_data->port_config)); + + pr_debug("%s: setting bt_fm parameters\n", __func__); + + dai_data->port_config.int_bt_fm.bt_fm_cfg_minor_version = + AFE_API_VERSION_INTERNAL_BT_FM_CONFIG; + dai_data->port_config.int_bt_fm.num_channels = dai_data->channels; + dai_data->port_config.int_bt_fm.sample_rate = dai_data->rate; + dai_data->port_config.int_bt_fm.bit_width = 16; + + return 0; +} + +static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->rate = params_rate(params); + dai_data->port_config.rtproxy.num_channels = params_channels(params); + dai_data->port_config.rtproxy.sample_rate = params_rate(params); + + pr_debug("channel %d entered,dai_id: %d,rate: %d\n", + dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate); + + dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version = + AFE_API_VERSION_RT_PROXY_CONFIG; + dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */ + dai_data->port_config.rtproxy.interleaved = 1; + dai_data->port_config.rtproxy.frame_size = params_period_bytes(params); + dai_data->port_config.rtproxy.jitter_allowance = + dai_data->port_config.rtproxy.frame_size/2; + dai_data->port_config.rtproxy.low_water_mark = 0; + dai_data->port_config.rtproxy.high_water_mark = 0; + + return 0; +} + +static int msm_dai_q6_pseudo_port_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + /* Q6 only supports 16 as now */ + dai_data->port_config.pseudo_port.pseud_port_cfg_minor_version = + AFE_API_VERSION_PSEUDO_PORT_CONFIG; + dai_data->port_config.pseudo_port.num_channels = + params_channels(params); + dai_data->port_config.pseudo_port.bit_width = 16; + dai_data->port_config.pseudo_port.data_format = 0; + dai_data->port_config.pseudo_port.timing_mode = + AFE_PSEUDOPORT_TIMING_MODE_TIMER; + dai_data->port_config.pseudo_port.sample_rate = params_rate(params); + + dev_dbg(dai->dev, "%s: bit_wd[%hu] num_channels [%hu] format[%hu]\n" + "timing Mode %hu sample_rate %d\n", __func__, + dai_data->port_config.pseudo_port.bit_width, + dai_data->port_config.pseudo_port.num_channels, + dai_data->port_config.pseudo_port.data_format, + dai_data->port_config.pseudo_port.timing_mode, + dai_data->port_config.pseudo_port.sample_rate); + + return 0; +} + +/* Current implementation assumes hw_param is called once + * This may not be the case but what to do when ADM and AFE + * port are already opened and parameter changes + */ +static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int rc = 0; + + switch (dai->id) { + case PRIMARY_I2S_TX: + case PRIMARY_I2S_RX: + case SECONDARY_I2S_RX: + rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream); + break; + case MI2S_RX: + rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream); + break; + case SLIMBUS_0_RX: + case SLIMBUS_1_RX: + case SLIMBUS_2_RX: + case SLIMBUS_3_RX: + case SLIMBUS_4_RX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_7_RX: + case SLIMBUS_8_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_TX: + case SLIMBUS_2_TX: + case SLIMBUS_3_TX: + case SLIMBUS_4_TX: + case SLIMBUS_5_TX: + case SLIMBUS_6_TX: + case SLIMBUS_7_TX: + case SLIMBUS_8_TX: + rc = msm_dai_q6_slim_bus_hw_params(params, dai, + substream->stream); + break; + case INT_BT_SCO_RX: + case INT_BT_SCO_TX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case INT_FM_TX: + rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream); + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + rc = msm_dai_q6_usb_audio_hw_params(params, dai, + substream->stream); + break; + case RT_PROXY_DAI_001_TX: + case RT_PROXY_DAI_001_RX: + case RT_PROXY_DAI_002_TX: + case RT_PROXY_DAI_002_RX: + rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai); + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + rc = msm_dai_q6_pseudo_port_hw_params(params, + dai, substream->stream); + break; + default: + dev_err(dai->dev, "invalid AFE port ID 0x%x\n", dai->id); + rc = -EINVAL; + break; + } + + return rc; +} + +static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: stop pseudo port:%d\n", __func__, dai->id); + rc = afe_close(dai->id); /* can block */ + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + pr_debug("%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } +} + +static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + dai_data->port_config.i2s.ws_src = 1; /* CPU is master */ + break; + case SND_SOC_DAIFMT_CBM_CFM: + dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */ + break; + default: + pr_err("%s: fmt 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + int rc = 0; + + dev_dbg(dai->dev, "%s: id = %d fmt[%d]\n", __func__, + dai->id, fmt); + switch (dai->id) { + case PRIMARY_I2S_TX: + case PRIMARY_I2S_RX: + case MI2S_RX: + case SECONDARY_I2S_RX: + rc = msm_dai_q6_cdc_set_fmt(dai, fmt); + break; + default: + dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id); + rc = -EINVAL; + break; + } + + return rc; +} + +static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) + +{ + int rc = 0; + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + unsigned int i = 0; + + dev_dbg(dai->dev, "%s: id = %d\n", __func__, dai->id); + switch (dai->id) { + case SLIMBUS_0_RX: + case SLIMBUS_1_RX: + case SLIMBUS_2_RX: + case SLIMBUS_3_RX: + case SLIMBUS_4_RX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_7_RX: + case SLIMBUS_8_RX: + /* + * channel number to be between 128 and 255. + * For RX port use channel numbers + * from 138 to 144 for pre-Taiko + * from 144 to 159 for Taiko + */ + if (!rx_slot) { + pr_err("%s: rx slot not found\n", __func__); + return -EINVAL; + } + for (i = 0; i < rx_num; i++) { + dai_data->port_config.slim_sch.shared_ch_mapping[i] = + rx_slot[i]; + pr_debug("%s: find number of channels[%d] ch[%d]\n", + __func__, i, rx_slot[i]); + } + dai_data->port_config.slim_sch.num_channels = rx_num; + pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__, + (dai->id - SLIMBUS_0_RX) / 2, rx_num, + dai_data->port_config.slim_sch.shared_ch_mapping[0], + dai_data->port_config.slim_sch.shared_ch_mapping[1]); + + break; + case SLIMBUS_0_TX: + case SLIMBUS_1_TX: + case SLIMBUS_2_TX: + case SLIMBUS_3_TX: + case SLIMBUS_4_TX: + case SLIMBUS_5_TX: + case SLIMBUS_6_TX: + case SLIMBUS_7_TX: + case SLIMBUS_8_TX: + /* + * channel number to be between 128 and 255. + * For TX port use channel numbers + * from 128 to 137 for pre-Taiko + * from 128 to 143 for Taiko + */ + if (!tx_slot) { + pr_err("%s: tx slot not found\n", __func__); + return -EINVAL; + } + for (i = 0; i < tx_num; i++) { + dai_data->port_config.slim_sch.shared_ch_mapping[i] = + tx_slot[i]; + pr_debug("%s: find number of channels[%d] ch[%d]\n", + __func__, i, tx_slot[i]); + } + dai_data->port_config.slim_sch.num_channels = tx_num; + pr_debug("%s:SLIMBUS_%d_TX cnt[%d] ch[%d %d]\n", __func__, + (dai->id - SLIMBUS_0_TX) / 2, tx_num, + dai_data->port_config.slim_sch.shared_ch_mapping[0], + dai_data->port_config.slim_sch.shared_ch_mapping[1]); + break; + default: + dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id); + rc = -EINVAL; + break; + } + return rc; +} + +static struct snd_soc_dai_ops msm_dai_q6_ops = { + .prepare = msm_dai_q6_prepare, + .hw_params = msm_dai_q6_hw_params, + .shutdown = msm_dai_q6_shutdown, + .set_fmt = msm_dai_q6_set_fmt, + .set_channel_map = msm_dai_q6_set_channel_map, +}; + +/* + * For single CPU DAI registration, the dai id needs to be + * set explicitly in the dai probe as ASoC does not read + * the cpu->driver->id field rather it assigns the dai id + * from the device name that is in the form %s.%d. This dai + * id should be assigned to back-end AFE port id and used + * during dai prepare. For multiple dai registration, it + * is not required to call this function, however the dai-> + * driver->id field must be defined and set to corresponding + * AFE Port id. + */ +static inline void msm_dai_q6_set_dai_id(struct snd_soc_dai *dai) +{ + if (!dai->driver->id) { + dev_warn(dai->dev, "DAI driver id is not set\n"); + return; + } + dai->id = dai->driver->id; +} + +static int msm_dai_q6_cal_info_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u16 port_id = ((struct soc_enum *) + kcontrol->private_value)->reg; + + dai_data->cal_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: setting cal_mode to %d\n", + __func__, dai_data->cal_mode); + afe_set_cal_mode(port_id, dai_data->cal_mode); + + return 0; +} + +static int msm_dai_q6_cal_info_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = dai_data->cal_mode; + return 0; +} + +static int msm_dai_q6_sb_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + if (dai_data) { + dai_data->port_config.slim_sch.data_format = value; + pr_debug("%s: format = %d\n", __func__, value); + } + + return 0; +} + +static int msm_dai_q6_sb_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) + ucontrol->value.integer.value[0] = + dai_data->port_config.slim_sch.data_format; + + return 0; +} + +static int msm_dai_q6_usb_audio_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u32 val = ucontrol->value.integer.value[0]; + + if (dai_data) { + dai_data->port_config.usb_audio.dev_token = val; + pr_debug("%s: dev_token = 0x%x\n", __func__, + dai_data->port_config.usb_audio.dev_token); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + } + + return 0; +} + +static int msm_dai_q6_usb_audio_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + ucontrol->value.integer.value[0] = + dai_data->port_config.usb_audio.dev_token; + pr_debug("%s: dev_token = 0x%x\n", __func__, + dai_data->port_config.usb_audio.dev_token); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + } + + return 0; +} + +static int msm_dai_q6_afe_enc_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_enc_config); + + return 0; +} + +static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + int format_size = sizeof(dai_data->enc_config.format); + + pr_debug("%s:encoder config for %d format\n", + __func__, dai_data->enc_config.format); + memcpy(ucontrol->value.bytes.data, + &dai_data->enc_config.format, + format_size); + switch (dai_data->enc_config.format) { + case ENC_FMT_SBC: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_sbc_enc_cfg_t)); + break; + case ENC_FMT_AAC_V2: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_aac_enc_cfg_v2_t)); + break; + case ENC_FMT_APTX: + case ENC_FMT_APTX_HD: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_aac_enc_cfg_v2_t)); + break; + default: + pr_debug("%s: unknown format = %d\n", + __func__, dai_data->enc_config.format); + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + int format_size = sizeof(dai_data->enc_config.format); + + memset(&dai_data->enc_config, 0x0, + sizeof(struct afe_enc_config)); + memcpy(&dai_data->enc_config.format, + ucontrol->value.bytes.data, + format_size); + pr_debug("%s: Received encoder config for %d format\n", + __func__, dai_data->enc_config.format); + switch (dai_data->enc_config.format) { + case ENC_FMT_SBC: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_sbc_enc_cfg_t)); + break; + case ENC_FMT_AAC_V2: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_aac_enc_cfg_v2_t)); + break; + case ENC_FMT_APTX: + case ENC_FMT_APTX_HD: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_custom_enc_cfg_aptx_t)); + break; + default: + pr_debug("%s: Ignore enc config for unknown format = %d\n", + __func__, dai_data->enc_config.format); + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + +static const char *const afe_input_chs_text[] = {"Zero", "One", "Two"}; + +static const struct soc_enum afe_input_chs_enum[] = { + SOC_ENUM_SINGLE_EXT(3, afe_input_chs_text), +}; + +static const char *const afe_input_bit_format_text[] = {"S16_LE", "S24_LE"}; + +static const struct soc_enum afe_input_bit_format_enum[] = { + SOC_ENUM_SINGLE_EXT(2, afe_input_bit_format_text), +}; + +static int msm_dai_q6_afe_input_channel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + ucontrol->value.integer.value[0] = dai_data->afe_in_channels; + pr_debug("%s:afe input channel = %d\n", + __func__, dai_data->afe_in_channels); + } + + return 0; +} + +static int msm_dai_q6_afe_input_channel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + dai_data->afe_in_channels = ucontrol->value.integer.value[0]; + pr_debug("%s: updating afe input channel : %d\n", + __func__, dai_data->afe_in_channels); + } + + return 0; +} + +static int msm_dai_q6_afe_input_bit_format_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + switch (dai_data->afe_in_bitformat) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: afe input bit format : %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dai_q6_afe_input_bit_format_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + switch (ucontrol->value.integer.value[0]) { + case 1: + dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: updating afe input bit format : %d\n", + __func__, dai_data->afe_in_bitformat); + + return 0; +} + + +static const struct snd_kcontrol_new afe_enc_config_controls[] = { + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIM_7_RX Encoder Config", + .info = msm_dai_q6_afe_enc_cfg_info, + .get = msm_dai_q6_afe_enc_cfg_get, + .put = msm_dai_q6_afe_enc_cfg_put, + }, + SOC_ENUM_EXT("AFE Input Channels", afe_input_chs_enum[0], + msm_dai_q6_afe_input_channel_get, + msm_dai_q6_afe_input_channel_put), + SOC_ENUM_EXT("AFE Input Bit Format", afe_input_bit_format_enum[0], + msm_dai_q6_afe_input_bit_format_get, + msm_dai_q6_afe_input_bit_format_put), +}; + +static const char * const afe_cal_mode_text[] = { + "CAL_MODE_DEFAULT", "CAL_MODE_NONE" +}; + +static const struct soc_enum slim_2_rx_enum = + SOC_ENUM_SINGLE(SLIMBUS_2_RX, 0, ARRAY_SIZE(afe_cal_mode_text), + afe_cal_mode_text); + +static const struct soc_enum rt_proxy_1_rx_enum = + SOC_ENUM_SINGLE(RT_PROXY_PORT_001_RX, 0, ARRAY_SIZE(afe_cal_mode_text), + afe_cal_mode_text); + +static const struct soc_enum rt_proxy_1_tx_enum = + SOC_ENUM_SINGLE(RT_PROXY_PORT_001_TX, 0, ARRAY_SIZE(afe_cal_mode_text), + afe_cal_mode_text); + +static const struct snd_kcontrol_new sb_config_controls[] = { + SOC_ENUM_EXT("SLIM_4_TX Format", sb_config_enum[0], + msm_dai_q6_sb_format_get, + msm_dai_q6_sb_format_put), + SOC_ENUM_EXT("SLIM_2_RX SetCalMode", slim_2_rx_enum, + msm_dai_q6_cal_info_get, + msm_dai_q6_cal_info_put), + SOC_ENUM_EXT("SLIM_2_RX Format", sb_config_enum[0], + msm_dai_q6_sb_format_get, + msm_dai_q6_sb_format_put) +}; + +static const struct snd_kcontrol_new rt_proxy_config_controls[] = { + SOC_ENUM_EXT("RT_PROXY_1_RX SetCalMode", rt_proxy_1_rx_enum, + msm_dai_q6_cal_info_get, + msm_dai_q6_cal_info_put), + SOC_ENUM_EXT("RT_PROXY_1_TX SetCalMode", rt_proxy_1_tx_enum, + msm_dai_q6_cal_info_get, + msm_dai_q6_cal_info_put), +}; + +static const struct snd_kcontrol_new usb_audio_cfg_controls[] = { + SOC_SINGLE_EXT("USB_AUDIO_RX dev_token", 0, 0, UINT_MAX, 0, + msm_dai_q6_usb_audio_cfg_get, + msm_dai_q6_usb_audio_cfg_put), + SOC_SINGLE_EXT("USB_AUDIO_TX dev_token", 0, 0, UINT_MAX, 0, + msm_dai_q6_usb_audio_cfg_get, + msm_dai_q6_usb_audio_cfg_put), +}; + +static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data; + int rc = 0; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + + dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL); + + if (!dai_data) + rc = -ENOMEM; + else + dev_set_drvdata(dai->dev, dai_data); + + msm_dai_q6_set_dai_id(dai); + + switch (dai->id) { + case SLIMBUS_4_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&sb_config_controls[0], + dai_data)); + break; + case SLIMBUS_2_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&sb_config_controls[1], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&sb_config_controls[2], + dai_data)); + break; + case SLIMBUS_7_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[0], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[1], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[2], + dai_data)); + break; + case RT_PROXY_DAI_001_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&rt_proxy_config_controls[0], + dai_data)); + break; + case RT_PROXY_DAI_001_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&rt_proxy_config_controls[1], + dai_data)); + break; + case AFE_PORT_ID_USB_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[0], + dai_data)); + break; + case AFE_PORT_ID_USB_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[1], + dai_data)); + break; + } + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n", + __func__, dai->name); + + rc = msm_dai_q6_dai_add_route(dai); + return rc; +} + +static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data; + int rc; + + dai_data = dev_get_drvdata(dai->dev); + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: stop pseudo port:%d\n", __func__, dai->id); + rc = afe_close(dai->id); /* can block */ + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + kfree(dai_data); + + return 0; +} + +static struct snd_soc_dai_driver msm_dai_q6_afe_rx_dai[] = { + { + .playback = { + .stream_name = "AFE Playback", + .aif_name = "PCM_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_001_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "AFE-PROXY RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_002_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_afe_tx_dai[] = { + { + .capture = { + .stream_name = "AFE Capture", + .aif_name = "PCM_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_002_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "AFE-PROXY TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_001_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = { + .playback = { + .stream_name = "Internal BT-SCO Playback", + .aif_name = "INT_BT_SCO_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_BT_SCO_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_bt_a2dp_rx_dai = { + .playback = { + .stream_name = "Internal BT-A2DP Playback", + .aif_name = "INT_BT_A2DP_RX", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_max = 48000, + .rate_min = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_BT_A2DP_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = { + .capture = { + .stream_name = "Internal BT-SCO Capture", + .aif_name = "INT_BT_SCO_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_BT_SCO_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = { + .playback = { + .stream_name = "Internal FM Playback", + .aif_name = "INT_FM_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_max = 48000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_FM_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = { + .capture = { + .stream_name = "Internal FM Capture", + .aif_name = "INT_FM_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_max = 48000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_FM_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_voc_playback_dai[] = { + { + .playback = { + .stream_name = "Voice Farend Playback", + .aif_name = "VOICE_PLAYBACK_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE_PLAYBACK_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Voice2 Farend Playback", + .aif_name = "VOICE2_PLAYBACK_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE2_PLAYBACK_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai[] = { + { + .capture = { + .stream_name = "Voice Uplink Capture", + .aif_name = "INCALL_RECORD_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE_RECORD_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Voice Downlink Capture", + .aif_name = "INCALL_RECORD_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE_RECORD_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = { + .playback = { + .stream_name = "USB Audio Playback", + .aif_name = "USB_AUDIO_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_max = 384000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = AFE_PORT_ID_USB_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = { + .capture = { + .stream_name = "USB Audio Capture", + .aif_name = "USB_AUDIO_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_max = 384000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = AFE_PORT_ID_USB_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static int msm_auxpcm_dev_probe(struct platform_device *pdev) +{ + struct msm_dai_q6_auxpcm_dai_data *dai_data; + struct msm_dai_auxpcm_pdata *auxpcm_pdata; + uint32_t val_array[RATE_MAX_NUM_OF_AUX_PCM_RATES]; + uint32_t val = 0; + const char *intf_name; + int rc = 0, i = 0, len = 0; + const uint32_t *slot_mapping_array = NULL; + u32 array_length = 0; + + dai_data = kzalloc(sizeof(struct msm_dai_q6_auxpcm_dai_data), + GFP_KERNEL); + if (!dai_data) + return -ENOMEM; + + auxpcm_pdata = kzalloc(sizeof(struct msm_dai_auxpcm_pdata), + GFP_KERNEL); + + if (!auxpcm_pdata) { + dev_err(&pdev->dev, "Failed to allocate memory for platform data\n"); + goto fail_pdata_nomem; + } + + dev_dbg(&pdev->dev, "%s: dev %pK, dai_data %pK, auxpcm_pdata %pK\n", + __func__, &pdev->dev, dai_data, auxpcm_pdata); + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-mode", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-mode missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.mode = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.mode = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-sync", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-sync missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.sync = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.sync = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-frame", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-frame missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.frame = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.frame = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-quant", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-quant missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.quant = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.quant = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-num-slots", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-num-slots missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.num_slots = (u16)val_array[RATE_8KHZ]; + + if (auxpcm_pdata->mode_8k.num_slots > + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame)) { + dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n", + __func__, + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame), + auxpcm_pdata->mode_8k.num_slots); + rc = -EINVAL; + goto fail_invalid_dt; + } + auxpcm_pdata->mode_16k.num_slots = (u16)val_array[RATE_16KHZ]; + + if (auxpcm_pdata->mode_16k.num_slots > + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame)) { + dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n", + __func__, + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame), + auxpcm_pdata->mode_16k.num_slots); + rc = -EINVAL; + goto fail_invalid_dt; + } + + slot_mapping_array = of_get_property(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-slot-mapping", &len); + + if (slot_mapping_array == NULL) { + dev_err(&pdev->dev, "%s slot_mapping_array is not valid\n", + __func__); + rc = -EINVAL; + goto fail_invalid_dt; + } + + array_length = auxpcm_pdata->mode_8k.num_slots + + auxpcm_pdata->mode_16k.num_slots; + + if (len != sizeof(uint32_t) * array_length) { + dev_err(&pdev->dev, "%s Length is %d and expected is %zd\n", + __func__, len, sizeof(uint32_t) * array_length); + rc = -EINVAL; + goto fail_invalid_dt; + } + + auxpcm_pdata->mode_8k.slot_mapping = + kzalloc(sizeof(uint16_t) * + auxpcm_pdata->mode_8k.num_slots, + GFP_KERNEL); + if (!auxpcm_pdata->mode_8k.slot_mapping) { + dev_err(&pdev->dev, "%s No mem for mode_8k slot mapping\n", + __func__); + rc = -ENOMEM; + goto fail_invalid_dt; + } + + for (i = 0; i < auxpcm_pdata->mode_8k.num_slots; i++) + auxpcm_pdata->mode_8k.slot_mapping[i] = + (u16)be32_to_cpu(slot_mapping_array[i]); + + auxpcm_pdata->mode_16k.slot_mapping = + kzalloc(sizeof(uint16_t) * + auxpcm_pdata->mode_16k.num_slots, + GFP_KERNEL); + + if (!auxpcm_pdata->mode_16k.slot_mapping) { + dev_err(&pdev->dev, "%s No mem for mode_16k slot mapping\n", + __func__); + rc = -ENOMEM; + goto fail_invalid_16k_slot_mapping; + } + + for (i = 0; i < auxpcm_pdata->mode_16k.num_slots; i++) + auxpcm_pdata->mode_16k.slot_mapping[i] = + (u16)be32_to_cpu(slot_mapping_array[i + + auxpcm_pdata->mode_8k.num_slots]); + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-data", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-data missing in DT node\n", + __func__); + goto fail_invalid_dt1; + } + auxpcm_pdata->mode_8k.data = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.data = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-pcm-clk-rate", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, + "%s: qcom,msm-cpudai-auxpcm-pcm-clk-rate missing in DT\n", + __func__); + goto fail_invalid_dt1; + } + auxpcm_pdata->mode_8k.pcm_clk_rate = (int)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.pcm_clk_rate = (int)val_array[RATE_16KHZ]; + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,msm-auxpcm-interface", &intf_name); + if (rc) { + dev_err(&pdev->dev, + "%s: qcom,msm-auxpcm-interface missing in DT node\n", + __func__); + goto fail_nodev_intf; + } + + if (!strcmp(intf_name, "primary")) { + dai_data->rx_pid = AFE_PORT_ID_PRIMARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_PRIMARY_PCM_TX; + pdev->id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID; + i = 0; + } else if (!strcmp(intf_name, "secondary")) { + dai_data->rx_pid = AFE_PORT_ID_SECONDARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_SECONDARY_PCM_TX; + pdev->id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID; + i = 1; + } else if (!strcmp(intf_name, "tertiary")) { + dai_data->rx_pid = AFE_PORT_ID_TERTIARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_TERTIARY_PCM_TX; + pdev->id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID; + i = 2; + } else if (!strcmp(intf_name, "quaternary")) { + dai_data->rx_pid = AFE_PORT_ID_QUATERNARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_QUATERNARY_PCM_TX; + pdev->id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID; + i = 3; + } else { + dev_err(&pdev->dev, "%s: invalid DT intf name %s\n", + __func__, intf_name); + goto fail_invalid_intf; + } + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-afe-clk-ver", &val); + if (rc) + dai_data->afe_clk_ver = AFE_CLK_VERSION_V1; + else + dai_data->afe_clk_ver = val; + + mutex_init(&dai_data->rlock); + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + dev_set_drvdata(&pdev->dev, dai_data); + pdev->dev.platform_data = (void *) auxpcm_pdata; + + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_aux_pcm_dai_component, + &msm_dai_q6_aux_pcm_dai[i], 1); + if (rc) { + dev_err(&pdev->dev, "%s: auxpcm dai reg failed, rc=%d\n", + __func__, rc); + goto fail_reg_dai; + } + + return rc; + +fail_reg_dai: +fail_invalid_intf: +fail_nodev_intf: +fail_invalid_dt1: + kfree(auxpcm_pdata->mode_16k.slot_mapping); +fail_invalid_16k_slot_mapping: + kfree(auxpcm_pdata->mode_8k.slot_mapping); +fail_invalid_dt: + kfree(auxpcm_pdata); +fail_pdata_nomem: + kfree(dai_data); + return rc; +} + +static int msm_auxpcm_dev_remove(struct platform_device *pdev) +{ + struct msm_dai_q6_auxpcm_dai_data *dai_data; + + dai_data = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + + mutex_destroy(&dai_data->rlock); + kfree(dai_data); + kfree(pdev->dev.platform_data); + + return 0; +} + +static const struct of_device_id msm_auxpcm_dev_dt_match[] = { + { .compatible = "qcom,msm-auxpcm-dev", }, + {} +}; + + +static struct platform_driver msm_auxpcm_dev_driver = { + .probe = msm_auxpcm_dev_probe, + .remove = msm_auxpcm_dev_remove, + .driver = { + .name = "msm-auxpcm-dev", + .owner = THIS_MODULE, + .of_match_table = msm_auxpcm_dev_dt_match, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = { + { + .playback = { + .stream_name = "Slimbus Playback", + .aif_name = "SLIMBUS_0_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_0_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus1 Playback", + .aif_name = "SLIMBUS_1_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_1_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus2 Playback", + .aif_name = "SLIMBUS_2_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_2_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus3 Playback", + .aif_name = "SLIMBUS_3_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_3_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus4 Playback", + .aif_name = "SLIMBUS_4_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_4_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus6 Playback", + .aif_name = "SLIMBUS_6_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_6_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus5 Playback", + .aif_name = "SLIMBUS_5_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_5_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus7 Playback", + .aif_name = "SLIMBUS_7_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_7_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus8 Playback", + .aif_name = "SLIMBUS_8_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_8_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai[] = { + { + .capture = { + .stream_name = "Slimbus Capture", + .aif_name = "SLIMBUS_0_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_0_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus1 Capture", + .aif_name = "SLIMBUS_1_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_1_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus2 Capture", + .aif_name = "SLIMBUS_2_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_2_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus3 Capture", + .aif_name = "SLIMBUS_3_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_3_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus4 Capture", + .aif_name = "SLIMBUS_4_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_4_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus5 Capture", + .aif_name = "SLIMBUS_5_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_5_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus6 Capture", + .aif_name = "SLIMBUS_6_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_6_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus7 Capture", + .aif_name = "SLIMBUS_7_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_7_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus8 Capture", + .aif_name = "SLIMBUS_8_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_8_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_config.i2s.data_format = value; + pr_debug("%s: value = %d, channel = %d, line = %d\n", + __func__, value, dai_data->port_config.i2s.mono_stereo, + dai_data->port_config.i2s.channel_mode); + return 0; +} + +static int msm_dai_q6_mi2s_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_config.i2s.data_format; + return 0; +} + +static const struct snd_kcontrol_new mi2s_config_controls[] = { + SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("TERT MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUAT MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUIN MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("TERT MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUAT MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUIN MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("SENARY MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), +}; + +static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_mi2s_pdata *mi2s_pdata = + (struct msm_mi2s_pdata *) dai->dev->platform_data; + struct snd_kcontrol *kcontrol = NULL; + int rc = 0; + const struct snd_kcontrol_new *ctrl = NULL; + + dai->id = mi2s_pdata->intf_id; + + if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode) { + if (dai->id == MSM_PRIM_MI2S) + ctrl = &mi2s_config_controls[0]; + if (dai->id == MSM_SEC_MI2S) + ctrl = &mi2s_config_controls[1]; + if (dai->id == MSM_TERT_MI2S) + ctrl = &mi2s_config_controls[2]; + if (dai->id == MSM_QUAT_MI2S) + ctrl = &mi2s_config_controls[3]; + if (dai->id == MSM_QUIN_MI2S) + ctrl = &mi2s_config_controls[4]; + } + + if (ctrl) { + kcontrol = snd_ctl_new1(ctrl, + &mi2s_dai_data->rx_dai.mi2s_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, kcontrol); + + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: err add RX fmt ctl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + ctrl = NULL; + if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) { + if (dai->id == MSM_PRIM_MI2S) + ctrl = &mi2s_config_controls[4]; + if (dai->id == MSM_SEC_MI2S) + ctrl = &mi2s_config_controls[5]; + if (dai->id == MSM_TERT_MI2S) + ctrl = &mi2s_config_controls[6]; + if (dai->id == MSM_QUAT_MI2S) + ctrl = &mi2s_config_controls[7]; + if (dai->id == MSM_QUIN_MI2S) + ctrl = &mi2s_config_controls[9]; + if (dai->id == MSM_SENARY_MI2S) + ctrl = &mi2s_config_controls[10]; + } + + if (ctrl) { + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(ctrl, + &mi2s_dai_data->tx_dai.mi2s_dai_data)); + + if (IS_ERR_VALUE(rc)) { + if (kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + kcontrol); + dev_err(dai->dev, "%s: err add TX fmt ctl DAI = %s\n", + __func__, dai->name); + } + } + rc = msm_dai_q6_dai_add_route(dai); +rtn: + return rc; +} + + +static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + int rc; + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) { + rc = afe_close(MI2S_RX); /* can block */ + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close MI2S_RX port\n"); + clear_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask); + } + if (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) { + rc = afe_close(MI2S_TX); /* can block */ + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close MI2S_TX port\n"); + clear_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask); + } + kfree(mi2s_dai_data); + return 0; +} + +static int msm_dai_q6_mi2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + + return 0; +} + + +static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id) +{ + int ret = 0; + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + switch (mi2s_id) { + case MSM_PRIM_MI2S: + *port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_SEC_MI2S: + *port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_TERT_MI2S: + *port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_QUAT_MI2S: + *port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_SEC_MI2S_SD1: + *port_id = AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + break; + case MSM_QUIN_MI2S: + *port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_INT0_MI2S: + *port_id = AFE_PORT_ID_INT0_MI2S_RX; + break; + case MSM_INT1_MI2S: + *port_id = AFE_PORT_ID_INT1_MI2S_RX; + break; + case MSM_INT2_MI2S: + *port_id = AFE_PORT_ID_INT2_MI2S_RX; + break; + case MSM_INT3_MI2S: + *port_id = AFE_PORT_ID_INT3_MI2S_RX; + break; + case MSM_INT4_MI2S: + *port_id = AFE_PORT_ID_INT4_MI2S_RX; + break; + case MSM_INT5_MI2S: + *port_id = AFE_PORT_ID_INT5_MI2S_RX; + break; + case MSM_INT6_MI2S: + *port_id = AFE_PORT_ID_INT6_MI2S_RX; + break; + default: + pr_err("%s: playback err id 0x%x\n", + __func__, mi2s_id); + ret = -1; + break; + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + switch (mi2s_id) { + case MSM_PRIM_MI2S: + *port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_SEC_MI2S: + *port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_TERT_MI2S: + *port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_QUAT_MI2S: + *port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_QUIN_MI2S: + *port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + case MSM_SENARY_MI2S: + *port_id = AFE_PORT_ID_SENARY_MI2S_TX; + break; + case MSM_INT0_MI2S: + *port_id = AFE_PORT_ID_INT0_MI2S_TX; + break; + case MSM_INT1_MI2S: + *port_id = AFE_PORT_ID_INT1_MI2S_TX; + break; + case MSM_INT2_MI2S: + *port_id = AFE_PORT_ID_INT2_MI2S_TX; + break; + case MSM_INT3_MI2S: + *port_id = AFE_PORT_ID_INT3_MI2S_TX; + break; + case MSM_INT4_MI2S: + *port_id = AFE_PORT_ID_INT4_MI2S_TX; + break; + case MSM_INT5_MI2S: + *port_id = AFE_PORT_ID_INT5_MI2S_TX; + break; + case MSM_INT6_MI2S: + *port_id = AFE_PORT_ID_INT6_MI2S_TX; + break; + default: + pr_err("%s: capture err id 0x%x\n", __func__, mi2s_id); + ret = -1; + break; + } + break; + default: + pr_err("%s: default err %d\n", __func__, stream); + ret = -1; + break; + } + pr_debug("%s: port_id = 0x%x\n", __func__, *port_id); + return ret; +} + +static int msm_dai_q6_mi2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai.mi2s_dai_data : + &mi2s_dai_data->tx_dai.mi2s_dai_data); + u16 port_id = 0; + int rc = 0; + + if (msm_mi2s_get_port_id(dai->id, substream->stream, + &port_id) != 0) { + dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n", + __func__, port_id); + return -EINVAL; + } + + dev_dbg(dai->dev, "%s: dai id %d, afe port id = 0x%x\n" + "dai_data->channels = %u sample_rate = %u\n", __func__, + dai->id, port_id, dai_data->channels, dai_data->rate); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + /* PORT START should be set if prepare called + * in active state. + */ + rc = afe_port_start(port_id, &dai_data->port_config, + dai_data->rate); + + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) { + set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); + dev_dbg(dai->dev, "%s: set hwfree_status to started\n", + __func__); + } + return rc; +} + +static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_mi2s_dai_config *mi2s_dai_config = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai : &mi2s_dai_data->tx_dai); + struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data; + struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s; + + dai_data->channels = params_channels(params); + switch (dai_data->channels) { + case 8: + case 7: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_8CHS) + goto error_invalid_data; + dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_8CHS; + break; + case 6: + case 5: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_6CHS) + goto error_invalid_data; + dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_6CHS; + break; + case 4: + case 3: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_QUAD01) + goto error_invalid_data; + if (mi2s_dai_config->pdata_mi2s_lines == AFE_PORT_I2S_QUAD23) + dai_data->port_config.i2s.channel_mode = + mi2s_dai_config->pdata_mi2s_lines; + else + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_QUAD01; + break; + case 2: + case 1: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0) + goto error_invalid_data; + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_SD0: + case AFE_PORT_I2S_SD1: + case AFE_PORT_I2S_SD2: + case AFE_PORT_I2S_SD3: + dai_data->port_config.i2s.channel_mode = + mi2s_dai_config->pdata_mi2s_lines; + break; + case AFE_PORT_I2S_QUAD01: + case AFE_PORT_I2S_6CHS: + case AFE_PORT_I2S_8CHS: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD0; + break; + case AFE_PORT_I2S_QUAD23: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD2; + break; + } + if (dai_data->channels == 2) + dai_data->port_config.i2s.mono_stereo = + MSM_AFE_CH_STEREO; + else + dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO; + break; + default: + pr_err("%s: default err channels %d\n", + __func__, dai_data->channels); + goto error_invalid_data; + } + dai_data->rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.i2s.bit_width = 16; + dai_data->bitwidth = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.i2s.bit_width = 24; + dai_data->bitwidth = 24; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->port_config.i2s.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + dai_data->port_config.i2s.sample_rate = dai_data->rate; + if ((test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) && + test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) || + (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) && + test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) { + if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate != + mi2s_dai_data->rx_dai.mi2s_dai_data.rate) || + (mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth != + mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth)) { + dev_err(dai->dev, "%s: Error mismatch in HW params\n" + "Tx sample_rate = %u bit_width = %hu\n" + "Rx sample_rate = %u bit_width = %hu\n" + , __func__, + mi2s_dai_data->tx_dai.mi2s_dai_data.rate, + mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth, + mi2s_dai_data->rx_dai.mi2s_dai_data.rate, + mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth); + return -EINVAL; + } + } + dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n" + "sample_rate = %u i2s_cfg_minor_version = 0x%x\n" + "bit_width = %hu channel_mode = 0x%x mono_stereo = %#x\n" + "ws_src = 0x%x sample_rate = %u data_format = 0x%x\n" + "reserved = %u\n", __func__, dai->id, dai_data->channels, + dai_data->rate, i2s->i2s_cfg_minor_version, i2s->bit_width, + i2s->channel_mode, i2s->mono_stereo, i2s->ws_src, + i2s->sample_rate, i2s->data_format, i2s->reserved); + + return 0; + +error_invalid_data: + pr_err("%s: dai_data->channels = %d channel_mode = %d\n", __func__, + dai_data->channels, dai_data->port_config.i2s.channel_mode); + return -EINVAL; +} + + +static int msm_dai_q6_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + + if (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) || + test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) { + dev_err(dai->dev, "%s: err chg i2s mode while dai running", + __func__); + return -EPERM; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1; + mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0; + mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0; + break; + default: + pr_err("%s: fmt %d\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai.mi2s_dai_data : + &mi2s_dai_data->tx_dai.mi2s_dai_data); + + if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) { + clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); + dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__); + } + return 0; +} + +static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai.mi2s_dai_data : + &mi2s_dai_data->tx_dai.mi2s_dai_data); + u16 port_id = 0; + int rc = 0; + + if (msm_mi2s_get_port_id(dai->id, substream->stream, + &port_id) != 0) { + dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n", + __func__, port_id); + } + + dev_dbg(dai->dev, "%s: closing afe port id = 0x%x\n", + __func__, port_id); + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(port_id); + if (IS_ERR_VALUE(rc)) + dev_err(dai->dev, "fail to close AFE port\n"); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) + clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); +} + +static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = { + .startup = msm_dai_q6_mi2s_startup, + .prepare = msm_dai_q6_mi2s_prepare, + .hw_params = msm_dai_q6_mi2s_hw_params, + .hw_free = msm_dai_q6_mi2s_hw_free, + .set_fmt = msm_dai_q6_mi2s_set_fmt, + .shutdown = msm_dai_q6_mi2s_shutdown, +}; + +/* Channel min and max are initialized base on platform data */ +static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { + { + .playback = { + .stream_name = "Primary MI2S Playback", + .aif_name = "PRI_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Primary MI2S Capture", + .aif_name = "PRI_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_PRIM_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Secondary MI2S Playback", + .aif_name = "SEC_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Secondary MI2S Capture", + .aif_name = "SEC_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_SEC_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Tertiary MI2S Playback", + .aif_name = "TERT_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Tertiary MI2S Capture", + .aif_name = "TERT_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_TERT_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Quaternary MI2S Playback", + .aif_name = "QUAT_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Quaternary MI2S Capture", + .aif_name = "QUAT_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_QUAT_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Secondary MI2S Playback SD1", + .aif_name = "SEC_MI2S_RX_SD1", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = MSM_SEC_MI2S_SD1, + }, + { + .playback = { + .stream_name = "Quinary MI2S Playback", + .aif_name = "QUIN_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Quinary MI2S Capture", + .aif_name = "QUIN_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_QUIN_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .capture = { + .stream_name = "Senary_mi2s Capture", + .aif_name = "SENARY_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_SENARY_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT0 MI2S Playback", + .aif_name = "INT0_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT0 MI2S Capture", + .aif_name = "INT0_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT0_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT1 MI2S Playback", + .aif_name = "INT1_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT1 MI2S Capture", + .aif_name = "INT1_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT1_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT2 MI2S Playback", + .aif_name = "INT2_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT2 MI2S Capture", + .aif_name = "INT2_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT2_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT3 MI2S Playback", + .aif_name = "INT3_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT3 MI2S Capture", + .aif_name = "INT3_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT3_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT4 MI2S Playback", + .aif_name = "INT4_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT4 MI2S Capture", + .aif_name = "INT4_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT4_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT5 MI2S Playback", + .aif_name = "INT5_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT5 MI2S Capture", + .aif_name = "INT5_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT5_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT6 MI2S Playback", + .aif_name = "INT6_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT6 MI2S Capture", + .aif_name = "INT6_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .id = MSM_INT6_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, +}; + + +static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr, + unsigned int *ch_cnt) +{ + u8 num_of_sd_lines; + + num_of_sd_lines = num_of_bits_set(sd_lines); + switch (num_of_sd_lines) { + case 0: + pr_debug("%s: no line is assigned\n", __func__); + break; + case 1: + switch (sd_lines) { + case MSM_MI2S_SD0: + *config_ptr = AFE_PORT_I2S_SD0; + break; + case MSM_MI2S_SD1: + *config_ptr = AFE_PORT_I2S_SD1; + break; + case MSM_MI2S_SD2: + *config_ptr = AFE_PORT_I2S_SD2; + break; + case MSM_MI2S_SD3: + *config_ptr = AFE_PORT_I2S_SD3; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 2: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1: + *config_ptr = AFE_PORT_I2S_QUAD01; + break; + case MSM_MI2S_SD2 | MSM_MI2S_SD3: + *config_ptr = AFE_PORT_I2S_QUAD23; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 3: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2: + *config_ptr = AFE_PORT_I2S_6CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 4: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3: + *config_ptr = AFE_PORT_I2S_8CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + default: + pr_err("%s: invalid SD lines %d\n", __func__, num_of_sd_lines); + goto error_invalid_data; + } + *ch_cnt = num_of_sd_lines; + return 0; + +error_invalid_data: + pr_err("%s: invalid data\n", __func__); + return -EINVAL; +} + +static int msm_dai_q6_mi2s_platform_data_validation( + struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver) +{ + struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev); + struct msm_mi2s_pdata *mi2s_pdata = + (struct msm_mi2s_pdata *) pdev->dev.platform_data; + unsigned int ch_cnt; + int rc = 0; + u16 sd_line; + + if (mi2s_pdata == NULL) { + pr_err("%s: mi2s_pdata NULL", __func__); + return -EINVAL; + } + + rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines, + &sd_line, &ch_cnt); + + if (IS_ERR_VALUE(rc)) { + dev_err(&pdev->dev, "invalid MI2S RX sd line config\n"); + goto rtn; + } + + if (ch_cnt) { + dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode = + sd_line; + dai_data->rx_dai.pdata_mi2s_lines = sd_line; + dai_driver->playback.channels_min = 1; + dai_driver->playback.channels_max = ch_cnt << 1; + } else { + dai_driver->playback.channels_min = 0; + dai_driver->playback.channels_max = 0; + } + rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines, + &sd_line, &ch_cnt); + + if (IS_ERR_VALUE(rc)) { + dev_err(&pdev->dev, "invalid MI2S TX sd line config\n"); + goto rtn; + } + + if (ch_cnt) { + dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode = + sd_line; + dai_data->tx_dai.pdata_mi2s_lines = sd_line; + dai_driver->capture.channels_min = 1; + dai_driver->capture.channels_max = ch_cnt << 1; + } else { + dai_driver->capture.channels_min = 0; + dai_driver->capture.channels_max = 0; + } + + dev_dbg(&pdev->dev, "%s: playback sdline 0x%x capture sdline 0x%x\n", + __func__, dai_data->rx_dai.pdata_mi2s_lines, + dai_data->tx_dai.pdata_mi2s_lines); + dev_dbg(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n", + __func__, dai_driver->playback.channels_max, + dai_driver->capture.channels_max); +rtn: + return rc; +} + +static const struct snd_soc_component_driver msm_q6_mi2s_dai_component = { + .name = "msm-dai-q6-mi2s", +}; +static int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev) +{ + struct msm_dai_q6_mi2s_dai_data *dai_data; + const char *q6_mi2s_dev_id = "qcom,msm-dai-q6-mi2s-dev-id"; + u32 tx_line = 0; + u32 rx_line = 0; + u32 mi2s_intf = 0; + struct msm_mi2s_pdata *mi2s_pdata; + int rc; + + rc = of_property_read_u32(pdev->dev.of_node, q6_mi2s_dev_id, + &mi2s_intf); + if (rc) { + dev_err(&pdev->dev, + "%s: missing 0x%x in dt node\n", __func__, mi2s_intf); + goto rtn; + } + + dev_dbg(&pdev->dev, "dev name %s dev id 0x%x\n", dev_name(&pdev->dev), + mi2s_intf); + + if ((mi2s_intf < MSM_MI2S_MIN || mi2s_intf > MSM_MI2S_MAX) + || (mi2s_intf >= ARRAY_SIZE(msm_dai_q6_mi2s_dai))) { + dev_err(&pdev->dev, + "%s: Invalid MI2S ID %u from Device Tree\n", + __func__, mi2s_intf); + rc = -ENXIO; + goto rtn; + } + + pdev->id = mi2s_intf; + + mi2s_pdata = kzalloc(sizeof(struct msm_mi2s_pdata), GFP_KERNEL); + if (!mi2s_pdata) { + rc = -ENOMEM; + goto rtn; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines", + &rx_line); + if (rc) { + dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__, + "qcom,msm-mi2s-rx-lines"); + goto free_pdata; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines", + &tx_line); + if (rc) { + dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__, + "qcom,msm-mi2s-tx-lines"); + goto free_pdata; + } + dev_dbg(&pdev->dev, "dev name %s Rx line 0x%x , Tx ine 0x%x\n", + dev_name(&pdev->dev), rx_line, tx_line); + mi2s_pdata->rx_sd_lines = rx_line; + mi2s_pdata->tx_sd_lines = tx_line; + mi2s_pdata->intf_id = mi2s_intf; + + dai_data = kzalloc(sizeof(struct msm_dai_q6_mi2s_dai_data), + GFP_KERNEL); + if (!dai_data) { + rc = -ENOMEM; + goto free_pdata; + } else + dev_set_drvdata(&pdev->dev, dai_data); + + pdev->dev.platform_data = mi2s_pdata; + + rc = msm_dai_q6_mi2s_platform_data_validation(pdev, + &msm_dai_q6_mi2s_dai[mi2s_intf]); + if (IS_ERR_VALUE(rc)) + goto free_dai_data; + + rc = snd_soc_register_component(&pdev->dev, &msm_q6_mi2s_dai_component, + &msm_dai_q6_mi2s_dai[mi2s_intf], 1); + + if (IS_ERR_VALUE(rc)) + goto err_register; + return 0; + +err_register: + dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n"); +free_dai_data: + kfree(dai_data); +free_pdata: + kfree(mi2s_pdata); +rtn: + return rc; +} + +static int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct snd_soc_component_driver msm_dai_q6_component = { + .name = "msm-dai-q6-dev", +}; + +static int msm_dai_q6_dev_probe(struct platform_device *pdev) +{ + int rc, id, i, len; + const char *q6_dev_id = "qcom,msm-dai-q6-dev-id"; + char stream_name[80]; + + rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, q6_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (id) { + case SLIMBUS_0_RX: + strlcpy(stream_name, "Slimbus Playback", 80); + goto register_slim_playback; + case SLIMBUS_2_RX: + strlcpy(stream_name, "Slimbus2 Playback", 80); + goto register_slim_playback; + case SLIMBUS_1_RX: + strlcpy(stream_name, "Slimbus1 Playback", 80); + goto register_slim_playback; + case SLIMBUS_3_RX: + strlcpy(stream_name, "Slimbus3 Playback", 80); + goto register_slim_playback; + case SLIMBUS_4_RX: + strlcpy(stream_name, "Slimbus4 Playback", 80); + goto register_slim_playback; + case SLIMBUS_5_RX: + strlcpy(stream_name, "Slimbus5 Playback", 80); + goto register_slim_playback; + case SLIMBUS_6_RX: + strlcpy(stream_name, "Slimbus6 Playback", 80); + goto register_slim_playback; + case SLIMBUS_7_RX: + strlcpy(stream_name, "Slimbus7 Playback", sizeof(stream_name)); + goto register_slim_playback; + case SLIMBUS_8_RX: + strlcpy(stream_name, "Slimbus8 Playback", sizeof(stream_name)); + goto register_slim_playback; +register_slim_playback: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_rx_dai); i++) { + if (msm_dai_q6_slimbus_rx_dai[i].playback.stream_name && + !strcmp(stream_name, + msm_dai_q6_slimbus_rx_dai[i] + .playback.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_slimbus_rx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case SLIMBUS_0_TX: + strlcpy(stream_name, "Slimbus Capture", 80); + goto register_slim_capture; + case SLIMBUS_1_TX: + strlcpy(stream_name, "Slimbus1 Capture", 80); + goto register_slim_capture; + case SLIMBUS_2_TX: + strlcpy(stream_name, "Slimbus2 Capture", 80); + goto register_slim_capture; + case SLIMBUS_3_TX: + strlcpy(stream_name, "Slimbus3 Capture", 80); + goto register_slim_capture; + case SLIMBUS_4_TX: + strlcpy(stream_name, "Slimbus4 Capture", 80); + goto register_slim_capture; + case SLIMBUS_5_TX: + strlcpy(stream_name, "Slimbus5 Capture", 80); + goto register_slim_capture; + case SLIMBUS_6_TX: + strlcpy(stream_name, "Slimbus6 Capture", 80); + goto register_slim_capture; + case SLIMBUS_7_TX: + strlcpy(stream_name, "Slimbus7 Capture", sizeof(stream_name)); + goto register_slim_capture; + case SLIMBUS_8_TX: + strlcpy(stream_name, "Slimbus8 Capture", sizeof(stream_name)); + goto register_slim_capture; +register_slim_capture: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_tx_dai); i++) { + if (msm_dai_q6_slimbus_tx_dai[i].capture.stream_name && + !strcmp(stream_name, + msm_dai_q6_slimbus_tx_dai[i] + .capture.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_slimbus_tx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case INT_BT_SCO_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_bt_sco_rx_dai, 1); + break; + case INT_BT_SCO_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_bt_sco_tx_dai, 1); + break; + case INT_BT_A2DP_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_bt_a2dp_rx_dai, 1); + break; + case INT_FM_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_fm_rx_dai, 1); + break; + case INT_FM_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_fm_tx_dai, 1); + break; + case AFE_PORT_ID_USB_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_usb_rx_dai, 1); + break; + case AFE_PORT_ID_USB_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_usb_tx_dai, 1); + break; + case RT_PROXY_DAI_001_RX: + strlcpy(stream_name, "AFE Playback", 80); + goto register_afe_playback; + case RT_PROXY_DAI_002_RX: + strlcpy(stream_name, "AFE-PROXY RX", 80); +register_afe_playback: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_rx_dai); i++) { + if (msm_dai_q6_afe_rx_dai[i].playback.stream_name && + !strcmp(stream_name, + msm_dai_q6_afe_rx_dai[i].playback.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_afe_rx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case RT_PROXY_DAI_001_TX: + strlcpy(stream_name, "AFE-PROXY TX", 80); + goto register_afe_capture; + case RT_PROXY_DAI_002_TX: + strlcpy(stream_name, "AFE Capture", 80); +register_afe_capture: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_tx_dai); i++) { + if (msm_dai_q6_afe_tx_dai[i].capture.stream_name && + !strcmp(stream_name, + msm_dai_q6_afe_tx_dai[i].capture.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_afe_tx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case VOICE_PLAYBACK_TX: + strlcpy(stream_name, "Voice Farend Playback", 80); + goto register_voice_playback; + case VOICE2_PLAYBACK_TX: + strlcpy(stream_name, "Voice2 Farend Playback", 80); +register_voice_playback: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_voc_playback_dai); i++) { + if (msm_dai_q6_voc_playback_dai[i].playback.stream_name + && !strcmp(stream_name, + msm_dai_q6_voc_playback_dai[i].playback.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_voc_playback_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s Device not found stream name %s\n", + __func__, stream_name); + break; + case VOICE_RECORD_RX: + strlcpy(stream_name, "Voice Downlink Capture", 80); + goto register_uplink_capture; + case VOICE_RECORD_TX: + strlcpy(stream_name, "Voice Uplink Capture", 80); +register_uplink_capture: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_incall_record_dai); i++) { + if (msm_dai_q6_incall_record_dai[i].capture.stream_name + && !strcmp(stream_name, + msm_dai_q6_incall_record_dai[i]. + capture.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_incall_record_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + + default: + rc = -ENODEV; + break; + } + + return rc; +} + +static int msm_dai_q6_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-q6-dev", }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_dev_dt_match); + +static struct platform_driver msm_dai_q6_dev = { + .probe = msm_dai_q6_dev_probe, + .remove = msm_dai_q6_dev_remove, + .driver = { + .name = "msm-dai-q6-dev", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_dev_dt_match, + }, +}; + +static int msm_dai_q6_probe(struct platform_device *pdev) +{ + int rc; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + + return rc; +} + +static int msm_dai_q6_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id msm_dai_q6_dt_match[] = { + { .compatible = "qcom,msm-dai-q6", }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_dt_match); +static struct platform_driver msm_dai_q6 = { + .probe = msm_dai_q6_probe, + .remove = msm_dai_q6_remove, + .driver = { + .name = "msm-dai-q6", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_dt_match, + }, +}; + +static int msm_dai_mi2s_q6_probe(struct platform_device *pdev) +{ + int rc; + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + return rc; +} + +static int msm_dai_mi2s_q6_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id msm_dai_mi2s_dt_match[] = { + { .compatible = "qcom,msm-dai-mi2s", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match); + +static struct platform_driver msm_dai_mi2s_q6 = { + .probe = msm_dai_mi2s_q6_probe, + .remove = msm_dai_mi2s_q6_remove, + .driver = { + .name = "msm-dai-mi2s", + .owner = THIS_MODULE, + .of_match_table = msm_dai_mi2s_dt_match, + }, +}; + +static const struct of_device_id msm_dai_q6_mi2s_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-q6-mi2s", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_q6_mi2s_dev_dt_match); + +static struct platform_driver msm_dai_q6_mi2s_driver = { + .probe = msm_dai_q6_mi2s_dev_probe, + .remove = msm_dai_q6_mi2s_dev_remove, + .driver = { + .name = "msm-dai-q6-mi2s", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_mi2s_dev_dt_match, + }, +}; + +static int msm_dai_q6_spdif_dev_probe(struct platform_device *pdev) +{ + int rc; + + pdev->id = AFE_PORT_ID_SPDIF_RX; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_spdif_q6_component, + &msm_dai_q6_spdif_spdif_rx_dai, 1); + return rc; +} + +static int msm_dai_q6_spdif_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_spdif_dt_match[] = { + {.compatible = "qcom,msm-dai-q6-spdif"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_spdif_dt_match); + +static struct platform_driver msm_dai_q6_spdif_driver = { + .probe = msm_dai_q6_spdif_dev_probe, + .remove = msm_dai_q6_spdif_dev_remove, + .driver = { + .name = "msm-dai-q6-spdif", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_spdif_dt_match, + }, +}; + +static int msm_dai_tdm_q6_probe(struct platform_device *pdev) +{ + int rc = 0; + u32 num_ports = 0; + const uint32_t *port_id_array = NULL; + uint32_t array_length = 0; + int i = 0; + int group_idx = 0; + + /* extract tdm group info into static */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-group-id", + (u32 *)&tdm_group_cfg.group_id); + if (rc) { + dev_err(&pdev->dev, "%s: Group ID from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-group-id"); + goto rtn; + } + dev_dbg(&pdev->dev, "%s: Group ID from DT file 0x%x\n", + __func__, tdm_group_cfg.group_id); + + dev_info(&pdev->dev, "%s: dev_name: %s group_id: 0x%x\n", + __func__, dev_name(&pdev->dev), tdm_group_cfg.group_id); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-group-num-ports", + &num_ports); + if (rc) { + dev_err(&pdev->dev, "%s: Group Num Ports from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-group-num-ports"); + goto rtn; + } + dev_dbg(&pdev->dev, "%s: Group Num Ports from DT file 0x%x\n", + __func__, num_ports); + + if (num_ports > AFE_GROUP_DEVICE_NUM_PORTS) { + dev_err(&pdev->dev, "%s Group Num Ports %d greater than Max %d\n", + __func__, num_ports, AFE_GROUP_DEVICE_NUM_PORTS); + rc = -EINVAL; + goto rtn; + } + + port_id_array = of_get_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-group-port-id", + &array_length); + if (port_id_array == NULL) { + dev_err(&pdev->dev, "%s port_id_array is not valid\n", + __func__); + rc = -EINVAL; + goto rtn; + } + if (array_length != sizeof(uint32_t) * num_ports) { + dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n", + __func__, array_length, sizeof(uint32_t) * num_ports); + rc = -EINVAL; + goto rtn; + } + + for (i = 0; i < num_ports; i++) + tdm_group_cfg.port_id[i] = + (u16)be32_to_cpu(port_id_array[i]); + /* Unused index should be filled with 0 or AFE_PORT_INVALID */ + for (i = num_ports; i < AFE_GROUP_DEVICE_NUM_PORTS; i++) + tdm_group_cfg.port_id[i] = + AFE_PORT_INVALID; + + /* extract tdm clk info into static */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-rate", + &tdm_clk_set.clk_freq_in_hz); + if (rc) { + dev_err(&pdev->dev, "%s: Clk Rate from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-clk-rate"); + goto rtn; + } + dev_dbg(&pdev->dev, "%s: Clk Rate from DT file %d\n", + __func__, tdm_clk_set.clk_freq_in_hz); + + /* other initializations within device group */ + group_idx = msm_dai_q6_get_group_idx(tdm_group_cfg.group_id); + if (group_idx < 0) { + dev_err(&pdev->dev, "%s: group id 0x%x not supported\n", + __func__, tdm_group_cfg.group_id); + rc = -EINVAL; + goto rtn; + } + atomic_set(&tdm_group_ref[group_idx], 0); + + /* probe child node info */ + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + goto rtn; + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + +rtn: + return rc; +} + +static int msm_dai_tdm_q6_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id msm_dai_tdm_dt_match[] = { + { .compatible = "qcom,msm-dai-tdm", }, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_dai_tdm_dt_match); + +static struct platform_driver msm_dai_tdm_q6 = { + .probe = msm_dai_tdm_q6_probe, + .remove = msm_dai_tdm_q6_remove, + .driver = { + .name = "msm-dai-tdm", + .owner = THIS_MODULE, + .of_match_table = msm_dai_tdm_dt_match, + }, +}; + +static int msm_dai_q6_tdm_data_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_cfg.tdm.data_format = value; + pr_debug("%s: data_format = %d\n", + __func__, dai_data->port_cfg.tdm.data_format); + return 0; +} + +static int msm_dai_q6_tdm_data_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_cfg.tdm.data_format; + pr_debug("%s: data_format = %d\n", + __func__, dai_data->port_cfg.tdm.data_format); + return 0; +} + +static int msm_dai_q6_tdm_header_type_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_cfg.custom_tdm_header.header_type = value; + pr_debug("%s: header_type = %d\n", + __func__, + dai_data->port_cfg.custom_tdm_header.header_type); + return 0; +} + +static int msm_dai_q6_tdm_header_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_cfg.custom_tdm_header.header_type; + pr_debug("%s: header_type = %d\n", + __func__, + dai_data->port_cfg.custom_tdm_header.header_type); + return 0; +} + +static int msm_dai_q6_tdm_header_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int i = 0; + + for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) { + dai_data->port_cfg.custom_tdm_header.header[i] = + (u16)ucontrol->value.integer.value[i]; + pr_debug("%s: header #%d = 0x%x\n", + __func__, i, + dai_data->port_cfg.custom_tdm_header.header[i]); + } + return 0; +} + +static int msm_dai_q6_tdm_header_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int i = 0; + + for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) { + ucontrol->value.integer.value[i] = + dai_data->port_cfg.custom_tdm_header.header[i]; + pr_debug("%s: header #%d = 0x%x\n", + __func__, i, + dai_data->port_cfg.custom_tdm_header.header[i]); + } + return 0; +} + +static const struct snd_kcontrol_new tdm_config_controls_data_format[] = { + SOC_ENUM_EXT("PRI_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), +}; + +static const struct snd_kcontrol_new tdm_config_controls_header_type[] = { + SOC_ENUM_EXT("PRI_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), +}; + +static const struct snd_kcontrol_new tdm_config_controls_header[] = { + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), +}; + +static int msm_dai_q6_tdm_set_clk( + struct msm_dai_q6_tdm_dai_data *dai_data, + u16 port_id, bool enable) +{ + int rc = 0; + + switch (dai_data->group_cfg.tdm_cfg.group_id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + if (dai_data->clk_set.clk_freq_in_hz) { + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT; + } else + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT; + break; + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX: + if (dai_data->clk_set.clk_freq_in_hz) { + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT; + } else + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT; + break; + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX: + if (dai_data->clk_set.clk_freq_in_hz) { + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT; + } else + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT; + break; + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX: + if (dai_data->clk_set.clk_freq_in_hz) { + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT; + } else + dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT; + break; + default: + pr_err("%s: port id 0x%x not supported\n", + __func__, port_id); + return -EINVAL; + } + dai_data->clk_set.enable = enable; + + rc = afe_set_lpass_clock_v2(port_id, + &dai_data->clk_set); + if (rc < 0) + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, rc); + + return rc; +} + +static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *tdm_dai_data = + dev_get_drvdata(dai->dev); + struct snd_kcontrol *data_format_kcontrol = NULL; + struct snd_kcontrol *header_type_kcontrol = NULL; + struct snd_kcontrol *header_kcontrol = NULL; + int port_idx = 0; + const struct snd_kcontrol_new *data_format_ctrl = NULL; + const struct snd_kcontrol_new *header_type_ctrl = NULL; + const struct snd_kcontrol_new *header_ctrl = NULL; + + msm_dai_q6_set_dai_id(dai); + + port_idx = msm_dai_q6_get_port_idx(dai->id); + if (port_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + rc = -EINVAL; + goto rtn; + } + + data_format_ctrl = + &tdm_config_controls_data_format[port_idx]; + header_type_ctrl = + &tdm_config_controls_header_type[port_idx]; + header_ctrl = + &tdm_config_controls_header[port_idx]; + + if (data_format_ctrl) { + data_format_kcontrol = snd_ctl_new1(data_format_ctrl, + tdm_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, + data_format_kcontrol); + + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: err add data format ctrl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + if (header_type_ctrl) { + header_type_kcontrol = snd_ctl_new1(header_type_ctrl, + tdm_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, + header_type_kcontrol); + + if (IS_ERR_VALUE(rc)) { + if (data_format_kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + data_format_kcontrol); + dev_err(dai->dev, "%s: err add header type ctrl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + if (header_ctrl) { + header_kcontrol = snd_ctl_new1(header_ctrl, + tdm_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, + header_kcontrol); + + if (IS_ERR_VALUE(rc)) { + if (header_type_kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + header_type_kcontrol); + if (data_format_kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + data_format_kcontrol); + dev_err(dai->dev, "%s: err add header ctrl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + rc = msm_dai_q6_dai_add_route(dai); + +rtn: + return rc; +} + + +static int msm_dai_q6_dai_tdm_remove(struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *tdm_dai_data = + dev_get_drvdata(dai->dev); + u16 group_id = tdm_dai_data->group_cfg.tdm_cfg.group_id; + int group_idx = 0; + atomic_t *group_ref = NULL; + + group_idx = msm_dai_q6_get_group_idx(dai->id); + if (group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + return -EINVAL; + } + + group_ref = &tdm_group_ref[group_idx]; + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, tdm_dai_data->status_mask)) { + rc = afe_close(dai->id); /* can block */ + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n", + __func__, dai->id); + } + atomic_dec(group_ref); + clear_bit(STATUS_PORT_STARTED, + tdm_dai_data->status_mask); + + if (atomic_read(group_ref) == 0) { + rc = afe_port_group_enable(group_id, + NULL, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "fail to disable AFE group 0x%x\n", + group_id); + } + rc = msm_dai_q6_tdm_set_clk(tdm_dai_data, + dai->id, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", + __func__, dai->id); + } + } + } + + return 0; +} + +static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + struct afe_param_id_group_device_tdm_cfg *tdm_group = + &dai_data->group_cfg.tdm_cfg; + unsigned int cap_mask; + + dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id); + + /* HW only supports 16 and 32 bit slot width configuration */ + if ((slot_width != 16) && (slot_width != 32)) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + /* HW only supports 16 and 8 slots configuration */ + switch (slots) { + case 8: + cap_mask = 0xFF; + break; + case 16: + cap_mask = 0xFFFF; + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", + __func__, slots); + return -EINVAL; + } + + switch (dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + tdm_group->nslots_per_frame = slots; + tdm_group->slot_width = slot_width; + tdm_group->slot_mask = rx_mask & cap_mask; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + tdm_group->nslots_per_frame = slots; + tdm_group->slot_width = slot_width; + tdm_group->slot_mask = tx_mask & cap_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + return rc; +} + +static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + struct afe_param_id_slot_mapping_cfg *slot_mapping = + &dai_data->port_cfg.slot_mapping; + int i = 0; + + dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id); + + switch (dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + if (!rx_slot) { + dev_err(dai->dev, "%s: rx slot not found\n", __func__); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid rx num %d\n", __func__, + rx_num); + return -EINVAL; + } + + for (i = 0; i < rx_num; i++) + slot_mapping->offset[i] = rx_slot[i]; + for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) + slot_mapping->offset[i] = + AFE_SLOT_MAPPING_OFFSET_INVALID; + + slot_mapping->num_channel = rx_num; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + if (!tx_slot) { + dev_err(dai->dev, "%s: tx slot not found\n", __func__); + return -EINVAL; + } + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid tx num %d\n", __func__, + tx_num); + return -EINVAL; + } + + for (i = 0; i < tx_num; i++) + slot_mapping->offset[i] = tx_slot[i]; + for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) + slot_mapping->offset[i] = + AFE_SLOT_MAPPING_OFFSET_INVALID; + + slot_mapping->num_channel = tx_num; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + return rc; +} + +static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + + struct afe_param_id_group_device_tdm_cfg *tdm_group = + &dai_data->group_cfg.tdm_cfg; + struct afe_param_id_tdm_cfg *tdm = + &dai_data->port_cfg.tdm; + struct afe_param_id_slot_mapping_cfg *slot_mapping = + &dai_data->port_cfg.slot_mapping; + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header = + &dai_data->port_cfg.custom_tdm_header; + + pr_debug("%s: dev_name: %s\n", + __func__, dev_name(dai->dev)); + + if ((params_channels(params) == 0) || + (params_channels(params) > 8)) { + dev_err(dai->dev, "%s: invalid param channels %d\n", + __func__, params_channels(params)); + return -EINVAL; + } + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->bitwidth = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->bitwidth = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->bitwidth = 32; + break; + default: + dev_err(dai->dev, "%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + /* + * update tdm group config param + * NOTE: group config is set to the same as slot config. + */ + tdm_group->bit_width = tdm_group->slot_width; + tdm_group->num_channels = tdm_group->nslots_per_frame; + tdm_group->sample_rate = dai_data->rate; + + pr_debug("%s: TDM GROUP:\n" + "num_channels=%d sample_rate=%d bit_width=%d\n" + "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n", + __func__, + tdm_group->num_channels, + tdm_group->sample_rate, + tdm_group->bit_width, + tdm_group->nslots_per_frame, + tdm_group->slot_width, + tdm_group->slot_mask); + pr_debug("%s: TDM GROUP:\n" + "port_id[0]=0x%x port_id[1]=0x%x port_id[2]=0x%x port_id[3]=0x%x\n" + "port_id[4]=0x%x port_id[5]=0x%x port_id[6]=0x%x port_id[7]=0x%x\n", + __func__, + tdm_group->port_id[0], + tdm_group->port_id[1], + tdm_group->port_id[2], + tdm_group->port_id[3], + tdm_group->port_id[4], + tdm_group->port_id[5], + tdm_group->port_id[6], + tdm_group->port_id[7]); + + /* + * update tdm config param + * NOTE: channels/rate/bitwidth are per stream property + */ + tdm->num_channels = dai_data->channels; + tdm->sample_rate = dai_data->rate; + tdm->bit_width = dai_data->bitwidth; + /* + * port slot config is the same as group slot config + * port slot mask should be set according to offset + */ + tdm->nslots_per_frame = tdm_group->nslots_per_frame; + tdm->slot_width = tdm_group->slot_width; + tdm->slot_mask = tdm_group->slot_mask; + + pr_debug("%s: TDM:\n" + "num_channels=%d sample_rate=%d bit_width=%d\n" + "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n" + "data_format=0x%x sync_mode=0x%x sync_src=0x%x\n" + "data_out=0x%x invert_sync=0x%x data_delay=0x%x\n", + __func__, + tdm->num_channels, + tdm->sample_rate, + tdm->bit_width, + tdm->nslots_per_frame, + tdm->slot_width, + tdm->slot_mask, + tdm->data_format, + tdm->sync_mode, + tdm->sync_src, + tdm->ctrl_data_out_enable, + tdm->ctrl_invert_sync_pulse, + tdm->ctrl_sync_data_delay); + + /* + * update slot mapping config param + * NOTE: channels/rate/bitwidth are per stream property + */ + slot_mapping->bitwidth = dai_data->bitwidth; + + pr_debug("%s: SLOT MAPPING:\n" + "num_channel=%d bitwidth=%d data_align=0x%x\n", + __func__, + slot_mapping->num_channel, + slot_mapping->bitwidth, + slot_mapping->data_align_type); + pr_debug("%s: SLOT MAPPING:\n" + "offset[0]=0x%x offset[1]=0x%x offset[2]=0x%x offset[3]=0x%x\n" + "offset[4]=0x%x offset[5]=0x%x offset[6]=0x%x offset[7]=0x%x\n", + __func__, + slot_mapping->offset[0], + slot_mapping->offset[1], + slot_mapping->offset[2], + slot_mapping->offset[3], + slot_mapping->offset[4], + slot_mapping->offset[5], + slot_mapping->offset[6], + slot_mapping->offset[7]); + + /* + * update custom header config param + * NOTE: channels/rate/bitwidth are per playback stream property. + * custom tdm header only applicable to playback stream. + */ + if (custom_tdm_header->header_type != + AFE_CUSTOM_TDM_HEADER_TYPE_INVALID) { + pr_debug("%s: CUSTOM TDM HEADER:\n" + "start_offset=0x%x header_width=%d\n" + "num_frame_repeat=%d header_type=0x%x\n", + __func__, + custom_tdm_header->start_offset, + custom_tdm_header->header_width, + custom_tdm_header->num_frame_repeat, + custom_tdm_header->header_type); + pr_debug("%s: CUSTOM TDM HEADER:\n" + "header[0]=0x%x header[1]=0x%x header[2]=0x%x header[3]=0x%x\n" + "header[4]=0x%x header[5]=0x%x header[6]=0x%x header[7]=0x%x\n", + __func__, + custom_tdm_header->header[0], + custom_tdm_header->header[1], + custom_tdm_header->header[2], + custom_tdm_header->header[3], + custom_tdm_header->header[4], + custom_tdm_header->header[5], + custom_tdm_header->header[6], + custom_tdm_header->header[7]); + } + + return 0; +} + +static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + int group_idx = 0; + atomic_t *group_ref = NULL; + + group_idx = msm_dai_q6_get_group_idx(dai->id); + if (group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + return -EINVAL; + } + + mutex_lock(&tdm_mutex); + + group_ref = &tdm_group_ref[group_idx]; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + /* PORT START should be set if prepare called + * in active state. + */ + if (atomic_read(group_ref) == 0) { + /* TX and RX share the same clk. + * AFE clk is enabled per group to simplify the logic. + * DSP will monitor the clk count. + */ + rc = msm_dai_q6_tdm_set_clk(dai_data, + dai->id, true); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n", + __func__, dai->id); + goto rtn; + } + rc = afe_port_group_enable(group_id, + &dai_data->group_cfg, true); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to enable AFE group 0x%x\n", + __func__, group_id); + goto rtn; + } + } + + rc = afe_tdm_port_start(dai->id, &dai_data->port_cfg, + dai_data->rate); + if (IS_ERR_VALUE(rc)) { + if (atomic_read(group_ref) == 0) { + afe_port_group_enable(group_id, + NULL, false); + msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); + } + dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n", + __func__, dai->id); + } else { + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + atomic_inc(group_ref); + } + + /* TODO: need to monitor PCM/MI2S/TDM HW status */ + /* NOTE: AFE should error out if HW resource contention */ + + } + +rtn: + mutex_unlock(&tdm_mutex); + return rc; +} + +static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + int group_idx = 0; + atomic_t *group_ref = NULL; + + group_idx = msm_dai_q6_get_group_idx(dai->id); + if (group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + return; + } + + mutex_lock(&tdm_mutex); + + group_ref = &tdm_group_ref[group_idx]; + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(dai->id); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n", + __func__, dai->id); + } + atomic_dec(group_ref); + clear_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + + if (atomic_read(group_ref) == 0) { + rc = afe_port_group_enable(group_id, + NULL, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to disable AFE group 0x%x\n", + __func__, group_id); + } + rc = msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", + __func__, dai->id); + } + } + + /* TODO: need to monitor PCM/MI2S/TDM HW status */ + /* NOTE: AFE should error out if HW resource contention */ + + } + + mutex_unlock(&tdm_mutex); +} + +static struct snd_soc_dai_ops msm_dai_q6_tdm_ops = { + .prepare = msm_dai_q6_tdm_prepare, + .hw_params = msm_dai_q6_tdm_hw_params, + .set_tdm_slot = msm_dai_q6_tdm_set_tdm_slot, + .set_channel_map = msm_dai_q6_tdm_set_channel_map, + .shutdown = msm_dai_q6_tdm_shutdown, +}; + +static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { + { + .playback = { + .stream_name = "Primary TDM0 Playback", + .aif_name = "PRI_TDM_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM1 Playback", + .aif_name = "PRI_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM2 Playback", + .aif_name = "PRI_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM3 Playback", + .aif_name = "PRI_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM4 Playback", + .aif_name = "PRI_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM5 Playback", + .aif_name = "PRI_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM6 Playback", + .aif_name = "PRI_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM7 Playback", + .aif_name = "PRI_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM0 Capture", + .aif_name = "PRI_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM1 Capture", + .aif_name = "PRI_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM2 Capture", + .aif_name = "PRI_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM3 Capture", + .aif_name = "PRI_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM4 Capture", + .aif_name = "PRI_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM5 Capture", + .aif_name = "PRI_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM6 Capture", + .aif_name = "PRI_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM7 Capture", + .aif_name = "PRI_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM0 Playback", + .aif_name = "SEC_TDM_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM1 Playback", + .aif_name = "SEC_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM2 Playback", + .aif_name = "SEC_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM3 Playback", + .aif_name = "SEC_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM4 Playback", + .aif_name = "SEC_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM5 Playback", + .aif_name = "SEC_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM6 Playback", + .aif_name = "SEC_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM7 Playback", + .aif_name = "SEC_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM0 Capture", + .aif_name = "SEC_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM1 Capture", + .aif_name = "SEC_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM2 Capture", + .aif_name = "SEC_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM3 Capture", + .aif_name = "SEC_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM4 Capture", + .aif_name = "SEC_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM5 Capture", + .aif_name = "SEC_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM6 Capture", + .aif_name = "SEC_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM7 Capture", + .aif_name = "SEC_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM0 Playback", + .aif_name = "TERT_TDM_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM1 Playback", + .aif_name = "TERT_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM2 Playback", + .aif_name = "TERT_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM3 Playback", + .aif_name = "TERT_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM4 Playback", + .aif_name = "TERT_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM5 Playback", + .aif_name = "TERT_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM6 Playback", + .aif_name = "TERT_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM7 Playback", + .aif_name = "TERT_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM0 Capture", + .aif_name = "TERT_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM1 Capture", + .aif_name = "TERT_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM2 Capture", + .aif_name = "TERT_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM3 Capture", + .aif_name = "TERT_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM4 Capture", + .aif_name = "TERT_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM5 Capture", + .aif_name = "TERT_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM6 Capture", + .aif_name = "TERT_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM7 Capture", + .aif_name = "TERT_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM0 Playback", + .aif_name = "QUAT_TDM_RX_0", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM1 Playback", + .aif_name = "QUAT_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM2 Playback", + .aif_name = "QUAT_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM3 Playback", + .aif_name = "QUAT_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM4 Playback", + .aif_name = "QUAT_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM5 Playback", + .aif_name = "QUAT_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM6 Playback", + .aif_name = "QUAT_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM7 Playback", + .aif_name = "QUAT_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM0 Capture", + .aif_name = "QUAT_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM1 Capture", + .aif_name = "QUAT_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM2 Capture", + .aif_name = "QUAT_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM3 Capture", + .aif_name = "QUAT_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM4 Capture", + .aif_name = "QUAT_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM5 Capture", + .aif_name = "QUAT_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM6 Capture", + .aif_name = "QUAT_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM7 Capture", + .aif_name = "QUAT_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, +}; + +static const struct snd_soc_component_driver msm_q6_tdm_dai_component = { + .name = "msm-dai-q6-tdm", +}; + +static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = NULL; + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header = NULL; + int rc = 0; + u32 tdm_dev_id = 0; + int port_idx = 0; + + /* retrieve device/afe id */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-dev-id", + &tdm_dev_id); + if (rc) { + dev_err(&pdev->dev, "%s: Device ID missing in DT file\n", + __func__); + goto rtn; + } + if ((tdm_dev_id < AFE_PORT_ID_TDM_PORT_RANGE_START) || + (tdm_dev_id > AFE_PORT_ID_TDM_PORT_RANGE_END)) { + dev_err(&pdev->dev, "%s: Invalid TDM Device ID 0x%x in DT file\n", + __func__, tdm_dev_id); + rc = -ENXIO; + goto rtn; + } + pdev->id = tdm_dev_id; + + dev_info(&pdev->dev, "%s: dev_name: %s dev_id: 0x%x\n", + __func__, dev_name(&pdev->dev), tdm_dev_id); + + dai_data = kzalloc(sizeof(struct msm_dai_q6_tdm_dai_data), + GFP_KERNEL); + if (!dai_data) { + rc = -ENOMEM; + dev_err(&pdev->dev, + "%s Failed to allocate memory for tdm dai_data\n", + __func__); + goto rtn; + } + memset(dai_data, 0, sizeof(*dai_data)); + + /* TDM CFG */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-sync-mode", + (u32 *)&dai_data->port_cfg.tdm.sync_mode); + if (rc) { + dev_err(&pdev->dev, "%s: Sync Mode from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-sync-mode"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Sync Mode from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.sync_mode); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-sync-src", + (u32 *)&dai_data->port_cfg.tdm.sync_src); + if (rc) { + dev_err(&pdev->dev, "%s: Sync Src from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-sync-src"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Sync Src from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.sync_src); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-data-out", + (u32 *)&dai_data->port_cfg.tdm.ctrl_data_out_enable); + if (rc) { + dev_err(&pdev->dev, "%s: Data Out from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-data-out"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Data Out from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.ctrl_data_out_enable); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-invert-sync", + (u32 *)&dai_data->port_cfg.tdm.ctrl_invert_sync_pulse); + if (rc) { + dev_err(&pdev->dev, "%s: Invert Sync from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-invert-sync"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Invert Sync from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.ctrl_invert_sync_pulse); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-data-delay", + (u32 *)&dai_data->port_cfg.tdm.ctrl_sync_data_delay); + if (rc) { + dev_err(&pdev->dev, "%s: Data Delay from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-data-delay"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Data Delay from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.ctrl_sync_data_delay); + + /* TDM CFG -- set default */ + dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA; + dai_data->port_cfg.tdm.tdm_cfg_minor_version = + AFE_API_VERSION_TDM_CONFIG; + + /* TDM SLOT MAPPING CFG */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-data-align", + &dai_data->port_cfg.slot_mapping.data_align_type); + if (rc) { + dev_err(&pdev->dev, "%s: Data Align from DT file %s\n", + __func__, + "qcom,msm-cpudai-tdm-data-align"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Data Align from DT file 0x%x\n", + __func__, dai_data->port_cfg.slot_mapping.data_align_type); + + /* TDM SLOT MAPPING CFG -- set default */ + dai_data->port_cfg.slot_mapping.minor_version = + AFE_API_VERSION_SLOT_MAPPING_CONFIG; + + /* CUSTOM TDM HEADER CFG */ + custom_tdm_header = &dai_data->port_cfg.custom_tdm_header; + if (of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-start-offset", NULL) && + of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-width", NULL) && + of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-num-frame-repeat", NULL)) { + /* if the property exist */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-start-offset", + (u32 *)&custom_tdm_header->start_offset); + if (rc) { + dev_err(&pdev->dev, "%s: Header Start Offset from DT file %s\n", + __func__, + "qcom,msm-cpudai-tdm-header-start-offset"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Header Start Offset from DT file 0x%x\n", + __func__, custom_tdm_header->start_offset); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-width", + (u32 *)&custom_tdm_header->header_width); + if (rc) { + dev_err(&pdev->dev, "%s: Header Width from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-header-width"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Header Width from DT file 0x%x\n", + __func__, custom_tdm_header->header_width); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-num-frame-repeat", + (u32 *)&custom_tdm_header->num_frame_repeat); + if (rc) { + dev_err(&pdev->dev, "%s: Header Num Frame Repeat from DT file %s\n", + __func__, + "qcom,msm-cpudai-tdm-header-num-frame-repeat"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Header Num Frame Repeat from DT file 0x%x\n", + __func__, custom_tdm_header->num_frame_repeat); + + /* CUSTOM TDM HEADER CFG -- set default */ + custom_tdm_header->minor_version = + AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG; + custom_tdm_header->header_type = + AFE_CUSTOM_TDM_HEADER_TYPE_INVALID; + } else { + dev_info(&pdev->dev, + "%s: Custom tdm header not supported\n", __func__); + /* CUSTOM TDM HEADER CFG -- set default */ + custom_tdm_header->header_type = + AFE_CUSTOM_TDM_HEADER_TYPE_INVALID; + /* proceed with probe */ + } + + /* copy static clk per parent node */ + dai_data->clk_set = tdm_clk_set; + /* copy static group cfg per parent node */ + dai_data->group_cfg.tdm_cfg = tdm_group_cfg; + + dev_set_drvdata(&pdev->dev, dai_data); + + port_idx = msm_dai_q6_get_port_idx(tdm_dev_id); + if (port_idx < 0) { + dev_err(&pdev->dev, "%s Port id 0x%x not supported\n", + __func__, tdm_dev_id); + rc = -EINVAL; + goto free_dai_data; + } + + rc = snd_soc_register_component(&pdev->dev, + &msm_q6_tdm_dai_component, + &msm_dai_q6_tdm_dai[port_idx], 1); + + if (rc) { + dev_err(&pdev->dev, "%s: TDM dai 0x%x register failed, rc=%d\n", + __func__, tdm_dev_id, rc); + goto err_register; + } + + return 0; + +err_register: +free_dai_data: + kfree(dai_data); +rtn: + return rc; +} + +static int msm_dai_q6_tdm_dev_remove(struct platform_device *pdev) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + + kfree(dai_data); + + return 0; +} + +static const struct of_device_id msm_dai_q6_tdm_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-q6-tdm", }, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_dai_q6_tdm_dev_dt_match); + +static struct platform_driver msm_dai_q6_tdm_driver = { + .probe = msm_dai_q6_tdm_dev_probe, + .remove = msm_dai_q6_tdm_dev_remove, + .driver = { + .name = "msm-dai-q6-tdm", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_tdm_dev_dt_match, + }, +}; + +static int __init msm_dai_q6_init(void) +{ + int rc; + + rc = platform_driver_register(&msm_auxpcm_dev_driver); + if (rc) { + pr_err("%s: fail to register auxpcm dev driver", __func__); + goto fail; + } + + rc = platform_driver_register(&msm_dai_q6); + if (rc) { + pr_err("%s: fail to register dai q6 driver", __func__); + goto dai_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_dev); + if (rc) { + pr_err("%s: fail to register dai q6 dev driver", __func__); + goto dai_q6_dev_fail; + } + + rc = platform_driver_register(&msm_dai_q6_mi2s_driver); + if (rc) { + pr_err("%s: fail to register dai MI2S dev drv\n", __func__); + goto dai_q6_mi2s_drv_fail; + } + + rc = platform_driver_register(&msm_dai_mi2s_q6); + if (rc) { + pr_err("%s: fail to register dai MI2S\n", __func__); + goto dai_mi2s_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_spdif_driver); + if (rc) { + pr_err("%s: fail to register dai SPDIF\n", __func__); + goto dai_spdif_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_tdm_driver); + if (rc) { + pr_err("%s: fail to register dai TDM dev drv\n", __func__); + goto dai_q6_tdm_drv_fail; + } + + rc = platform_driver_register(&msm_dai_tdm_q6); + if (rc) { + pr_err("%s: fail to register dai TDM\n", __func__); + goto dai_tdm_q6_fail; + } + return rc; + +dai_tdm_q6_fail: + platform_driver_unregister(&msm_dai_q6_tdm_driver); +dai_q6_tdm_drv_fail: + platform_driver_unregister(&msm_dai_q6_spdif_driver); +dai_spdif_q6_fail: + platform_driver_unregister(&msm_dai_mi2s_q6); +dai_mi2s_q6_fail: + platform_driver_unregister(&msm_dai_q6_mi2s_driver); +dai_q6_mi2s_drv_fail: + platform_driver_unregister(&msm_dai_q6_dev); +dai_q6_dev_fail: + platform_driver_unregister(&msm_dai_q6); +dai_q6_fail: + platform_driver_unregister(&msm_auxpcm_dev_driver); +fail: + return rc; +} +module_init(msm_dai_q6_init); + +static void __exit msm_dai_q6_exit(void) +{ + platform_driver_unregister(&msm_dai_q6_dev); + platform_driver_unregister(&msm_dai_q6); + platform_driver_unregister(&msm_auxpcm_dev_driver); + platform_driver_unregister(&msm_dai_q6_spdif_driver); +} +module_exit(msm_dai_q6_exit); + +/* Module information */ +MODULE_DESCRIPTION("MSM DSP DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/sound/soc/msm/qdsp6v2/msm-dai-slim.c new file mode 100644 index 000000000000..e012cf2373e9 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SLIM_DEV_NAME "msm-dai-slim" + +#define SLIM_DAI_RATES (SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000 | \ + SNDRV_PCM_RATE_384000) + +#define SLIM_DAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define DAI_STATE_INITIALIZED (0x01 << 0) +#define DAI_STATE_PREPARED (0x01 << 1) +#define DAI_STATE_RUNNING (0x01 << 2) + +#define SET_DAI_STATE(status, state) \ + (status |= state) + +#define CLR_DAI_STATE(status, state) \ + (status = status & (~state)) + +enum { + MSM_DAI_SLIM0 = 0, + NUM_SLIM_DAIS, +}; + +struct msm_slim_dai_data { + unsigned int dai_id; + u16 *chan_h; + u16 *sh_ch; + u16 grph; + u32 rate; + u16 bits; + u16 ch_cnt; + u8 status; + struct snd_soc_dai_driver *dai_drv; + struct msm_slim_dma_data dma_data; + struct slim_port_cfg port_cfg; +}; + +struct msm_dai_slim_drv_data { + struct slim_device *sdev; + u16 num_dais; + struct msm_slim_dai_data slim_dai_data[NUM_SLIM_DAIS]; +}; + +struct msm_slim_dai_data *msm_slim_get_dai_data( + struct msm_dai_slim_drv_data *drv_data, + struct snd_soc_dai *dai) +{ + struct msm_slim_dai_data *dai_data_t; + int i; + + for (i = 0; i < drv_data->num_dais; i++) { + dai_data_t = &drv_data->slim_dai_data[i]; + if (dai_data_t->dai_id == dai->id) + return dai_data_t; + } + + dev_err(dai->dev, + "%s: no dai data found for dai_id %d\n", + __func__, dai->id); + return NULL; +} + +static int msm_dai_slim_ch_ctl(struct msm_slim_dma_data *dma_data, + struct snd_soc_dai *dai, bool enable) +{ + struct slim_device *sdev; + struct msm_dai_slim_drv_data *drv_data; + struct msm_slim_dai_data *dai_data; + int rc, rc1, i; + + if (!dma_data || !dma_data->sdev) { + pr_err("%s: Invalid %s\n", __func__, + (!dma_data) ? "dma_data" : "slim_device"); + return -EINVAL; + } + + sdev = dma_data->sdev; + drv_data = dev_get_drvdata(&sdev->dev); + dai_data = msm_slim_get_dai_data(drv_data, dai); + + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai_id %d\n", + __func__, dai->id); + return -EINVAL; + } + + dev_dbg(&sdev->dev, + "%s: enable = %s, rate = %u\n", __func__, + enable ? "true" : "false", + dai_data->rate); + + if (enable) { + if (!(dai_data->status & DAI_STATE_PREPARED)) { + dev_err(&sdev->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return -EINVAL; + } + + rc = slim_alloc_mgrports(sdev, + SLIM_REQ_DEFAULT, dai_data->ch_cnt, + &(dma_data->ph), + sizeof(dma_data->ph)); + + if (IS_ERR_VALUE(rc)) { + dev_err(&sdev->dev, + "%s:alloc mgrport failed rc %d\n", + __func__, rc); + goto done; + } + + rc = slim_config_mgrports(sdev, &(dma_data->ph), + dai_data->ch_cnt, + &(dai_data->port_cfg)); + if (IS_ERR_VALUE(rc)) { + dev_err(&sdev->dev, + "%s: config mgrport failed rc %d\n", + __func__, rc); + goto err_done; + } + + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_connect_sink(sdev, + &dma_data->ph, 1, + dai_data->chan_h[i]); + if (IS_ERR_VALUE(rc)) { + dev_err(&sdev->dev, + "%s: slim_connect_sink failed, ch = %d, err = %d\n", + __func__, i, rc); + goto err_done; + } + } + + rc = slim_control_ch(sdev, + dai_data->grph, + SLIM_CH_ACTIVATE, true); + if (IS_ERR_VALUE(rc)) { + dev_err(&sdev->dev, + "%s: slim activate ch failed, err = %d\n", + __func__, rc); + goto err_done; + } + /* Mark dai status as running */ + SET_DAI_STATE(dai_data->status, DAI_STATE_RUNNING); + } else { + if (!(dai_data->status & DAI_STATE_RUNNING)) { + dev_err(&sdev->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return -EINVAL; + } + + rc = slim_control_ch(sdev, + dai_data->grph, + SLIM_CH_REMOVE, true); + if (IS_ERR_VALUE(rc)) { + dev_err(&sdev->dev, + "%s: slim activate ch failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = slim_dealloc_mgrports(sdev, + &dma_data->ph, 1); + if (IS_ERR_VALUE(rc)) { + dev_err(&sdev->dev, + "%s: dealloc mgrport failed, err = %d\n", + __func__, rc); + goto done; + } + /* clear running state for dai*/ + CLR_DAI_STATE(dai_data->status, DAI_STATE_RUNNING); + } + + return rc; + +err_done: + rc1 = slim_dealloc_mgrports(sdev, + &dma_data->ph, 1); + if (IS_ERR_VALUE(rc1)) + dev_err(&sdev->dev, + "%s: dealloc mgrport failed, err = %d\n", + __func__, rc1); +done: + return rc; +} + +static int msm_dai_slim_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dai_data *dai_data; + int rc = 0; + + dai_data = msm_slim_get_dai_data(drv_data, dai); + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai_id %d\n", + __func__, dai->id); + rc = -EINVAL; + goto done; + } + + if (!dai_data->ch_cnt || dai_data->ch_cnt != params_channels(params)) { + dev_err(dai->dev, "%s: invalid ch_cnt %d %d\n", + __func__, dai_data->ch_cnt, params_channels(params)); + rc = -EINVAL; + goto done; + } + + dai_data->rate = params_rate(params); + dai_data->port_cfg.port_opts = SLIM_OPT_NONE; + if (dai_data->rate >= SNDRV_PCM_RATE_48000) + dai_data->port_cfg.watermark = 16; + else + dai_data->port_cfg.watermark = 8; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->bits = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dai_data->bits = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->bits = 32; + break; + default: + dev_err(dai->dev, "%s: invalid format %d\n", __func__, + params_format(params)); + rc = -EINVAL; + goto done; + } + + dev_dbg(dai->dev, "%s: ch_cnt=%u rate=%u, bit_width = %u\n", + __func__, dai_data->ch_cnt, dai_data->rate, + dai_data->bits); +done: + return rc; +} + +static int msm_dai_slim_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dai_data *dai_data; + struct snd_soc_dai_driver *dai_drv; + u8 i = 0; + + dev_dbg(dai->dev, + "%s: tx_num=%u, rx_num=%u\n", + __func__, tx_num, rx_num); + + dai_data = msm_slim_get_dai_data(drv_data, dai); + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai_id %d\n", + __func__, dai->id); + return -EINVAL; + } + + dai_drv = dai_data->dai_drv; + + if (tx_num > dai_drv->capture.channels_max) { + dev_err(dai->dev, "%s: tx_num %u max out master port cnt\n", + __func__, tx_num); + return -EINVAL; + } + + for (i = 0; i < tx_num; i++) + dai_data->sh_ch[i] = tx_slot[i]; + + dai_data->ch_cnt = tx_num; + return 0; +} + +static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dma_data *dma_data; + struct msm_slim_dai_data *dai_data = NULL; + struct slim_ch prop; + int rc; + u8 i, j; + + dai_data = msm_slim_get_dai_data(drv_data, dai); + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai %d\n", + __func__, dai->id); + return -EINVAL; + } + + if (!(dai_data->status & DAI_STATE_INITIALIZED)) { + dev_err(dai->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return -EINVAL; + } + + dma_data = &dai_data->dma_data; + snd_soc_dai_set_dma_data(dai, substream, dma_data); + + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_query_ch(drv_data->sdev, dai_data->sh_ch[i], + &dai_data->chan_h[i]); + if (rc) { + dev_err(dai->dev, "%s:query chan handle failed rc %d\n", + __func__, rc); + goto error_chan_query; + } + } + + prop.prot = SLIM_AUTO_ISO; + prop.baser = SLIM_RATE_4000HZ; + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.ratem = (dai_data->rate/4000); + prop.sampleszbits = dai_data->bits; + + rc = slim_define_ch(drv_data->sdev, &prop, dai_data->chan_h, + dai_data->ch_cnt, true, &dai_data->grph); + + if (rc) { + dev_err(dai->dev, "%s:define chan failed rc %d\n", + __func__, rc); + goto error_define_chan; + } + + /* Mark stream status as prepared */ + SET_DAI_STATE(dai_data->status, DAI_STATE_PREPARED); + + return rc; + +error_define_chan: +error_chan_query: + for (j = 0; j < i; j++) + slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]); + return rc; +} + +static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream, + struct snd_soc_dai *dai) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dma_data *dma_data = NULL; + struct msm_slim_dai_data *dai_data; + int i, rc = 0; + + dai_data = msm_slim_get_dai_data(drv_data, dai); + dma_data = snd_soc_dai_get_dma_data(dai, stream); + if (!dma_data || !dai_data) { + dev_err(dai->dev, + "%s: Invalid %s\n", __func__, + (!dma_data) ? "dma_data" : "dai_data"); + return; + } + + if ((!(dai_data->status & DAI_STATE_PREPARED)) || + dai_data->status & DAI_STATE_RUNNING) { + dev_err(dai->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return; + } + + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]); + if (rc) { + dev_err(dai->dev, + "%s: dealloc_ch failed, err = %d\n", + __func__, rc); + } + } + + snd_soc_dai_set_dma_data(dai, stream, NULL); + /* clear prepared state for the dai */ + CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED); +} + +static const struct snd_soc_component_driver msm_dai_slim_component = { + .name = "msm-dai-slim-cmpnt", +}; + +static struct snd_soc_dai_ops msm_dai_slim_ops = { + .prepare = msm_dai_slim_prepare, + .hw_params = msm_dai_slim_hw_params, + .shutdown = msm_dai_slim_shutdown, + .set_channel_map = msm_dai_slim_set_channel_map, +}; + +static struct snd_soc_dai_driver msm_slim_dais[] = { + { + /* + * The first dai name should be same as device name + * to support registering single and multile dais. + */ + .name = SLIM_DEV_NAME, + .id = MSM_DAI_SLIM0, + .capture = { + .rates = SLIM_DAI_RATES, + .formats = SLIM_DAI_FORMATS, + .channels_min = 1, + /* + * max channels allowed is + * dependent on platform and + * will be updated before this + * dai driver is registered. + */ + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + .stream_name = "SLIM_DAI0 Capture", + }, + .ops = &msm_dai_slim_ops, + }, + /* + * If multiple dais are needed, + * add dais here and update the + * dai_id enum. + */ +}; + +static void msm_dai_slim_remove_dai_data( + struct device *dev, + struct msm_dai_slim_drv_data *drv_data) +{ + int i; + struct msm_slim_dai_data *dai_data_t; + + for (i = 0; i < drv_data->num_dais; i++) { + dai_data_t = &drv_data->slim_dai_data[i]; + + kfree(dai_data_t->chan_h); + dai_data_t->chan_h = NULL; + kfree(dai_data_t->sh_ch); + dai_data_t->sh_ch = NULL; + } +} + +static int msm_dai_slim_populate_dai_data(struct device *dev, + struct msm_dai_slim_drv_data *drv_data) +{ + struct snd_soc_dai_driver *dai_drv; + struct msm_slim_dai_data *dai_data_t; + u8 num_ch; + int i, j, rc; + + for (i = 0; i < drv_data->num_dais; i++) { + num_ch = 0; + dai_drv = &msm_slim_dais[i]; + num_ch += dai_drv->capture.channels_max; + num_ch += dai_drv->playback.channels_max; + + dai_data_t = &drv_data->slim_dai_data[i]; + dai_data_t->dai_drv = dai_drv; + dai_data_t->dai_id = dai_drv->id; + dai_data_t->dma_data.sdev = drv_data->sdev; + dai_data_t->dma_data.dai_channel_ctl = + msm_dai_slim_ch_ctl; + SET_DAI_STATE(dai_data_t->status, + DAI_STATE_INITIALIZED); + + dai_data_t->chan_h = devm_kzalloc(dev, + sizeof(u16) * num_ch, + GFP_KERNEL); + if (!dai_data_t->chan_h) { + dev_err(dev, + "%s: DAI ID %d, Failed to alloc channel handles\n", + __func__, i); + rc = -ENOMEM; + goto err_mem_alloc; + } + + dai_data_t->sh_ch = devm_kzalloc(dev, + sizeof(u16) * num_ch, + GFP_KERNEL); + if (!dai_data_t->sh_ch) { + dev_err(dev, + "%s: DAI ID %d, Failed to alloc sh_ch\n", + __func__, i); + rc = -ENOMEM; + goto err_mem_alloc; + } + } + return 0; + +err_mem_alloc: + for (j = 0; j < i; j++) { + dai_data_t = &drv_data->slim_dai_data[i]; + + devm_kfree(dev, dai_data_t->chan_h); + dai_data_t->chan_h = NULL; + + devm_kfree(dev, dai_data_t->sh_ch); + dai_data_t->sh_ch = NULL; + } + return rc; +} + +static int msm_dai_slim_dev_probe(struct slim_device *sdev) +{ + int rc, i; + u8 max_channels; + u32 apps_ch_pipes; + struct msm_dai_slim_drv_data *drv_data; + struct device *dev = &sdev->dev; + struct snd_soc_dai_driver *dai_drv; + + if (!dev->of_node || + !dev->of_node->parent) { + dev_err(dev, + "%s: Invalid %s\n", __func__, + (!dev->of_node) ? "of_node" : "parent_of_node"); + return -EINVAL; + } + + rc = of_property_read_u32(dev->of_node->parent, + "qcom,apps-ch-pipes", + &apps_ch_pipes); + if (rc) { + dev_err(dev, + "%s: Failed to lookup property %s in node %s, err = %d\n", + __func__, "qcom,apps-ch-pipes", + dev->of_node->parent->full_name, rc); + goto err_ret; + } + + max_channels = hweight_long(apps_ch_pipes); + if (max_channels <= 0) { + dev_err(dev, + "%s: Invalid apps owned ports %d\n", + __func__, max_channels); + goto err_ret; + } + + dev_dbg(dev, "%s: max channels = %u\n", + __func__, max_channels); + + for (i = 0; i < ARRAY_SIZE(msm_slim_dais); i++) { + dai_drv = &msm_slim_dais[i]; + dai_drv->capture.channels_max = max_channels; + dai_drv->playback.channels_max = max_channels; + } + + drv_data = devm_kzalloc(dev, sizeof(*drv_data), + GFP_KERNEL); + if (!drv_data) { + rc = -ENOMEM; + goto err_ret; + } + + drv_data->sdev = sdev; + drv_data->num_dais = NUM_SLIM_DAIS; + + rc = msm_dai_slim_populate_dai_data(dev, drv_data); + if (rc) { + dev_err(dev, + "%s: failed to setup dai_data, err = %d\n", + __func__, rc); + goto err_populate_dai; + } + + rc = snd_soc_register_component(&sdev->dev, &msm_dai_slim_component, + msm_slim_dais, NUM_SLIM_DAIS); + + if (IS_ERR_VALUE(rc)) { + dev_err(dev, "%s: failed to register DAI, err = %d\n", + __func__, rc); + goto err_reg_comp; + } + + dev_set_drvdata(dev, drv_data); + return rc; + +err_reg_comp: + msm_dai_slim_remove_dai_data(dev, drv_data); + +err_populate_dai: + devm_kfree(dev, drv_data); + +err_ret: + return rc; +} + +static int msm_dai_slim_dev_remove(struct slim_device *sdev) +{ + snd_soc_unregister_component(&sdev->dev); + return 0; +} + +static const struct slim_device_id msm_dai_slim_dt_match[] = { + {SLIM_DEV_NAME, 0 }, + {} +}; + +static struct slim_driver msm_dai_slim_driver = { + .driver = { + .name = SLIM_DEV_NAME, + .owner = THIS_MODULE, + }, + .probe = msm_dai_slim_dev_probe, + .remove = msm_dai_slim_dev_remove, + .id_table = msm_dai_slim_dt_match, +}; + +static int __init msm_dai_slim_init(void) +{ + int rc; + + rc = slim_driver_register(&msm_dai_slim_driver); + if (rc) + pr_err("%s: failed to register with slimbus driver rc = %d", + __func__, rc); + return rc; +} +module_init(msm_dai_slim_init); + +static void __exit msm_dai_slim_exit(void) +{ +} +module_exit(msm_dai_slim_exit); + +/* Module information */ +MODULE_DESCRIPTION("Slimbus apps-owned channel handling driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c new file mode 100644 index 000000000000..3a2c3d3dbbcf --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + STUB_RX, + STUB_TX, + STUB_1_RX, + STUB_1_TX, + STUB_DTMF_TX, + STUB_HOST_RX_CAPTURE_TX, + STUB_HOST_RX_PLAYBACK_RX, + STUB_HOST_TX_CAPTURE_TX, + STUB_HOST_TX_PLAYBACK_RX, +}; + +static int msm_dai_stub_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + pr_debug("%s:\n", __func__); + + return 0; +} + +static struct snd_soc_dai_ops msm_dai_stub_ops = { + .set_channel_map = msm_dai_stub_set_channel_map, +}; + +static int msm_dai_stub_add_route(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai || !dai->driver) { + pr_err("%s Invalid params\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + return 0; +} + +static int msm_dai_stub_dai_probe(struct snd_soc_dai *dai) +{ + return msm_dai_stub_add_route(dai); +} + +static int msm_dai_stub_dai_remove(struct snd_soc_dai *dai) +{ + pr_debug("%s:\n", __func__); + return 0; +} + +static struct snd_soc_dai_driver msm_dai_stub_dai_rx = { + .playback = { + .stream_name = "Stub Playback", + .aif_name = "STUB_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_stub_dai_tx[] = { + { + .capture = { + .stream_name = "Stub Capture", + .aif_name = "STUB_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, + { + .capture = { + .stream_name = "Stub1 Capture", + .aif_name = "STUB_1_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + } +}; + +static struct snd_soc_dai_driver msm_dai_stub_dtmf_tx_dai = { + .capture = { + .stream_name = "DTMF TX", + .aif_name = "STUB_DTMF_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_stub_host_capture_tx_dai[] = { + { + .capture = { + .stream_name = "CS-VOICE HOST RX CAPTURE", + .aif_name = "STUB_HOST_RX_CAPTURE_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, + { + .capture = { + .stream_name = "CS-VOICE HOST TX CAPTURE", + .aif_name = "STUB_HOST_TX_CAPTURE_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_stub_host_playback_rx_dai[] = { + { + .playback = { + .stream_name = "CS-VOICE HOST RX PLAYBACK", + .aif_name = "STUB_HOST_RX_PLAYBACK_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, + { + .playback = { + .stream_name = "CS-VOICE HOST TX PLAYBACK", + .aif_name = "STUB_HOST_TX_PLAYBACK_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, +}; + +static const struct snd_soc_component_driver msm_dai_stub_component = { + .name = "msm-dai-stub-dev", +}; + +static int msm_dai_stub_dev_probe(struct platform_device *pdev) +{ + int rc, id = -1; + const char *stub_dev_id = "qcom,msm-dai-stub-dev-id"; + + rc = of_property_read_u32(pdev->dev.of_node, stub_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, stub_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (id) { + case STUB_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, &msm_dai_stub_dai_rx, 1); + break; + case STUB_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, &msm_dai_stub_dai_tx[0], 1); + break; + case STUB_1_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, &msm_dai_stub_dai_tx[1], 1); + break; + case STUB_DTMF_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_dtmf_tx_dai, 1); + break; + case STUB_HOST_RX_CAPTURE_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_capture_tx_dai[0], 1); + break; + case STUB_HOST_TX_CAPTURE_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_capture_tx_dai[1], 1); + break; + case STUB_HOST_RX_PLAYBACK_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_playback_rx_dai[0], 1); + break; + case STUB_HOST_TX_PLAYBACK_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_playback_rx_dai[1], 1); + break; + } + + return rc; +} + +static int msm_dai_stub_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_stub_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-stub-dev", }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_dai_stub_dev_dt_match); + +static struct platform_driver msm_dai_stub_dev = { + .probe = msm_dai_stub_dev_probe, + .remove = msm_dai_stub_dev_remove, + .driver = { + .name = "msm-dai-stub-dev", + .owner = THIS_MODULE, + .of_match_table = msm_dai_stub_dev_dt_match, + }, +}; + +static int msm_dai_stub_probe(struct platform_device *pdev) +{ + int rc = 0; + + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + + return rc; +} + +static int msm_dai_stub_remove(struct platform_device *pdev) +{ + pr_debug("%s:\n", __func__); + + return 0; +} + +static const struct of_device_id msm_dai_stub_dt_match[] = { + {.compatible = "qcom,msm-dai-stub"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_dai_stub_dt_match); + + +static struct platform_driver msm_dai_stub_driver = { + .probe = msm_dai_stub_probe, + .remove = msm_dai_stub_remove, + .driver = { + .name = "msm-dai-stub", + .owner = THIS_MODULE, + .of_match_table = msm_dai_stub_dt_match, + }, +}; + +static int __init msm_dai_stub_init(void) +{ + int rc = 0; + + pr_debug("%s:\n", __func__); + + rc = platform_driver_register(&msm_dai_stub_driver); + if (rc) { + pr_err("%s: fail to register dai q6 driver", __func__); + goto fail; + } + + rc = platform_driver_register(&msm_dai_stub_dev); + if (rc) { + pr_err("%s: fail to register dai q6 dev driver", __func__); + goto dai_stub_dev_fail; + } + return rc; + +dai_stub_dev_fail: + platform_driver_unregister(&msm_dai_stub_driver); +fail: + return rc; +} +module_init(msm_dai_stub_init); + +static void __exit msm_dai_stub_exit(void) +{ + pr_debug("%s:\n", __func__); + + platform_driver_unregister(&msm_dai_stub_dev); + platform_driver_unregister(&msm_dai_stub_driver); +} +module_exit(msm_dai_stub_exit); + +/* Module information */ +MODULE_DESCRIPTION("MSM Stub DSP DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-common.h b/sound/soc/msm/qdsp6v2/msm-dolby-common.h new file mode 100644 index 000000000000..f14e42e3faa0 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dolby-common.h @@ -0,0 +1,266 @@ + +/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_DOLBY_COMMON_H_ +#define _MSM_DOLBY_COMMON_H_ + +#include + + +#define DOLBY_BUNDLE_MODULE_ID 0x00010723 +#define DOLBY_VISUALIZER_MODULE_ID 0x0001072B + +#define DOLBY_PARAM_ID_VDHE 0x0001074D +#define DOLBY_PARAM_ID_VSPE 0x00010750 +#define DOLBY_PARAM_ID_DSSF 0x00010753 +#define DOLBY_PARAM_ID_DVLI 0x0001073E +#define DOLBY_PARAM_ID_DVLO 0x0001073F +#define DOLBY_PARAM_ID_DVLE 0x0001073C +#define DOLBY_PARAM_ID_DVMC 0x00010741 +#define DOLBY_PARAM_ID_DVME 0x00010740 +#define DOLBY_PARAM_ID_IENB 0x00010744 +#define DOLBY_PARAM_ID_IEBF 0x00010745 +#define DOLBY_PARAM_ID_IEON 0x00010743 +#define DOLBY_PARAM_ID_DEON 0x00010738 +#define DOLBY_PARAM_ID_NGON 0x00010736 +#define DOLBY_PARAM_ID_GEON 0x00010748 +#define DOLBY_PARAM_ID_GENB 0x00010749 +#define DOLBY_PARAM_ID_GEBF 0x0001074A +#define DOLBY_PARAM_ID_AONB 0x0001075B +#define DOLBY_PARAM_ID_AOBF 0x0001075C +#define DOLBY_PARAM_ID_AOBG 0x0001075D +#define DOLBY_PARAM_ID_AOON 0x00010759 +#define DOLBY_PARAM_ID_ARNB 0x0001075F +#define DOLBY_PARAM_ID_ARBF 0x00010760 +#define DOLBY_PARAM_ID_PLB 0x00010768 +#define DOLBY_PARAM_ID_PLMD 0x00010767 +#define DOLBY_PARAM_ID_DHSB 0x0001074E +#define DOLBY_PARAM_ID_DHRG 0x0001074F +#define DOLBY_PARAM_ID_DSSB 0x00010751 +#define DOLBY_PARAM_ID_DSSA 0x00010752 +#define DOLBY_PARAM_ID_DVLA 0x0001073D +#define DOLBY_PARAM_ID_IEBT 0x00010746 +#define DOLBY_PARAM_ID_IEA 0x0001076A +#define DOLBY_PARAM_ID_DEA 0x00010739 +#define DOLBY_PARAM_ID_DED 0x0001073A +#define DOLBY_PARAM_ID_GEBG 0x0001074B +#define DOLBY_PARAM_ID_AOCC 0x0001075A +#define DOLBY_PARAM_ID_ARBI 0x00010761 +#define DOLBY_PARAM_ID_ARBL 0x00010762 +#define DOLBY_PARAM_ID_ARBH 0x00010763 +#define DOLBY_PARAM_ID_AROD 0x00010764 +#define DOLBY_PARAM_ID_ARTP 0x00010765 +#define DOLBY_PARAM_ID_VMON 0x00010756 +#define DOLBY_PARAM_ID_VMB 0x00010757 +#define DOLBY_PARAM_ID_VCNB 0x00010733 +#define DOLBY_PARAM_ID_VCBF 0x00010734 +#define DOLBY_PARAM_ID_PREG 0x00010728 +#define DOLBY_PARAM_ID_VEN 0x00010732 +#define DOLBY_PARAM_ID_PSTG 0x00010729 +#define DOLBY_PARAM_ID_INIT_ENDP 0x00010727 + +/* Not Used with Set Param kcontrol, only to query using Get Param */ +#define DOLBY_PARAM_ID_VER 0x00010726 + +#define DOLBY_PARAM_ID_VCBG 0x00010730 +#define DOLBY_PARAM_ID_VCBE 0x00010731 + +/* DOLBY DAP control params */ +#define DOLBY_COMMIT_ALL_TO_DSP 0x70000001 +#define DOLBY_COMMIT_TO_DSP 0x70000002 +#define DOLBY_USE_CACHE 0x70000003 +#define DOLBY_AUTO_ENDP 0x70000004 +#define DOLBY_AUTO_ENDDEP_PARAMS 0x70000005 +#define DOLBY_DAP_BYPASS 0x70000006 + +#define DOLBY_ENABLE_CUSTOM_STEREO 0x000108c7 + +/* DOLBY DAP offsets start */ +#define DOLBY_PARAM_VDHE_LENGTH 1 +#define DOLBY_PARAM_VDHE_OFFSET 0 +#define DOLBY_PARAM_VSPE_LENGTH 1 +#define DOLBY_PARAM_VSPE_OFFSET (DOLBY_PARAM_VDHE_OFFSET + \ + DOLBY_PARAM_VDHE_LENGTH) +#define DOLBY_PARAM_DSSF_LENGTH 1 +#define DOLBY_PARAM_DSSF_OFFSET (DOLBY_PARAM_VSPE_OFFSET + \ + DOLBY_PARAM_VSPE_LENGTH) +#define DOLBY_PARAM_DVLI_LENGTH 1 +#define DOLBY_PARAM_DVLI_OFFSET (DOLBY_PARAM_DSSF_OFFSET + \ + DOLBY_PARAM_DSSF_LENGTH) +#define DOLBY_PARAM_DVLO_LENGTH 1 +#define DOLBY_PARAM_DVLO_OFFSET (DOLBY_PARAM_DVLI_OFFSET + \ + DOLBY_PARAM_DVLI_LENGTH) +#define DOLBY_PARAM_DVLE_LENGTH 1 +#define DOLBY_PARAM_DVLE_OFFSET (DOLBY_PARAM_DVLO_OFFSET + \ + DOLBY_PARAM_DVLO_LENGTH) +#define DOLBY_PARAM_DVMC_LENGTH 1 +#define DOLBY_PARAM_DVMC_OFFSET (DOLBY_PARAM_DVLE_OFFSET + \ + DOLBY_PARAM_DVLE_LENGTH) +#define DOLBY_PARAM_DVME_LENGTH 1 +#define DOLBY_PARAM_DVME_OFFSET (DOLBY_PARAM_DVMC_OFFSET + \ + DOLBY_PARAM_DVMC_LENGTH) +#define DOLBY_PARAM_IENB_LENGTH 1 +#define DOLBY_PARAM_IENB_OFFSET (DOLBY_PARAM_DVME_OFFSET + \ + DOLBY_PARAM_DVME_LENGTH) +#define DOLBY_PARAM_IEBF_LENGTH 40 +#define DOLBY_PARAM_IEBF_OFFSET (DOLBY_PARAM_IENB_OFFSET + \ + DOLBY_PARAM_IENB_LENGTH) +#define DOLBY_PARAM_IEON_LENGTH 1 +#define DOLBY_PARAM_IEON_OFFSET (DOLBY_PARAM_IEBF_OFFSET + \ + DOLBY_PARAM_IEBF_LENGTH) +#define DOLBY_PARAM_DEON_LENGTH 1 +#define DOLBY_PARAM_DEON_OFFSET (DOLBY_PARAM_IEON_OFFSET + \ + DOLBY_PARAM_IEON_LENGTH) +#define DOLBY_PARAM_NGON_LENGTH 1 +#define DOLBY_PARAM_NGON_OFFSET (DOLBY_PARAM_DEON_OFFSET + \ + DOLBY_PARAM_DEON_LENGTH) +#define DOLBY_PARAM_GEON_LENGTH 1 +#define DOLBY_PARAM_GEON_OFFSET (DOLBY_PARAM_NGON_OFFSET + \ + DOLBY_PARAM_NGON_LENGTH) +#define DOLBY_PARAM_GENB_LENGTH 1 +#define DOLBY_PARAM_GENB_OFFSET (DOLBY_PARAM_GEON_OFFSET + \ + DOLBY_PARAM_GEON_LENGTH) +#define DOLBY_PARAM_GEBF_LENGTH 40 +#define DOLBY_PARAM_GEBF_OFFSET (DOLBY_PARAM_GENB_OFFSET + \ + DOLBY_PARAM_GENB_LENGTH) +#define DOLBY_PARAM_AONB_LENGTH 1 +#define DOLBY_PARAM_AONB_OFFSET (DOLBY_PARAM_GEBF_OFFSET + \ + DOLBY_PARAM_GEBF_LENGTH) +#define DOLBY_PARAM_AOBF_LENGTH 40 +#define DOLBY_PARAM_AOBF_OFFSET (DOLBY_PARAM_AONB_OFFSET + \ + DOLBY_PARAM_AONB_LENGTH) +#define DOLBY_PARAM_AOBG_LENGTH 329 +#define DOLBY_PARAM_AOBG_OFFSET (DOLBY_PARAM_AOBF_OFFSET + \ + DOLBY_PARAM_AOBF_LENGTH) +#define DOLBY_PARAM_AOON_LENGTH 1 +#define DOLBY_PARAM_AOON_OFFSET (DOLBY_PARAM_AOBG_OFFSET + \ + DOLBY_PARAM_AOBG_LENGTH) +#define DOLBY_PARAM_ARNB_LENGTH 1 +#define DOLBY_PARAM_ARNB_OFFSET (DOLBY_PARAM_AOON_OFFSET + \ + DOLBY_PARAM_AOON_LENGTH) +#define DOLBY_PARAM_ARBF_LENGTH 40 +#define DOLBY_PARAM_ARBF_OFFSET (DOLBY_PARAM_ARNB_OFFSET + \ + DOLBY_PARAM_ARNB_LENGTH) +#define DOLBY_PARAM_PLB_LENGTH 1 +#define DOLBY_PARAM_PLB_OFFSET (DOLBY_PARAM_ARBF_OFFSET + \ + DOLBY_PARAM_ARBF_LENGTH) +#define DOLBY_PARAM_PLMD_LENGTH 1 +#define DOLBY_PARAM_PLMD_OFFSET (DOLBY_PARAM_PLB_OFFSET + \ + DOLBY_PARAM_PLB_LENGTH) +#define DOLBY_PARAM_DHSB_LENGTH 1 +#define DOLBY_PARAM_DHSB_OFFSET (DOLBY_PARAM_PLMD_OFFSET + \ + DOLBY_PARAM_PLMD_LENGTH) +#define DOLBY_PARAM_DHRG_LENGTH 1 +#define DOLBY_PARAM_DHRG_OFFSET (DOLBY_PARAM_DHSB_OFFSET + \ + DOLBY_PARAM_DHSB_LENGTH) +#define DOLBY_PARAM_DSSB_LENGTH 1 +#define DOLBY_PARAM_DSSB_OFFSET (DOLBY_PARAM_DHRG_OFFSET + \ + DOLBY_PARAM_DHRG_LENGTH) +#define DOLBY_PARAM_DSSA_LENGTH 1 +#define DOLBY_PARAM_DSSA_OFFSET (DOLBY_PARAM_DSSB_OFFSET + \ + DOLBY_PARAM_DSSB_LENGTH) +#define DOLBY_PARAM_DVLA_LENGTH 1 +#define DOLBY_PARAM_DVLA_OFFSET (DOLBY_PARAM_DSSA_OFFSET + \ + DOLBY_PARAM_DSSA_LENGTH) +#define DOLBY_PARAM_IEBT_LENGTH 40 +#define DOLBY_PARAM_IEBT_OFFSET (DOLBY_PARAM_DVLA_OFFSET + \ + DOLBY_PARAM_DVLA_LENGTH) +#define DOLBY_PARAM_IEA_LENGTH 1 +#define DOLBY_PARAM_IEA_OFFSET (DOLBY_PARAM_IEBT_OFFSET + \ + DOLBY_PARAM_IEBT_LENGTH) +#define DOLBY_PARAM_DEA_LENGTH 1 +#define DOLBY_PARAM_DEA_OFFSET (DOLBY_PARAM_IEA_OFFSET + \ + DOLBY_PARAM_IEA_LENGTH) +#define DOLBY_PARAM_DED_LENGTH 1 +#define DOLBY_PARAM_DED_OFFSET (DOLBY_PARAM_DEA_OFFSET + \ + DOLBY_PARAM_DEA_LENGTH) +#define DOLBY_PARAM_GEBG_LENGTH 40 +#define DOLBY_PARAM_GEBG_OFFSET (DOLBY_PARAM_DED_OFFSET + \ + DOLBY_PARAM_DED_LENGTH) +#define DOLBY_PARAM_AOCC_LENGTH 1 +#define DOLBY_PARAM_AOCC_OFFSET (DOLBY_PARAM_GEBG_OFFSET + \ + DOLBY_PARAM_GEBG_LENGTH) +#define DOLBY_PARAM_ARBI_LENGTH 40 +#define DOLBY_PARAM_ARBI_OFFSET (DOLBY_PARAM_AOCC_OFFSET + \ + DOLBY_PARAM_AOCC_LENGTH) +#define DOLBY_PARAM_ARBL_LENGTH 40 +#define DOLBY_PARAM_ARBL_OFFSET (DOLBY_PARAM_ARBI_OFFSET + \ + DOLBY_PARAM_ARBI_LENGTH) +#define DOLBY_PARAM_ARBH_LENGTH 40 +#define DOLBY_PARAM_ARBH_OFFSET (DOLBY_PARAM_ARBL_OFFSET + \ + DOLBY_PARAM_ARBL_LENGTH) +#define DOLBY_PARAM_AROD_LENGTH 1 +#define DOLBY_PARAM_AROD_OFFSET (DOLBY_PARAM_ARBH_OFFSET + \ + DOLBY_PARAM_ARBH_LENGTH) +#define DOLBY_PARAM_ARTP_LENGTH 1 +#define DOLBY_PARAM_ARTP_OFFSET (DOLBY_PARAM_AROD_OFFSET + \ + DOLBY_PARAM_AROD_LENGTH) +#define DOLBY_PARAM_VMON_LENGTH 1 +#define DOLBY_PARAM_VMON_OFFSET (DOLBY_PARAM_ARTP_OFFSET + \ + DOLBY_PARAM_ARTP_LENGTH) +#define DOLBY_PARAM_VMB_LENGTH 1 +#define DOLBY_PARAM_VMB_OFFSET (DOLBY_PARAM_VMON_OFFSET + \ + DOLBY_PARAM_VMON_LENGTH) +#define DOLBY_PARAM_VCNB_LENGTH 1 +#define DOLBY_PARAM_VCNB_OFFSET (DOLBY_PARAM_VMB_OFFSET + \ + DOLBY_PARAM_VMB_LENGTH) +#define DOLBY_PARAM_VCBF_LENGTH 20 +#define DOLBY_PARAM_VCBF_OFFSET (DOLBY_PARAM_VCNB_OFFSET + \ + DOLBY_PARAM_VCNB_LENGTH) +#define DOLBY_PARAM_PREG_LENGTH 1 +#define DOLBY_PARAM_PREG_OFFSET (DOLBY_PARAM_VCBF_OFFSET + \ + DOLBY_PARAM_VCBF_LENGTH) +#define DOLBY_PARAM_VEN_LENGTH 1 +#define DOLBY_PARAM_VEN_OFFSET (DOLBY_PARAM_PREG_OFFSET + \ + DOLBY_PARAM_PREG_LENGTH) +#define DOLBY_PARAM_PSTG_LENGTH 1 +#define DOLBY_PARAM_PSTG_OFFSET (DOLBY_PARAM_VEN_OFFSET + \ + DOLBY_PARAM_VEN_LENGTH) + +#define DOLBY_PARAM_INT_ENDP_LENGTH 1 +#define DOLBY_PARAM_PAYLOAD_SIZE 3 +#define DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM 329 + +#define TOTAL_LENGTH_DOLBY_PARAM 745 +#define DOLBY_VIS_PARAM_HEADER_SIZE 25 +#define DOLBY_PARAM_VCNB_MAX_LENGTH 40 + +#define DOLBY_INVALID_PORT_ID -1 + +enum { + DEVICE_NONE = 0x0, + /* output devices */ + EARPIECE = 0x1, + SPEAKER = 0x2, + WIRED_HEADSET = 0x4, + WIRED_HEADPHONE = 0x8, + BLUETOOTH_SCO = 0x10, + BLUETOOTH_SCO_HEADSET = 0x20, + BLUETOOTH_SCO_CARKIT = 0x40, + BLUETOOTH_A2DP = 0x80, + BLUETOOTH_A2DP_HEADPHONES = 0x100, + BLUETOOTH_A2DP_SPEAKER = 0x200, + AUX_DIGITAL = 0x400, + ANLG_DOCK_HEADSET = 0x800, + DGTL_DOCK_HEADSET = 0x1000, + USB_ACCESSORY = 0x2000, + USB_DEVICE = 0x4000, + REMOTE_SUBMIX = 0x8000, + ANC_HEADSET = 0x10000, + ANC_HEADPHONE = 0x20000, + PROXY = 0x2000000, + FM = 0x100000, + FM_TX = 0x1000000, + DEVICE_OUT_DEFAULT = 0x40000000, + DEVICE_OUT_ALL = 0x403FFFFF, +}; +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c new file mode 100644 index 000000000000..29a1b3d9bd91 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c @@ -0,0 +1,1071 @@ +/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "msm-dolby-dap-config.h" + +/* dolby endp based parameters */ +struct dolby_dap_endp_params_s { + int device; + int device_ch_caps; + int dap_device; + int params_id[DOLBY_NUM_ENDP_DEPENDENT_PARAMS]; + int params_len[DOLBY_NUM_ENDP_DEPENDENT_PARAMS]; + int params_offset[DOLBY_NUM_ENDP_DEPENDENT_PARAMS]; + int params_val[DOLBY_ENDDEP_PARAM_LENGTH]; +}; + +const struct dolby_dap_endp_params_s + dolby_dap_endp_params[NUM_DOLBY_ENDP_DEVICE] = { + {EARPIECE, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {SPEAKER, 2, DOLBY_ENDP_INT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {WIRED_HEADSET, 2, DOLBY_ENDP_HEADPHONES, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {WIRED_HEADPHONE, 2, DOLBY_ENDP_HEADPHONES, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {BLUETOOTH_SCO, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {BLUETOOTH_SCO_HEADSET, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {BLUETOOTH_SCO_CARKIT, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {BLUETOOTH_A2DP, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {BLUETOOTH_A2DP_HEADPHONES, 2, DOLBY_ENDP_HEADPHONES, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {BLUETOOTH_A2DP_SPEAKER, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {AUX_DIGITAL, 2, DOLBY_ENDP_HDMI, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-496, -496, 0} + }, + {AUX_DIGITAL, 6, DOLBY_ENDP_HDMI, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-496, -496, 0} + }, + {AUX_DIGITAL, 8, DOLBY_ENDP_HDMI, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-496, -496, 0} + }, + {ANLG_DOCK_HEADSET, 2, DOLBY_ENDP_HEADPHONES, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {DGTL_DOCK_HEADSET, 2, DOLBY_ENDP_HEADPHONES, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {USB_ACCESSORY, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {USB_DEVICE, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {REMOTE_SUBMIX, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {PROXY, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {PROXY, 6, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {FM, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, + {FM_TX, 2, DOLBY_ENDP_EXT_SPEAKERS, + {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB}, + {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH, + DOLBY_ENDDEP_PARAM_VMB_LENGTH}, + {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET, + DOLBY_ENDDEP_PARAM_VMB_OFFSET}, + {-320, -320, 144} + }, +}; + +/* dolby param ids to/from dsp */ +static uint32_t dolby_dap_params_id[ALL_DOLBY_PARAMS] = { + DOLBY_PARAM_ID_VDHE, DOLBY_PARAM_ID_VSPE, DOLBY_PARAM_ID_DSSF, + DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLE, + DOLBY_PARAM_ID_DVMC, DOLBY_PARAM_ID_DVME, DOLBY_PARAM_ID_IENB, + DOLBY_PARAM_ID_IEBF, DOLBY_PARAM_ID_IEON, DOLBY_PARAM_ID_DEON, + DOLBY_PARAM_ID_NGON, DOLBY_PARAM_ID_GEON, DOLBY_PARAM_ID_GENB, + DOLBY_PARAM_ID_GEBF, DOLBY_PARAM_ID_AONB, DOLBY_PARAM_ID_AOBF, + DOLBY_PARAM_ID_AOBG, DOLBY_PARAM_ID_AOON, DOLBY_PARAM_ID_ARNB, + DOLBY_PARAM_ID_ARBF, DOLBY_PARAM_ID_PLB, DOLBY_PARAM_ID_PLMD, + DOLBY_PARAM_ID_DHSB, DOLBY_PARAM_ID_DHRG, DOLBY_PARAM_ID_DSSB, + DOLBY_PARAM_ID_DSSA, DOLBY_PARAM_ID_DVLA, DOLBY_PARAM_ID_IEBT, + DOLBY_PARAM_ID_IEA, DOLBY_PARAM_ID_DEA, DOLBY_PARAM_ID_DED, + DOLBY_PARAM_ID_GEBG, DOLBY_PARAM_ID_AOCC, DOLBY_PARAM_ID_ARBI, + DOLBY_PARAM_ID_ARBL, DOLBY_PARAM_ID_ARBH, DOLBY_PARAM_ID_AROD, + DOLBY_PARAM_ID_ARTP, DOLBY_PARAM_ID_VMON, DOLBY_PARAM_ID_VMB, + DOLBY_PARAM_ID_VCNB, DOLBY_PARAM_ID_VCBF, DOLBY_PARAM_ID_PREG, + DOLBY_PARAM_ID_VEN, DOLBY_PARAM_ID_PSTG, DOLBY_COMMIT_ALL_TO_DSP, + DOLBY_COMMIT_TO_DSP, DOLBY_USE_CACHE, DOLBY_AUTO_ENDP, + DOLBY_AUTO_ENDDEP_PARAMS +}; + +/* modifed state: 0x00000000 - Not updated + * > 0x00000000 && < 0x00010000 + * Updated and not committed to DSP + * 0x00010001 - Updated and committed to DSP + * > 0x00010001 - Modified the committed value + */ +static int dolby_dap_params_modified[MAX_DOLBY_PARAMS] = { 0 }; +/* param offset */ +static uint32_t dolby_dap_params_offset[MAX_DOLBY_PARAMS] = { + DOLBY_PARAM_VDHE_OFFSET, DOLBY_PARAM_VSPE_OFFSET, + DOLBY_PARAM_DSSF_OFFSET, DOLBY_PARAM_DVLI_OFFSET, + DOLBY_PARAM_DVLO_OFFSET, DOLBY_PARAM_DVLE_OFFSET, + DOLBY_PARAM_DVMC_OFFSET, DOLBY_PARAM_DVME_OFFSET, + DOLBY_PARAM_IENB_OFFSET, DOLBY_PARAM_IEBF_OFFSET, + DOLBY_PARAM_IEON_OFFSET, DOLBY_PARAM_DEON_OFFSET, + DOLBY_PARAM_NGON_OFFSET, DOLBY_PARAM_GEON_OFFSET, + DOLBY_PARAM_GENB_OFFSET, DOLBY_PARAM_GEBF_OFFSET, + DOLBY_PARAM_AONB_OFFSET, DOLBY_PARAM_AOBF_OFFSET, + DOLBY_PARAM_AOBG_OFFSET, DOLBY_PARAM_AOON_OFFSET, + DOLBY_PARAM_ARNB_OFFSET, DOLBY_PARAM_ARBF_OFFSET, + DOLBY_PARAM_PLB_OFFSET, DOLBY_PARAM_PLMD_OFFSET, + DOLBY_PARAM_DHSB_OFFSET, DOLBY_PARAM_DHRG_OFFSET, + DOLBY_PARAM_DSSB_OFFSET, DOLBY_PARAM_DSSA_OFFSET, + DOLBY_PARAM_DVLA_OFFSET, DOLBY_PARAM_IEBT_OFFSET, + DOLBY_PARAM_IEA_OFFSET, DOLBY_PARAM_DEA_OFFSET, + DOLBY_PARAM_DED_OFFSET, DOLBY_PARAM_GEBG_OFFSET, + DOLBY_PARAM_AOCC_OFFSET, DOLBY_PARAM_ARBI_OFFSET, + DOLBY_PARAM_ARBL_OFFSET, DOLBY_PARAM_ARBH_OFFSET, + DOLBY_PARAM_AROD_OFFSET, DOLBY_PARAM_ARTP_OFFSET, + DOLBY_PARAM_VMON_OFFSET, DOLBY_PARAM_VMB_OFFSET, + DOLBY_PARAM_VCNB_OFFSET, DOLBY_PARAM_VCBF_OFFSET, + DOLBY_PARAM_PREG_OFFSET, DOLBY_PARAM_VEN_OFFSET, + DOLBY_PARAM_PSTG_OFFSET +}; +/* param_length */ +static uint32_t dolby_dap_params_length[MAX_DOLBY_PARAMS] = { + DOLBY_PARAM_VDHE_LENGTH, DOLBY_PARAM_VSPE_LENGTH, + DOLBY_PARAM_DSSF_LENGTH, DOLBY_PARAM_DVLI_LENGTH, + DOLBY_PARAM_DVLO_LENGTH, DOLBY_PARAM_DVLE_LENGTH, + DOLBY_PARAM_DVMC_LENGTH, DOLBY_PARAM_DVME_LENGTH, + DOLBY_PARAM_IENB_LENGTH, DOLBY_PARAM_IEBF_LENGTH, + DOLBY_PARAM_IEON_LENGTH, DOLBY_PARAM_DEON_LENGTH, + DOLBY_PARAM_NGON_LENGTH, DOLBY_PARAM_GEON_LENGTH, + DOLBY_PARAM_GENB_LENGTH, DOLBY_PARAM_GEBF_LENGTH, + DOLBY_PARAM_AONB_LENGTH, DOLBY_PARAM_AOBF_LENGTH, + DOLBY_PARAM_AOBG_LENGTH, DOLBY_PARAM_AOON_LENGTH, + DOLBY_PARAM_ARNB_LENGTH, DOLBY_PARAM_ARBF_LENGTH, + DOLBY_PARAM_PLB_LENGTH, DOLBY_PARAM_PLMD_LENGTH, + DOLBY_PARAM_DHSB_LENGTH, DOLBY_PARAM_DHRG_LENGTH, + DOLBY_PARAM_DSSB_LENGTH, DOLBY_PARAM_DSSA_LENGTH, + DOLBY_PARAM_DVLA_LENGTH, DOLBY_PARAM_IEBT_LENGTH, + DOLBY_PARAM_IEA_LENGTH, DOLBY_PARAM_DEA_LENGTH, + DOLBY_PARAM_DED_LENGTH, DOLBY_PARAM_GEBG_LENGTH, + DOLBY_PARAM_AOCC_LENGTH, DOLBY_PARAM_ARBI_LENGTH, + DOLBY_PARAM_ARBL_LENGTH, DOLBY_PARAM_ARBH_LENGTH, + DOLBY_PARAM_AROD_LENGTH, DOLBY_PARAM_ARTP_LENGTH, + DOLBY_PARAM_VMON_LENGTH, DOLBY_PARAM_VMB_LENGTH, + DOLBY_PARAM_VCNB_LENGTH, DOLBY_PARAM_VCBF_LENGTH, + DOLBY_PARAM_PREG_LENGTH, DOLBY_PARAM_VEN_LENGTH, + DOLBY_PARAM_PSTG_LENGTH +}; + +/* param_value */ +static uint32_t dolby_dap_params_value[TOTAL_LENGTH_DOLBY_PARAM] = {0}; + +struct dolby_dap_params_get_s { + int32_t port_id; + uint32_t device_id; + uint32_t param_id; + uint32_t offset; + uint32_t length; +}; + +struct dolby_dap_params_states_s { + bool use_cache; + bool auto_endp; + bool enddep_params; + int port_id[AFE_MAX_PORTS]; + int copp_idx[AFE_MAX_PORTS]; + int port_open_count; + int port_ids_dolby_can_be_enabled; + int device; +}; + +static struct dolby_dap_params_get_s dolby_dap_params_get = {-1, DEVICE_OUT_ALL, + 0, 0, 0}; +static struct dolby_dap_params_states_s dolby_dap_params_states = { true, true, + true, {DOLBY_INVALID_PORT_ID}, + {-1}, 0, DEVICE_OUT_ALL, 0 }; +/* + * port_ids_dolby_can_be_enabled is set to 0x7FFFFFFF. + * this needs to be removed after interface validation + */ + +static int msm_dolby_dap_map_device_to_dolby_endpoint(int device) +{ + int i, dolby_dap_device = DOLBY_ENDP_EXT_SPEAKERS; + + for (i = 0; i < NUM_DOLBY_ENDP_DEVICE; i++) { + if (dolby_dap_endp_params[i].device == device) { + dolby_dap_device = dolby_dap_endp_params[i].dap_device; + break; + } + } + /* default the endpoint to speaker if corresponding device entry */ + /* not found */ + if (i >= NUM_DOLBY_ENDP_DEVICE) + dolby_dap_params_states.device = SPEAKER; + return dolby_dap_device; +} + +static int msm_dolby_dap_send_end_point(int port_id, int copp_idx) +{ + int rc = 0; + char *params_value; + int *update_params_value; + uint32_t params_length = (DOLBY_PARAM_INT_ENDP_LENGTH + + DOLBY_PARAM_PAYLOAD_SIZE) * sizeof(uint32_t); + + pr_debug("%s\n", __func__); + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed", __func__); + return -ENOMEM; + } + update_params_value = (int *)params_value; + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = DOLBY_PARAM_ID_INIT_ENDP; + *update_params_value++ = DOLBY_PARAM_INT_ENDP_LENGTH * sizeof(uint32_t); + *update_params_value++ = + msm_dolby_dap_map_device_to_dolby_endpoint( + dolby_dap_params_states.device); + rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value, + params_length); + if (rc) { + pr_err("%s: send dolby params failed\n", __func__); + rc = -EINVAL; + } + kfree(params_value); + return rc; +} + +static int msm_dolby_dap_send_enddep_params(int port_id, int copp_idx, + int device_channels) +{ + int i, j, rc = 0, idx, offset; + char *params_value; + int *update_params_value; + uint32_t params_length = (DOLBY_ENDDEP_PARAM_LENGTH + + DOLBY_NUM_ENDP_DEPENDENT_PARAMS * + DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + + pr_debug("%s\n", __func__); + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed", __func__); + return -ENOMEM; + } + update_params_value = (int *)params_value; + for (idx = 0; idx < NUM_DOLBY_ENDP_DEVICE; idx++) { + if (dolby_dap_endp_params[idx].device == + dolby_dap_params_states.device) { + if (dolby_dap_params_states.device == AUX_DIGITAL || + dolby_dap_params_states.device == PROXY) { + if (dolby_dap_endp_params[idx].device_ch_caps == + device_channels) + break; + } else { + break; + } + } + } + if (idx >= NUM_DOLBY_ENDP_DEVICE) { + pr_err("%s: device is not set accordingly\n", __func__); + kfree(params_value); + return -EINVAL; + } + for (i = 0; i < DOLBY_ENDDEP_PARAM_LENGTH; i++) { + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = + dolby_dap_endp_params[idx].params_id[i]; + *update_params_value++ = + dolby_dap_endp_params[idx].params_len[i] * + sizeof(uint32_t); + offset = dolby_dap_endp_params[idx].params_offset[i]; + for (j = 0; j < dolby_dap_endp_params[idx].params_len[i]; j++) + *update_params_value++ = + dolby_dap_endp_params[idx].params_val[offset+j]; + } + rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value, + params_length); + if (rc) { + pr_err("%s: send dolby params failed\n", __func__); + rc = -EINVAL; + } + kfree(params_value); + return rc; +} + +static int msm_dolby_dap_send_cached_params(int port_id, int copp_idx, + int commit) +{ + char *params_value; + int *update_params_value, rc = 0; + uint32_t index_offset, i, j; + uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM + + MAX_DOLBY_PARAMS * DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + update_params_value = (int *)params_value; + params_length = 0; + for (i = 0; i < MAX_DOLBY_PARAMS; i++) { + if ((dolby_dap_params_modified[i] == 0) || + ((commit) && + ((dolby_dap_params_modified[i] & 0x00010000) && + ((dolby_dap_params_modified[i] & 0x0000FFFF) <= 1)))) + continue; + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = dolby_dap_params_id[i]; + *update_params_value++ = dolby_dap_params_length[i] * + sizeof(uint32_t); + index_offset = dolby_dap_params_offset[i]; + for (j = 0; j < dolby_dap_params_length[i]; j++) { + *update_params_value++ = + dolby_dap_params_value[index_offset+j]; + } + params_length += (DOLBY_PARAM_PAYLOAD_SIZE + + dolby_dap_params_length[i]) * sizeof(uint32_t); + } + pr_debug("%s, valid param length: %d", __func__, params_length); + if (params_length) { + rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value, + params_length); + if (rc) { + pr_err("%s: send dolby params failed\n", __func__); + kfree(params_value); + return -EINVAL; + } + for (i = 0; i < MAX_DOLBY_PARAMS; i++) { + if ((dolby_dap_params_modified[i] == 0) || + ((commit) && + ((dolby_dap_params_modified[i] & 0x00010000) && + ((dolby_dap_params_modified[i] & 0x0000FFFF) <= 1)) + )) + continue; + dolby_dap_params_modified[i] = 0x00010001; + } + } + kfree(params_value); + return 0; +} + +int msm_dolby_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + int ret = 0; + int index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return -EINVAL; + } + if ((port_id != DOLBY_INVALID_PORT_ID) && + (port_id & dolby_dap_params_states.port_ids_dolby_can_be_enabled)) { + dolby_dap_params_states.port_id[index] = port_id; + dolby_dap_params_states.copp_idx[index] = copp_idx; + dolby_dap_params_states.port_open_count++; + if (dolby_dap_params_states.auto_endp) { + ret = msm_dolby_dap_send_end_point(port_id, copp_idx); + if (ret) { + pr_err("%s: err sending endppoint\n", __func__); + return ret; + } + } + if (dolby_dap_params_states.use_cache) { + ret = msm_dolby_dap_send_cached_params(port_id, + copp_idx, 0); + if (ret) { + pr_err("%s: err sending cached params\n", + __func__); + return ret; + } + } + if (dolby_dap_params_states.enddep_params) { + msm_dolby_dap_send_enddep_params(port_id, copp_idx, + channels); + if (ret) { + pr_err("%s: err sending endp dependent params\n", + __func__); + return ret; + } + } + if (is_custom_stereo_on) + dolby_dap_set_custom_stereo_onoff(port_id, copp_idx, + is_custom_stereo_on); + } + return ret; +} + +void msm_dolby_dap_deinit(int port_id) +{ + int index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return; + } + dolby_dap_params_states.port_open_count--; + if ((dolby_dap_params_states.port_id[index] == port_id) && + (!dolby_dap_params_states.port_open_count)) { + dolby_dap_params_states.port_id[index] = DOLBY_INVALID_PORT_ID; + dolby_dap_params_states.copp_idx[index] = -1; + } +} + +static int msm_dolby_dap_set_vspe_vdhe(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + char *params_value; + int *update_params_value, rc = 0; + uint32_t index_offset, i, j; + uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM + + 2 * DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: Not a Dolby topology. Do not set custom stereo mixing\n", + __func__); + return -EINVAL; + } + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + update_params_value = (int *)params_value; + params_length = 0; + /* for VDHE and VSPE DAP params at index 0 and 1 in table */ + for (i = 0; i < 2; i++) { + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = dolby_dap_params_id[i]; + *update_params_value++ = dolby_dap_params_length[i] * + sizeof(uint32_t); + index_offset = dolby_dap_params_offset[i]; + for (j = 0; j < dolby_dap_params_length[i]; j++) { + if (is_custom_stereo_enabled) + *update_params_value++ = 0; + else + *update_params_value++ = + dolby_dap_params_value[index_offset+j]; + } + params_length += (DOLBY_PARAM_PAYLOAD_SIZE + + dolby_dap_params_length[i]) * sizeof(uint32_t); + } + pr_debug("%s, valid param length: %d", __func__, params_length); + if (params_length) { + rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value, + params_length); + if (rc) { + pr_err("%s: send vdhe/vspe params failed with rc=%d\n", + __func__, rc); + kfree(params_value); + return -EINVAL; + } + } + kfree(params_value); + return 0; +} + +int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + char *params_value; + int *update_params_value, rc = 0; + uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM + + DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + if (port_id == DOLBY_INVALID_PORT_ID) + return -EINVAL; + + msm_dolby_dap_set_vspe_vdhe(port_id, copp_idx, + is_custom_stereo_enabled); + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + update_params_value = (int *)params_value; + params_length = 0; + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = DOLBY_ENABLE_CUSTOM_STEREO; + *update_params_value++ = sizeof(uint32_t); + if (is_custom_stereo_enabled) + *update_params_value++ = 1; + else + *update_params_value++ = 0; + params_length += (DOLBY_PARAM_PAYLOAD_SIZE + 1) * sizeof(uint32_t); + pr_debug("%s, valid param length: %d", __func__, params_length); + if (params_length) { + rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value, + params_length); + if (rc) { + pr_err("%s: setting ds1 custom stereo param failed with rc=%d\n", + __func__, rc); + kfree(params_value); + return -EINVAL; + } + } + kfree(params_value); + return 0; +} + +static int msm_dolby_dap_map_device_to_port_id(int device) +{ + int port_id = SLIMBUS_0_RX; + + device = DEVICE_OUT_ALL; + /*update the device when single stream to multiple device is handled*/ + if (device == DEVICE_OUT_ALL) { + port_id = PRIMARY_I2S_RX | SLIMBUS_0_RX | HDMI_RX | + INT_BT_SCO_RX | INT_FM_RX | + RT_PROXY_PORT_001_RX | + AFE_PORT_ID_PRIMARY_PCM_RX | + MI2S_RX | SECONDARY_I2S_RX | + SLIMBUS_1_RX | SLIMBUS_4_RX | SLIMBUS_3_RX | + AFE_PORT_ID_SECONDARY_MI2S_RX; + } else { + /* update port_id based on the device */ + } + return port_id; +} + +int msm_dolby_dap_param_to_set_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* not used while setting the parameters */ + return 0; +} + +int msm_dolby_dap_param_to_set_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0, port_id, copp_idx; + uint32_t idx, j; + uint32_t device = ucontrol->value.integer.value[0]; + uint32_t param_id = ucontrol->value.integer.value[1]; + uint32_t offset = ucontrol->value.integer.value[2]; + uint32_t length = ucontrol->value.integer.value[3]; + + dolby_dap_params_states.port_ids_dolby_can_be_enabled = + msm_dolby_dap_map_device_to_port_id(device); + for (idx = 0; idx < ALL_DOLBY_PARAMS; idx++) { + /*paramid from user space*/ + if (param_id == dolby_dap_params_id[idx]) + break; + } + if (idx > ALL_DOLBY_PARAMS-1) { + pr_err("%s: invalid param id 0x%x to set\n", __func__, + param_id); + return -EINVAL; + } + switch (idx) { + case DOLBY_COMMIT_ALL_IDX: { + /* COMIIT ALL: Send all parameters to DSP */ + pr_debug("%s: COMMIT_ALL recvd\n", __func__); + for (idx = 0; idx < AFE_MAX_PORTS; idx++) { + port_id = dolby_dap_params_states.port_id[idx]; + copp_idx = + dolby_dap_params_states.copp_idx[idx]; + if ((copp_idx > 0) && + (copp_idx < MAX_COPPS_PER_PORT) && + (port_id != DOLBY_INVALID_PORT_ID)) + rc |= msm_dolby_dap_send_cached_params( + port_id, + copp_idx, + 0); + } + } + break; + case DOLBY_COMMIT_IDX: { + pr_debug("%s: COMMIT recvd\n", __func__); + /* COMMIT: Send only modified parameters to DSP */ + for (idx = 0; idx < AFE_MAX_PORTS; idx++) { + port_id = dolby_dap_params_states.port_id[idx]; + copp_idx = + dolby_dap_params_states.copp_idx[idx]; + if ((copp_idx > 0) && + (copp_idx < MAX_COPPS_PER_PORT) && + (port_id == DOLBY_INVALID_PORT_ID)) + rc |= msm_dolby_dap_send_cached_params( + port_id, + copp_idx, + 1); + } + } + break; + case DOLBY_USE_CACHE_IDX: { + pr_debug("%s: USE CACHE recvd val: %ld\n", __func__, + ucontrol->value.integer.value[4]); + dolby_dap_params_states.use_cache = + ucontrol->value.integer.value[4]; + } + break; + case DOLBY_AUTO_ENDP_IDX: { + pr_debug("%s: AUTO_ENDP recvd val: %ld\n", __func__, + ucontrol->value.integer.value[4]); + dolby_dap_params_states.auto_endp = + ucontrol->value.integer.value[4]; + } + break; + case DOLBY_AUTO_ENDDEP_IDX: { + pr_debug("%s: USE_ENDDEP_PARAMS recvd val: %ld\n", + __func__, ucontrol->value.integer.value[4]); + dolby_dap_params_states.enddep_params = + ucontrol->value.integer.value[4]; + } + break; + default: { + /* cache the parameters */ + dolby_dap_params_modified[idx] += 1; + dolby_dap_params_length[idx] = length; + pr_debug("%s: param recvd deviceId=0x%x paramId=0x%x offset=%d length=%d\n", + __func__, device, param_id, offset, length); + for (j = 0; j < length; j++) { + dolby_dap_params_value[ + dolby_dap_params_offset[idx] + + offset + j] + = ucontrol->value.integer.value[4+j]; + pr_debug("value[%d]: %ld\n", j, + ucontrol->value.integer.value[4+j]); + } + } + } + + return rc; +} + +int msm_dolby_dap_param_to_get_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0, i, index; + char *params_value; + int *update_params_value; + uint32_t params_length = DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM * + sizeof(uint32_t); + uint32_t param_payload_len = + DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + int port_id = dolby_dap_params_get.port_id, copp_idx; + + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s, port_id not set, do not query ADM\n", __func__); + return -EINVAL; + } + index = adm_validate_and_get_port_index(port_id); + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return -EINVAL; + } + copp_idx = dolby_dap_params_states.copp_idx[index]; + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_debug("%s: get params called before copp open.copp_idx:%d\n", + __func__, copp_idx); + return -EINVAL; + } + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + if (dolby_dap_params_get.param_id == DOLBY_PARAM_ID_VER) { + rc = adm_get_params(port_id, copp_idx, + DOLBY_BUNDLE_MODULE_ID, DOLBY_PARAM_ID_VER, + params_length + param_payload_len, + params_value); + } else { + for (i = 0; i < MAX_DOLBY_PARAMS; i++) + if (dolby_dap_params_id[i] == + dolby_dap_params_get.param_id) + break; + if (i > MAX_DOLBY_PARAMS-1) { + pr_err("%s: invalid param id to set", __func__); + rc = -EINVAL; + } else { + params_length = (dolby_dap_params_length[i] + + DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + rc = adm_get_params(port_id, copp_idx, + DOLBY_BUNDLE_MODULE_ID, + dolby_dap_params_id[i], + params_length + param_payload_len, + params_value); + } + } + if (rc) { + pr_err("%s: get parameters failed rc:%d\n", __func__, rc); + kfree(params_value); + return -EINVAL; + } + update_params_value = (int *)params_value; + ucontrol->value.integer.value[0] = dolby_dap_params_get.device_id; + ucontrol->value.integer.value[1] = dolby_dap_params_get.param_id; + ucontrol->value.integer.value[2] = dolby_dap_params_get.offset; + ucontrol->value.integer.value[3] = dolby_dap_params_get.length; + + pr_debug("%s: FROM DSP value[0] 0x%x value[1] %d value[2] 0x%x\n", + __func__, update_params_value[0], + update_params_value[1], update_params_value[2]); + for (i = 0; i < dolby_dap_params_get.length; i++) { + ucontrol->value.integer.value[DOLBY_PARAM_PAYLOAD_SIZE+i] = + update_params_value[i]; + pr_debug("value[%d]:%d\n", i, update_params_value[i]); + } + pr_debug("%s: Returning param_id=0x%x offset=%d length=%d\n", + __func__, dolby_dap_params_get.param_id, + dolby_dap_params_get.offset, + dolby_dap_params_get.length); + kfree(params_value); + return 0; +} + +int msm_dolby_dap_param_to_get_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int port_id, idx, copp_idx; + + dolby_dap_params_get.device_id = ucontrol->value.integer.value[0]; + port_id = msm_dolby_dap_map_device_to_port_id( + dolby_dap_params_get.device_id); + for (idx = 0; idx < AFE_MAX_PORTS; idx++) { + port_id = dolby_dap_params_states.port_id[idx]; + copp_idx = dolby_dap_params_states.copp_idx[idx]; + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT) || + (port_id == DOLBY_INVALID_PORT_ID)) + continue; + else + break; + } + if (idx == AFE_MAX_PORTS) + port_id = SLIMBUS_0_RX; + dolby_dap_params_get.port_id = port_id; + dolby_dap_params_get.param_id = ucontrol->value.integer.value[1]; + dolby_dap_params_get.offset = ucontrol->value.integer.value[2]; + dolby_dap_params_get.length = ucontrol->value.integer.value[3]; + pr_debug("%s: param_id=0x%x offset=%d length=%d\n", __func__, + dolby_dap_params_get.param_id, dolby_dap_params_get.offset, + dolby_dap_params_get.length); + return 0; +} + +int msm_dolby_dap_param_visualizer_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t length = dolby_dap_params_value[DOLBY_PARAM_VCNB_OFFSET]; + char *visualizer_data; + int i, rc; + int *update_visualizer_data; + uint32_t offset, params_length = + (2*length + DOLBY_VIS_PARAM_HEADER_SIZE)*sizeof(uint32_t); + uint32_t param_payload_len = + DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + int port_id, copp_idx, idx; + + for (idx = 0; idx < AFE_MAX_PORTS; idx++) { + port_id = dolby_dap_params_states.port_id[idx]; + copp_idx = dolby_dap_params_states.copp_idx[idx]; + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT) || + (port_id == DOLBY_INVALID_PORT_ID)) + continue; + else + break; + } + if (idx == AFE_MAX_PORTS) { + pr_debug("%s, port_id not set, returning error", __func__); + ucontrol->value.integer.value[0] = 0; + return -EINVAL; + } + visualizer_data = kzalloc(params_length, GFP_KERNEL); + if (!visualizer_data) + return -ENOMEM; + + offset = 0; + params_length = length * sizeof(uint32_t); + rc = adm_get_params(port_id, copp_idx, DOLBY_BUNDLE_MODULE_ID, + DOLBY_PARAM_ID_VCBG, + params_length + param_payload_len, + visualizer_data + offset); + if (rc) { + pr_err("%s: get parameters failed\n", __func__); + kfree(visualizer_data); + return -EINVAL; + } + + offset = length * sizeof(uint32_t); + rc = adm_get_params(port_id, copp_idx, DOLBY_BUNDLE_MODULE_ID, + DOLBY_PARAM_ID_VCBE, + params_length + param_payload_len, + visualizer_data + offset); + if (rc) { + pr_err("%s: get parameters failed\n", __func__); + kfree(visualizer_data); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = 2*length; + pr_debug("%s: visualizer data length %ld\n", __func__, + ucontrol->value.integer.value[0]); + update_visualizer_data = (int *)visualizer_data; + for (i = 0; i < 2*length; i++) { + ucontrol->value.integer.value[1+i] = update_visualizer_data[i]; + pr_debug("value[%d] %d\n", i, update_visualizer_data[i]); + } + kfree(visualizer_data); + return 0; +} + +int msm_dolby_dap_param_visualizer_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* not used while getting the visualizer data */ + return 0; +} + +int msm_dolby_dap_endpoint_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* not used while setting the endpoint */ + return 0; +} + +int msm_dolby_dap_endpoint_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int device = ucontrol->value.integer.value[0]; + + dolby_dap_params_states.device = device; + return 0; +} + +int msm_dolby_dap_security_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* not used while setting the manfr id*/ + return 0; +} + +int msm_dolby_dap_security_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int manufacturer_id = ucontrol->value.integer.value[0]; + + core_set_dolby_manufacturer_id(manufacturer_id); + return 0; +} + +int msm_dolby_dap_license_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + core_get_license_status(DOLBY_DS1_LICENSE_ID); + return 0; +} + +int msm_dolby_dap_license_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return core_set_license(ucontrol->value.integer.value[0], + DOLBY_DS1_LICENSE_ID); +} + +static const struct snd_kcontrol_new dolby_license_controls[] = { + SOC_SINGLE_MULTI_EXT("DS1 License", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 1, msm_dolby_dap_license_control_get, + msm_dolby_dap_license_control_put), +}; + +static const struct snd_kcontrol_new dolby_security_controls[] = { + SOC_SINGLE_MULTI_EXT("DS1 Security", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 1, msm_dolby_dap_security_control_get, + msm_dolby_dap_security_control_put), +}; + +static const struct snd_kcontrol_new dolby_dap_param_to_set_controls[] = { + SOC_SINGLE_MULTI_EXT("DS1 DAP Set Param", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, 128, msm_dolby_dap_param_to_set_control_get, + msm_dolby_dap_param_to_set_control_put), +}; + +static const struct snd_kcontrol_new dolby_dap_param_to_get_controls[] = { + SOC_SINGLE_MULTI_EXT("DS1 DAP Get Param", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, 128, msm_dolby_dap_param_to_get_control_get, + msm_dolby_dap_param_to_get_control_put), +}; + +static const struct snd_kcontrol_new dolby_dap_param_visualizer_controls[] = { + SOC_SINGLE_MULTI_EXT("DS1 DAP Get Visualizer", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 41, msm_dolby_dap_param_visualizer_control_get, + msm_dolby_dap_param_visualizer_control_put), +}; + +static const struct snd_kcontrol_new dolby_dap_param_end_point_controls[] = { + SOC_SINGLE_MULTI_EXT("DS1 DAP Endpoint", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 1, msm_dolby_dap_endpoint_control_get, + msm_dolby_dap_endpoint_control_put), +}; + +void msm_dolby_dap_add_controls(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, + dolby_license_controls, + ARRAY_SIZE(dolby_license_controls)); + + snd_soc_add_platform_controls(platform, + dolby_security_controls, + ARRAY_SIZE(dolby_security_controls)); + + snd_soc_add_platform_controls(platform, + dolby_dap_param_to_set_controls, + ARRAY_SIZE(dolby_dap_param_to_set_controls)); + + snd_soc_add_platform_controls(platform, + dolby_dap_param_to_get_controls, + ARRAY_SIZE(dolby_dap_param_to_get_controls)); + + snd_soc_add_platform_controls(platform, + dolby_dap_param_visualizer_controls, + ARRAY_SIZE(dolby_dap_param_visualizer_controls)); + + snd_soc_add_platform_controls(platform, + dolby_dap_param_end_point_controls, + ARRAY_SIZE(dolby_dap_param_end_point_controls)); +} diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h new file mode 100644 index 000000000000..ca6310a5b06b --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_DOLBY_DAP_CONFIG_H_ +#define _MSM_DOLBY_DAP_CONFIG_H_ + +#include +#include "msm-dolby-common.h" + +#ifdef CONFIG_DOLBY_DAP +/* DOLBY DOLBY GUIDS */ +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0x0001033B +#define NUM_DOLBY_ENDP_DEVICE 23 + +#define DOLBY_NUM_ENDP_DEPENDENT_PARAMS 3 +#define DOLBY_ENDDEP_PARAM_DVLO_OFFSET 0 +#define DOLBY_ENDDEP_PARAM_DVLO_LENGTH 1 +#define DOLBY_ENDDEP_PARAM_DVLI_OFFSET (DOLBY_ENDDEP_PARAM_DVLO_OFFSET + \ + DOLBY_ENDDEP_PARAM_DVLO_LENGTH) +#define DOLBY_ENDDEP_PARAM_DVLI_LENGTH 1 +#define DOLBY_ENDDEP_PARAM_VMB_OFFSET (DOLBY_ENDDEP_PARAM_DVLI_OFFSET + \ + DOLBY_ENDDEP_PARAM_DVLI_LENGTH) +#define DOLBY_ENDDEP_PARAM_VMB_LENGTH 1 +#define DOLBY_ENDDEP_PARAM_LENGTH (DOLBY_ENDDEP_PARAM_DVLO_LENGTH + \ + DOLBY_ENDDEP_PARAM_DVLI_LENGTH + DOLBY_ENDDEP_PARAM_VMB_LENGTH) + +#define MAX_DOLBY_PARAMS 47 +#define MAX_DOLBY_CTRL_PARAMS 5 +#define ALL_DOLBY_PARAMS (MAX_DOLBY_PARAMS + \ + MAX_DOLBY_CTRL_PARAMS) +#define DOLBY_COMMIT_ALL_IDX MAX_DOLBY_PARAMS +#define DOLBY_COMMIT_IDX (MAX_DOLBY_PARAMS+1) +#define DOLBY_USE_CACHE_IDX (MAX_DOLBY_PARAMS+2) +#define DOLBY_AUTO_ENDP_IDX (MAX_DOLBY_PARAMS+3) +#define DOLBY_AUTO_ENDDEP_IDX (MAX_DOLBY_PARAMS+4) + +/* DOLBY device definitions */ +enum { + DOLBY_ENDP_INT_SPEAKERS = 0, + DOLBY_ENDP_EXT_SPEAKERS, + DOLBY_ENDP_HEADPHONES, + DOLBY_ENDP_HDMI, + DOLBY_ENDP_SPDIF, + DOLBY_ENDP_DLNA, + DOLBY_ENDP_ANALOG, +}; + +/* DOLBY device definitions end */ + +struct dolby_dap_params { + uint32_t value[TOTAL_LENGTH_DOLBY_PARAM + MAX_DOLBY_PARAMS]; +} __packed; + +int msm_dolby_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on); +void msm_dolby_dap_deinit(int port_id); +void msm_dolby_dap_add_controls(struct snd_soc_platform *platform); +int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled); +/* Dolby DOLBY end */ +#else +int msm_dolby_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + return 0; +} +void msm_dolby_dap_deinit(int port_id) { } +void msm_dolby_dap_add_controls(struct snd_soc_platform *platform) { } +int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} +#endif + +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c new file mode 100644 index 000000000000..40ea49c6578f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c @@ -0,0 +1,2299 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "msm-ds2-dap-config.h" +#include "msm-pcm-routing-v2.h" +#include + + +#ifdef CONFIG_DOLBY_DS2 + +/* ramp up/down for 30ms */ +#define DOLBY_SOFT_VOLUME_PERIOD 40 +/* Step value 0ms or 0us */ +#define DOLBY_SOFT_VOLUME_STEP 1000 +#define DOLBY_ADDITIONAL_RAMP_WAIT 10 +#define SOFT_VOLUME_PARAM_SIZE 3 +#define PARAM_PAYLOAD_SIZE 3 + +enum { + DOLBY_SOFT_VOLUME_CURVE_LINEAR = 0, + DOLBY_SOFT_VOLUME_CURVE_EXP, + DOLBY_SOFT_VOLUME_CURVE_LOG, +}; + +#define VOLUME_ZERO_GAIN 0x0 +#define VOLUME_UNITY_GAIN 0x2000 +/* Wait time for module enable/disble */ +#define DOLBY_MODULE_ENABLE_PERIOD 50 + +/* DOLBY device definitions end */ +enum { + DOLBY_OFF_CACHE = 0, + DOLBY_SPEAKER_CACHE, + DOLBY_HEADPHONE_CACHE, + DOLBY_HDMI_CACHE, + DOLBY_WFD_CACHE, + DOLBY_FM_CACHE, + DOLBY_MAX_CACHE, +}; + +enum { + DAP_SOFT_BYPASS = 0, + DAP_HARD_BYPASS, +}; + +enum { + MODULE_DISABLE = 0, + MODULE_ENABLE, +}; +/* dolby param ids to/from dsp */ +static uint32_t ds2_dap_params_id[MAX_DS2_PARAMS] = { + DOLBY_PARAM_ID_VDHE, DOLBY_PARAM_ID_VSPE, DOLBY_PARAM_ID_DSSF, + DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLE, + DOLBY_PARAM_ID_DVMC, DOLBY_PARAM_ID_DVME, DOLBY_PARAM_ID_IENB, + DOLBY_PARAM_ID_IEBF, DOLBY_PARAM_ID_IEON, DOLBY_PARAM_ID_DEON, + DOLBY_PARAM_ID_NGON, DOLBY_PARAM_ID_GEON, DOLBY_PARAM_ID_GENB, + DOLBY_PARAM_ID_GEBF, DOLBY_PARAM_ID_AONB, DOLBY_PARAM_ID_AOBF, + DOLBY_PARAM_ID_AOBG, DOLBY_PARAM_ID_AOON, DOLBY_PARAM_ID_ARNB, + DOLBY_PARAM_ID_ARBF, DOLBY_PARAM_ID_PLB, DOLBY_PARAM_ID_PLMD, + DOLBY_PARAM_ID_DHSB, DOLBY_PARAM_ID_DHRG, DOLBY_PARAM_ID_DSSB, + DOLBY_PARAM_ID_DSSA, DOLBY_PARAM_ID_DVLA, DOLBY_PARAM_ID_IEBT, + DOLBY_PARAM_ID_IEA, DOLBY_PARAM_ID_DEA, DOLBY_PARAM_ID_DED, + DOLBY_PARAM_ID_GEBG, DOLBY_PARAM_ID_AOCC, DOLBY_PARAM_ID_ARBI, + DOLBY_PARAM_ID_ARBL, DOLBY_PARAM_ID_ARBH, DOLBY_PARAM_ID_AROD, + DOLBY_PARAM_ID_ARTP, DOLBY_PARAM_ID_VMON, DOLBY_PARAM_ID_VMB, + DOLBY_PARAM_ID_VCNB, DOLBY_PARAM_ID_VCBF, DOLBY_PARAM_ID_PREG, + DOLBY_PARAM_ID_VEN, DOLBY_PARAM_ID_PSTG, DOLBY_PARAM_ID_INIT_ENDP, +}; + +/* modifed state: 0x00000000 - Not updated + * > 0x00000000 && < 0x00010000 + * Updated and not committed to DSP + * 0x00010001 - Updated and committed to DSP + * > 0x00010001 - Modified the committed value + */ +/* param offset */ +static uint32_t ds2_dap_params_offset[MAX_DS2_PARAMS] = { + DOLBY_PARAM_VDHE_OFFSET, DOLBY_PARAM_VSPE_OFFSET, + DOLBY_PARAM_DSSF_OFFSET, DOLBY_PARAM_DVLI_OFFSET, + DOLBY_PARAM_DVLO_OFFSET, DOLBY_PARAM_DVLE_OFFSET, + DOLBY_PARAM_DVMC_OFFSET, DOLBY_PARAM_DVME_OFFSET, + DOLBY_PARAM_IENB_OFFSET, DOLBY_PARAM_IEBF_OFFSET, + DOLBY_PARAM_IEON_OFFSET, DOLBY_PARAM_DEON_OFFSET, + DOLBY_PARAM_NGON_OFFSET, DOLBY_PARAM_GEON_OFFSET, + DOLBY_PARAM_GENB_OFFSET, DOLBY_PARAM_GEBF_OFFSET, + DOLBY_PARAM_AONB_OFFSET, DOLBY_PARAM_AOBF_OFFSET, + DOLBY_PARAM_AOBG_OFFSET, DOLBY_PARAM_AOON_OFFSET, + DOLBY_PARAM_ARNB_OFFSET, DOLBY_PARAM_ARBF_OFFSET, + DOLBY_PARAM_PLB_OFFSET, DOLBY_PARAM_PLMD_OFFSET, + DOLBY_PARAM_DHSB_OFFSET, DOLBY_PARAM_DHRG_OFFSET, + DOLBY_PARAM_DSSB_OFFSET, DOLBY_PARAM_DSSA_OFFSET, + DOLBY_PARAM_DVLA_OFFSET, DOLBY_PARAM_IEBT_OFFSET, + DOLBY_PARAM_IEA_OFFSET, DOLBY_PARAM_DEA_OFFSET, + DOLBY_PARAM_DED_OFFSET, DOLBY_PARAM_GEBG_OFFSET, + DOLBY_PARAM_AOCC_OFFSET, DOLBY_PARAM_ARBI_OFFSET, + DOLBY_PARAM_ARBL_OFFSET, DOLBY_PARAM_ARBH_OFFSET, + DOLBY_PARAM_AROD_OFFSET, DOLBY_PARAM_ARTP_OFFSET, + DOLBY_PARAM_VMON_OFFSET, DOLBY_PARAM_VMB_OFFSET, + DOLBY_PARAM_VCNB_OFFSET, DOLBY_PARAM_VCBF_OFFSET, + DOLBY_PARAM_PREG_OFFSET, DOLBY_PARAM_VEN_OFFSET, + DOLBY_PARAM_PSTG_OFFSET, DOLBY_PARAM_INT_ENDP_OFFSET, +}; +/* param_length */ +static uint32_t ds2_dap_params_length[MAX_DS2_PARAMS] = { + DOLBY_PARAM_VDHE_LENGTH, DOLBY_PARAM_VSPE_LENGTH, + DOLBY_PARAM_DSSF_LENGTH, DOLBY_PARAM_DVLI_LENGTH, + DOLBY_PARAM_DVLO_LENGTH, DOLBY_PARAM_DVLE_LENGTH, + DOLBY_PARAM_DVMC_LENGTH, DOLBY_PARAM_DVME_LENGTH, + DOLBY_PARAM_IENB_LENGTH, DOLBY_PARAM_IEBF_LENGTH, + DOLBY_PARAM_IEON_LENGTH, DOLBY_PARAM_DEON_LENGTH, + DOLBY_PARAM_NGON_LENGTH, DOLBY_PARAM_GEON_LENGTH, + DOLBY_PARAM_GENB_LENGTH, DOLBY_PARAM_GEBF_LENGTH, + DOLBY_PARAM_AONB_LENGTH, DOLBY_PARAM_AOBF_LENGTH, + DOLBY_PARAM_AOBG_LENGTH, DOLBY_PARAM_AOON_LENGTH, + DOLBY_PARAM_ARNB_LENGTH, DOLBY_PARAM_ARBF_LENGTH, + DOLBY_PARAM_PLB_LENGTH, DOLBY_PARAM_PLMD_LENGTH, + DOLBY_PARAM_DHSB_LENGTH, DOLBY_PARAM_DHRG_LENGTH, + DOLBY_PARAM_DSSB_LENGTH, DOLBY_PARAM_DSSA_LENGTH, + DOLBY_PARAM_DVLA_LENGTH, DOLBY_PARAM_IEBT_LENGTH, + DOLBY_PARAM_IEA_LENGTH, DOLBY_PARAM_DEA_LENGTH, + DOLBY_PARAM_DED_LENGTH, DOLBY_PARAM_GEBG_LENGTH, + DOLBY_PARAM_AOCC_LENGTH, DOLBY_PARAM_ARBI_LENGTH, + DOLBY_PARAM_ARBL_LENGTH, DOLBY_PARAM_ARBH_LENGTH, + DOLBY_PARAM_AROD_LENGTH, DOLBY_PARAM_ARTP_LENGTH, + DOLBY_PARAM_VMON_LENGTH, DOLBY_PARAM_VMB_LENGTH, + DOLBY_PARAM_VCNB_LENGTH, DOLBY_PARAM_VCBF_LENGTH, + DOLBY_PARAM_PREG_LENGTH, DOLBY_PARAM_VEN_LENGTH, + DOLBY_PARAM_PSTG_LENGTH, DOLBY_PARAM_INT_ENDP_LENGTH, +}; + +struct ds2_dap_params_s { + int32_t params_val[TOTAL_LENGTH_DS2_PARAM]; + int32_t dap_params_modified[MAX_DS2_PARAMS]; +}; + +struct audio_rx_cal_data { + char aud_proc_data[AUD_PROC_BLOCK_SIZE]; + int32_t aud_proc_size; + char aud_vol_data[AUD_VOL_BLOCK_SIZE]; + int32_t aud_vol_size; +}; + +static struct ds2_dap_params_s ds2_dap_params[DOLBY_MAX_CACHE]; + +struct ds2_device_mapping { + int32_t device_id; /* audio_out_... */ + int port_id; /* afe port. constant for a target variant. routing-v2*/ + /*Only one Dolby COPP for a specific port*/ + int copp_idx; /* idx for the copp port on which ds2 is active */ + int cache_dev; /* idx to a shared parameter array dependent on device*/ + uint32_t stream_ref_count; + bool active; + void *cal_data; +}; + +static struct ds2_device_mapping dev_map[DS2_DEVICES_ALL]; + +struct ds2_dap_params_states_s { + bool use_cache; + bool dap_bypass; + bool dap_bypass_type; + bool node_opened; + int32_t device; + bool custom_stereo_onoff; +}; + +static struct ds2_dap_params_states_s ds2_dap_params_states = {true, false, + false, DEVICE_NONE}; + +static int all_supported_devices = EARPIECE|SPEAKER|WIRED_HEADSET| + WIRED_HEADPHONE|BLUETOOTH_SCO|AUX_DIGITAL| + ANLG_DOCK_HEADSET|DGTL_DOCK_HEADSET| + REMOTE_SUBMIX|ANC_HEADSET|ANC_HEADPHONE| + PROXY|FM|FM_TX|DEVICE_NONE| + BLUETOOTH_SCO_HEADSET|BLUETOOTH_SCO_CARKIT; + + +static void msm_ds2_dap_check_and_update_ramp_wait(int port_id, int copp_idx, + int *ramp_wait) +{ + + int32_t *update_params_value = NULL; + uint32_t params_length = SOFT_VOLUME_PARAM_SIZE * sizeof(uint32_t); + uint32_t param_payload_len = PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + int rc = 0; + + update_params_value = kzalloc(params_length, GFP_KERNEL); + if (!update_params_value) + goto end; + + rc = adm_get_params(port_id, copp_idx, + AUDPROC_MODULE_ID_VOL_CTRL, + AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS, + params_length + param_payload_len, + (char *) update_params_value); + if (rc == 0) { + pr_debug("%s: params_value [0x%x, 0x%x, 0x%x]\n", + __func__, update_params_value[0], + update_params_value[1], + update_params_value[2]); + *ramp_wait = update_params_value[0]; + } +end: + kfree(update_params_value); + /* + * No error returned as we do not need to error out from dap on/dap + * bypass. The default ramp parameter will be used to wait during + * ramp down. + */ +} + +static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + int32_t *update_params_value = NULL; + int32_t *param_val = NULL; + int idx, i, j, rc = 0, cdev; + uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM + + 2 * DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: Invalid port id\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) && + (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) { + pr_debug("%s:No Custom stereo for port:0x%x\n", + __func__, dev_map[dev_map_idx].port_id); + goto end; + } + + update_params_value = kzalloc(params_length, GFP_KERNEL); + if (!update_params_value) { + rc = -ENOMEM; + goto end; + } + params_length = 0; + param_val = update_params_value; + cdev = dev_map[dev_map_idx].cache_dev; + /* for VDHE and VSPE DAP params at index 0 and 1 in table */ + for (i = 0; i < 2; i++) { + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = ds2_dap_params_id[i]; + *update_params_value++ = ds2_dap_params_length[i] * + sizeof(uint32_t); + idx = ds2_dap_params_offset[i]; + for (j = 0; j < ds2_dap_params_length[i]; j++) { + if (is_custom_stereo_enabled) + *update_params_value++ = 0; + else + *update_params_value++ = + ds2_dap_params[cdev].params_val[idx+j]; + } + params_length += (DOLBY_PARAM_PAYLOAD_SIZE + + ds2_dap_params_length[i]) * + sizeof(uint32_t); + } + + pr_debug("%s: valid param length: %d\n", __func__, params_length); + if (params_length) { + rc = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + (char *)param_val, + params_length); + if (rc) { + pr_err("%s: send vdhe/vspe params failed with rc=%d\n", + __func__, rc); + rc = -EINVAL; + goto end; + } + } +end: + kfree(param_val); + return rc; +} + +int qti_set_custom_stereo_on(int port_id, int copp_idx, + bool is_custom_stereo_on) +{ + + uint16_t op_FL_ip_FL_weight; + uint16_t op_FL_ip_FR_weight; + uint16_t op_FR_ip_FL_weight; + uint16_t op_FR_ip_FR_weight; + + int32_t *update_params_value32 = NULL, rc = 0; + int32_t *param_val = NULL; + int16_t *update_params_value16 = 0; + uint32_t params_length_bytes = CUSTOM_STEREO_PAYLOAD_SIZE * + sizeof(uint32_t); + uint32_t avail_length = params_length_bytes; + + if ((port_id != SLIMBUS_0_RX) && + (port_id != RT_PROXY_PORT_001_RX)) { + pr_debug("%s:No Custom stereo for port:0x%x\n", + __func__, port_id); + goto skip_send_cmd; + } + + pr_debug("%s: port 0x%x, copp_idx %d, is_custom_stereo_on %d\n", + __func__, port_id, copp_idx, is_custom_stereo_on); + if (is_custom_stereo_on) { + op_FL_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + } else { + op_FL_ip_FL_weight = Q14_GAIN_UNITY; + op_FL_ip_FR_weight = 0; + op_FR_ip_FL_weight = 0; + op_FR_ip_FR_weight = Q14_GAIN_UNITY; + } + + update_params_value32 = kzalloc(params_length_bytes, GFP_KERNEL); + if (!update_params_value32) { + rc = -ENOMEM; + goto skip_send_cmd; + } + param_val = update_params_value32; + if (avail_length < 2 * sizeof(uint32_t)) + goto skip_send_cmd; + *update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER; + *update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF; + avail_length = avail_length - (2 * sizeof(uint32_t)); + + update_params_value16 = (int16_t *)update_params_value32; + if (avail_length < 10 * sizeof(uint16_t)) + goto skip_send_cmd; + *update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE; + /* for alignment only*/ + *update_params_value16++ = 0; + /* index is 32-bit param in little endian*/ + *update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM; + *update_params_value16++ = 0; + /* for stereo mixing num out ch*/ + *update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH; + /* for stereo mixing num in ch*/ + *update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH; + + /* Out ch map FL/FR*/ + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + + /* In ch map FL/FR*/ + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + avail_length = avail_length - (10 * sizeof(uint16_t)); + /* weighting coefficients as name suggests, + * mixing will be done according to these coefficients + */ + if (avail_length < 4 * sizeof(uint16_t)) + goto skip_send_cmd; + *update_params_value16++ = op_FL_ip_FL_weight; + *update_params_value16++ = op_FL_ip_FR_weight; + *update_params_value16++ = op_FR_ip_FL_weight; + *update_params_value16++ = op_FR_ip_FR_weight; + avail_length = avail_length - (4 * sizeof(uint16_t)); + if (params_length_bytes != 0) { + rc = adm_dolby_dap_send_params(port_id, copp_idx, + (char *)param_val, + params_length_bytes); + if (rc) { + pr_err("%s: send params failed rc=%d\n", __func__, rc); + rc = -EINVAL; + goto skip_send_cmd; + } + } + kfree(param_val); + return 0; +skip_send_cmd: + pr_err("%s: insufficient memory, send cmd failed\n", + __func__); + kfree(param_val); + return rc; +} +static int dap_set_custom_stereo_onoff(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + + int32_t *update_params_value = NULL, rc = 0; + int32_t *param_val = NULL; + uint32_t params_length_bytes = (TOTAL_LENGTH_DOLBY_PARAM + + DOLBY_PARAM_PAYLOAD_SIZE) * sizeof(uint32_t); + if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) && + (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) { + pr_debug("%s:No Custom stereo for port:0x%x\n", + __func__, dev_map[dev_map_idx].port_id); + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + rc = -EINVAL; + goto end; + } + + /* DAP custom stereo */ + msm_ds2_dap_set_vspe_vdhe(dev_map_idx, + is_custom_stereo_enabled); + update_params_value = kzalloc(params_length_bytes, GFP_KERNEL); + if (!update_params_value) { + pr_err("%s: params memory alloc failed\n", __func__); + rc = -ENOMEM; + goto end; + } + params_length_bytes = 0; + param_val = update_params_value; + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = DOLBY_ENABLE_CUSTOM_STEREO; + *update_params_value++ = sizeof(uint32_t); + if (is_custom_stereo_enabled) + *update_params_value++ = 1; + else + *update_params_value++ = 0; + params_length_bytes += (DOLBY_PARAM_PAYLOAD_SIZE + 1) * + sizeof(uint32_t); + pr_debug("%s: valid param length: %d\n", __func__, params_length_bytes); + if (params_length_bytes) { + rc = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + (char *)param_val, + params_length_bytes); + if (rc) { + pr_err("%s: custom stereo param failed with rc=%d\n", + __func__, rc); + rc = -EINVAL; + goto end; + } + } +end: + kfree(param_val); + return rc; + +} + + +static int set_custom_stereo_onoff(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + int rc = 0; + + pr_debug("%s: map index %d, custom stereo %d\n", __func__, dev_map_idx, + is_custom_stereo_enabled); + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port id\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: invalid copp idx\n", __func__); + rc = -EINVAL; + goto end; + } + + if (ds2_dap_params_states.dap_bypass == true && + ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS) { + + rc = qti_set_custom_stereo_on(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + is_custom_stereo_enabled); + if (rc < 0) { + pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d", + __func__, is_custom_stereo_enabled); + } + goto end; + + } + + if (ds2_dap_params_states.dap_bypass == false) { + rc = dap_set_custom_stereo_onoff(dev_map_idx, + is_custom_stereo_enabled); + if (rc < 0) { + pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d", + __func__, is_custom_stereo_enabled); + } + goto end; + } +end: + return rc; +} + +static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path, + int perf_mode) +{ + int rc = 0; + struct audio_rx_cal_data *aud_cal_data; + + pr_debug("%s: path %d, perf_mode %d, dev_map_idx %d\n", + __func__, path, perf_mode, dev_map_idx); + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + aud_cal_data = kzalloc(sizeof(struct audio_rx_cal_data), GFP_KERNEL); + if (!aud_cal_data) { + rc = -ENOMEM; + goto end; + } + + rc = adm_store_cal_data(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, path, perf_mode, + ADM_AUDPROC_CAL, aud_cal_data->aud_proc_data, + &aud_cal_data->aud_proc_size); + if (rc < 0) { + pr_err("%s: store cal data err %d\n", __func__, rc); + kfree(aud_cal_data); + goto end; + } + + rc = adm_store_cal_data(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, path, perf_mode, + ADM_AUDVOL_CAL, aud_cal_data->aud_vol_data, + &aud_cal_data->aud_vol_size); + if (rc < 0) { + pr_err("%s: store cal data err %d\n", __func__, rc); + kfree(aud_cal_data); + goto end; + } + + dev_map[dev_map_idx].cal_data = (void *)aud_cal_data; + +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_ds2_dap_free_cal_data(int dev_map_idx) +{ + int rc = 0; + struct audio_rx_cal_data *aud_cal_data; + + pr_debug("%s: dev_map_idx %d\n", __func__, dev_map_idx); + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + aud_cal_data = (struct audio_rx_cal_data *) + dev_map[dev_map_idx].cal_data; + kfree(aud_cal_data); + dev_map[dev_map_idx].cal_data = NULL; + +end: + return rc; +} + +static int msm_ds2_dap_send_cal_data(int dev_map_idx) +{ + int rc = 0; + struct audio_rx_cal_data *aud_cal_data = NULL; + + pr_debug("%s: devmap index %d\n", __func__, dev_map_idx); + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + if (dev_map[dev_map_idx].cal_data == NULL) { + pr_err("%s: No valid calibration data stored for idx %d\n", + __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + /* send aud proc cal */ + aud_cal_data = (struct audio_rx_cal_data *) + dev_map[dev_map_idx].cal_data; + rc = adm_send_calibration(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + ADM_PATH_PLAYBACK, 0, + ADM_AUDPROC_CAL, + aud_cal_data->aud_proc_data, + aud_cal_data->aud_proc_size); + if (rc < 0) { + pr_err("%s: adm_send_calibration failed %d\n", __func__, rc); + goto end; + } + + /* send aud volume cal*/ + rc = adm_send_calibration(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + ADM_PATH_PLAYBACK, 0, + ADM_AUDVOL_CAL, + aud_cal_data->aud_vol_data, + aud_cal_data->aud_vol_size); + if (rc < 0) + pr_err("%s: adm_send_calibration failed %d\n", __func__, rc); +end: + pr_debug("%s: return %d\n", __func__, rc); + return rc; +} + +static inline int msm_ds2_dap_can_enable_module(int32_t module_id) +{ + if (module_id == MTMX_MODULE_ID_DEFAULT_CHMIXER || + module_id == AUDPROC_MODULE_ID_RESAMPLER || + module_id == AUDPROC_MODULE_ID_VOL_CTRL) { + return false; + } + return true; +} + +static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx) +{ + int rc = 0, i = 0, port_id, copp_idx; + /* Account for 32 bit integer allocation */ + int32_t param_sz = (ADM_GET_TOPO_MODULE_LIST_LENGTH / sizeof(uint32_t)); + int32_t *update_param_val = NULL; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + port_id = dev_map[dev_map_idx].port_id; + copp_idx = dev_map[dev_map_idx].copp_idx; + pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id, copp_idx); + update_param_val = kzalloc(ADM_GET_TOPO_MODULE_LIST_LENGTH, GFP_KERNEL); + if (!update_param_val) { + pr_err("%s, param memory alloc failed\n", __func__); + rc = -ENOMEM; + goto end; + } + + if (!ds2_dap_params_states.dap_bypass) { + /* get modules from dsp */ + rc = adm_get_pp_topo_module_list(port_id, copp_idx, + ADM_GET_TOPO_MODULE_LIST_LENGTH, + (char *)update_param_val); + if (rc < 0) { + pr_err("%s:topo list port %d, err %d,copp_idx %d\n", + __func__, port_id, copp_idx, rc); + goto end; + } + + if (update_param_val[0] > (param_sz - 1)) { + pr_err("%s:max modules exp/ret [%d: %d]\n", + __func__, (param_sz - 1), + update_param_val[0]); + rc = -EINVAL; + goto end; + } + /* Turn off modules */ + for (i = 1; i < update_param_val[0]; i++) { + if (!msm_ds2_dap_can_enable_module( + update_param_val[i]) || + (update_param_val[i] == DS2_MODULE_ID)) { + pr_debug("%s: Do not enable/disable %d\n", + __func__, update_param_val[i]); + continue; + } + + pr_debug("%s: param disable %d\n", + __func__, update_param_val[i]); + adm_param_enable(port_id, copp_idx, update_param_val[i], + MODULE_DISABLE); + } + } else { + msm_ds2_dap_send_cal_data(dev_map_idx); + + } + adm_param_enable(port_id, copp_idx, DS2_MODULE_ID, + !ds2_dap_params_states.dap_bypass); +end: + kfree(update_param_val); + return rc; +} + +static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified, + int32_t idx, int32_t commit) +{ + if ((dap_params_modified[idx] == 0) || + (commit && + ((dap_params_modified[idx] & 0x00010000) && + ((dap_params_modified[idx] & 0x0000FFFF) <= 1)))) { + pr_debug("%s: not modified at idx %d\n", __func__, idx); + return false; + } + pr_debug("%s: modified at idx %d\n", __func__, idx); + return true; +} + +static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id) +{ + int32_t cache_dev = -1; + + switch (device_id) { + case DEVICE_NONE: + cache_dev = DOLBY_OFF_CACHE; + break; + case EARPIECE: + case SPEAKER: + cache_dev = DOLBY_SPEAKER_CACHE; + break; + case WIRED_HEADSET: + case WIRED_HEADPHONE: + case ANLG_DOCK_HEADSET: + case DGTL_DOCK_HEADSET: + case ANC_HEADSET: + case ANC_HEADPHONE: + case BLUETOOTH_SCO: + case BLUETOOTH_SCO_HEADSET: + case BLUETOOTH_SCO_CARKIT: + cache_dev = DOLBY_HEADPHONE_CACHE; + break; + case FM: + case FM_TX: + cache_dev = DOLBY_FM_CACHE; + break; + case AUX_DIGITAL: + cache_dev = DOLBY_HDMI_CACHE; + break; + case PROXY: + case REMOTE_SUBMIX: + cache_dev = DOLBY_WFD_CACHE; + break; + default: + pr_err("%s: invalid cache device\n", __func__); + } + pr_debug("%s: cache device %d\n", __func__, cache_dev); + return cache_dev; +} + +static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data, + int32_t *num_device, int32_t *dev_arr, + int32_t array_size) +{ + int32_t idx = 0; + int supported_devices = 0; + + if (!array_size) { + pr_err("%s: array size zero\n", __func__); + return -EINVAL; + } + + if (dolby_data->device_id == DEVICE_OUT_ALL || + dolby_data->device_id == DEVICE_OUT_DEFAULT) + supported_devices = all_supported_devices; + else + supported_devices = dolby_data->device_id; + + if ((idx < array_size) && (supported_devices & EARPIECE)) + dev_arr[idx++] = EARPIECE; + if ((idx < array_size) && (supported_devices & SPEAKER)) + dev_arr[idx++] = SPEAKER; + if ((idx < array_size) && (supported_devices & WIRED_HEADSET)) + dev_arr[idx++] = WIRED_HEADSET; + if ((idx < array_size) && (supported_devices & WIRED_HEADPHONE)) + dev_arr[idx++] = WIRED_HEADPHONE; + if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO)) + dev_arr[idx++] = BLUETOOTH_SCO; + if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_CARKIT)) + dev_arr[idx++] = BLUETOOTH_SCO_CARKIT; + if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_HEADSET)) + dev_arr[idx++] = BLUETOOTH_SCO_HEADSET; + if ((idx < array_size) && (supported_devices & AUX_DIGITAL)) + dev_arr[idx++] = AUX_DIGITAL; + if ((idx < array_size) && (supported_devices & ANLG_DOCK_HEADSET)) + dev_arr[idx++] = ANLG_DOCK_HEADSET; + if ((idx < array_size) && (supported_devices & DGTL_DOCK_HEADSET)) + dev_arr[idx++] = DGTL_DOCK_HEADSET; + if ((idx < array_size) && (supported_devices & REMOTE_SUBMIX)) + dev_arr[idx++] = REMOTE_SUBMIX; + if ((idx < array_size) && (supported_devices & ANC_HEADSET)) + dev_arr[idx++] = ANC_HEADSET; + if ((idx < array_size) && (supported_devices & ANC_HEADPHONE)) + dev_arr[idx++] = ANC_HEADPHONE; + if ((idx < array_size) && (supported_devices & PROXY)) + dev_arr[idx++] = PROXY; + if ((idx < array_size) && (supported_devices & FM)) + dev_arr[idx++] = FM; + if ((idx < array_size) && (supported_devices & FM_TX)) + dev_arr[idx++] = FM_TX; + /* CHECK device none separately */ + if ((idx < array_size) && (supported_devices == DEVICE_NONE)) + dev_arr[idx++] = DEVICE_NONE; + pr_debug("%s: dev id 0x%x, idx %d\n", __func__, + supported_devices, idx); + *num_device = idx; + return 0; +} + +static int msm_ds2_dap_get_port_id( + int32_t device_id, int32_t be_id) +{ + struct msm_pcm_routing_bdai_data bedais; + int port_id = DOLBY_INVALID_PORT_ID; + int port_type = 0; + + if (be_id < 0) { + port_id = -1; + goto end; + } + + msm_pcm_routing_get_bedai_info(be_id, &bedais); + pr_debug("%s: be port_id %d\n", __func__, bedais.port_id); + port_id = bedais.port_id; + port_type = afe_get_port_type(bedais.port_id); + if (port_type != MSM_AFE_PORT_TYPE_RX) + port_id = DOLBY_INVALID_PORT_ID; +end: + pr_debug("%s: device_id 0x%x, be_id %d, port_id %d\n", + __func__, device_id, be_id, port_id); + return port_id; +} + +static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id) +{ + int i; + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if (dev_map[i].device_id == device_id) + dev_map[i].port_id = port_id; + } + pr_debug("%s: port_id %d, device_id 0x%x\n", + __func__, port_id, device_id); + return 0; +} + +static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx, + int wait_time) +{ + int ret = 0; + + adm_set_wait_parameters(port_id, copp_idx); + msm_pcm_routing_release_lock(); + ret = adm_wait_timeout(port_id, copp_idx, wait_time); + msm_pcm_routing_acquire_lock(); + /* Reset the parameters if wait has timed out */ + if (ret == 0) + adm_reset_wait_parameters(port_id, copp_idx); + return ret; +} + +static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data) +{ + int rc = 0, i = 0, j = 0; + /*Account for 32 bit integer allocation */ + int32_t param_sz = (ADM_GET_TOPO_MODULE_LIST_LENGTH / sizeof(uint32_t)); + int32_t *mod_list = NULL; + int port_id = 0, copp_idx = -1; + bool cs_onoff = ds2_dap_params_states.custom_stereo_onoff; + int ramp_wait = DOLBY_SOFT_VOLUME_PERIOD; + + pr_debug("%s: bypass type %d bypass %d custom stereo %d\n", __func__, + ds2_dap_params_states.dap_bypass_type, + ds2_dap_params_states.dap_bypass, + ds2_dap_params_states.custom_stereo_onoff); + mod_list = kzalloc(ADM_GET_TOPO_MODULE_LIST_LENGTH, GFP_KERNEL); + if (!mod_list) { + pr_err("%s: param memory alloc failed\n", __func__); + rc = -ENOMEM; + goto end; + } + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + pr_debug("%s: active dev %d\n", __func__, dev_map[i].active); + if (dev_map[i].active) { + port_id = dev_map[i].port_id; + copp_idx = dev_map[i].copp_idx; + + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port\n", __func__); + rc = 0; + goto end; + } + + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = 0; + goto end; + } + + /* getmodules from dsp */ + rc = adm_get_pp_topo_module_list(port_id, copp_idx, + ADM_GET_TOPO_MODULE_LIST_LENGTH, + (char *)mod_list); + if (rc < 0) { + pr_err("%s:adm get topo list port %d", + __func__, port_id); + pr_err("copp_idx %d, err %d\n", + copp_idx, rc); + goto end; + } + if (mod_list[0] > (param_sz - 1)) { + pr_err("%s:max modules exp/ret [%d: %d]\n", + __func__, (param_sz - 1), + mod_list[0]); + rc = -EINVAL; + goto end; + } + /* + * get ramp parameters + * check for change in ramp parameters + * update ramp wait + */ + msm_ds2_dap_check_and_update_ramp_wait(port_id, + copp_idx, + &ramp_wait); + + /* Mute before switching modules */ + rc = adm_set_volume(port_id, copp_idx, + VOLUME_ZERO_GAIN); + if (rc < 0) { + /* + * Not Fatal can continue bypass operations. + * Do not need to block playback + */ + pr_info("%s :Set volume port_id %d", + __func__, port_id); + pr_info("copp_idx %d, error %d\n", + copp_idx, rc); + } + + rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx, + (ramp_wait + + DOLBY_ADDITIONAL_RAMP_WAIT)); + if (rc == -EINTR) { + pr_info("%s:bypass interrupted-ignore,port %d", + __func__, port_id); + pr_info("copp_idx %d\n", copp_idx); + rc = 0; + continue; + } + + /* if dap bypass is set */ + if (ds2_dap_params_states.dap_bypass) { + /* Turn off dap module */ + adm_param_enable(port_id, copp_idx, + DS2_MODULE_ID, MODULE_DISABLE); + /* + * If custom stereo is on at the time of bypass, + * switch off custom stereo on dap and turn on + * custom stereo on qti channel mixer. + */ + if (cs_onoff) { + rc = dap_set_custom_stereo_onoff(i, + !cs_onoff); + if (rc < 0) { + pr_info("%s:D_CS i %d,rc %d\n", + __func__, i, rc); + } + rc = qti_set_custom_stereo_on(port_id, + copp_idx, + cs_onoff); + if (rc < 0) { + pr_info("%s:Q_CS port id 0x%x", + __func__, port_id); + pr_info("copp idx %d, rc %d\n", + copp_idx, rc); + } + } + /* Add adm api to resend calibration on port */ + rc = msm_ds2_dap_send_cal_data(i); + if (rc < 0) { + /* + * Not fatal,continue bypass operations. + * Do not need to block playback + */ + pr_info("%s:send cal err %d index %d\n", + __func__, rc, i); + } + } else { + /* Turn off qti modules */ + for (j = 1; j < mod_list[0]; j++) { + if (!msm_ds2_dap_can_enable_module( + mod_list[j]) || + mod_list[j] == + DS2_MODULE_ID) + continue; + pr_debug("%s: param disable %d\n", + __func__, mod_list[j]); + adm_param_enable(port_id, copp_idx, + mod_list[j], + MODULE_DISABLE); + } + + /* Enable DAP modules */ + pr_debug("%s:DS2 param enable\n", __func__); + adm_param_enable(port_id, copp_idx, + DS2_MODULE_ID, MODULE_ENABLE); + /* + * If custom stereo is on at the time of dap on, + * switch off custom stereo on qti channel mixer + * and turn on custom stereo on DAP. + * mixer(qti). + */ + if (cs_onoff) { + rc = qti_set_custom_stereo_on(port_id, + copp_idx, + !cs_onoff); + if (rc < 0) { + pr_info("%s:Q_CS port_id 0x%x", + __func__, port_id); + pr_info("copp_idx %d rc %d\n", + copp_idx, rc); + } + rc = dap_set_custom_stereo_onoff(i, + cs_onoff); + if (rc < 0) { + pr_info("%s:D_CS i %d,rc %d\n", + __func__, i, rc); + } + } + } + + rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx, + DOLBY_MODULE_ENABLE_PERIOD); + if (rc == -EINTR) { + pr_info("%s:bypass interrupted port_id %d copp_idx %d\n", + __func__, port_id, copp_idx); + /* Interrupted ignore bypass */ + rc = 0; + continue; + } + + /* set volume to unity gain after module on/off */ + rc = adm_set_volume(port_id, copp_idx, + VOLUME_UNITY_GAIN); + if (rc < 0) { + /* + * Not Fatal can continue bypass operations. + * Do not need to block playback + */ + pr_info("%s: Set vol port %d copp %d, rc %d\n", + __func__, port_id, copp_idx, rc); + rc = 0; + } + } + } + +end: + kfree(mod_list); + pr_debug("%s:return rc=%d\n", __func__, rc); + return rc; +} + +static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx) +{ + int rc = 0; + int32_t *update_params_value = NULL, *params_value = NULL; + uint32_t params_length = (DOLBY_PARAM_INT_ENDP_LENGTH + + DOLBY_PARAM_PAYLOAD_SIZE) * sizeof(uint32_t); + int cache_device = 0; + struct ds2_dap_params_s *ds2_ap_params_obj = NULL; + int32_t *modified_param = NULL; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + cache_device = dev_map[dev_map_idx].cache_dev; + + ds2_ap_params_obj = &ds2_dap_params[cache_device]; + pr_debug("%s: cache dev %d, dev_map_idx %d\n", __func__, + cache_device, dev_map_idx); + pr_debug("%s: endp - %pK %pK\n", __func__, + &ds2_dap_params[cache_device], ds2_ap_params_obj); + + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + rc = -ENOMEM; + goto end; + } + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = -EINVAL; + goto end; + } + + update_params_value = params_value; + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = DOLBY_PARAM_ID_INIT_ENDP; + *update_params_value++ = DOLBY_PARAM_INT_ENDP_LENGTH * sizeof(uint32_t); + *update_params_value++ = ds2_ap_params_obj->params_val[ + ds2_dap_params_offset[endp_idx]]; + pr_debug("%s: off %d, length %d\n", __func__, + ds2_dap_params_offset[endp_idx], + ds2_dap_params_length[endp_idx]); + pr_debug("%s: param 0x%x, param val %d\n", __func__, + ds2_dap_params_id[endp_idx], ds2_ap_params_obj-> + params_val[ds2_dap_params_offset[endp_idx]]); + rc = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + (char *)params_value, params_length); + if (rc) { + pr_err("%s: send dolby params failed rc %d\n", __func__, rc); + rc = -EINVAL; + } + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified param structure invalid\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (msm_ds2_dap_check_is_param_modified(modified_param, endp_idx, 0)) + ds2_ap_params_obj->dap_params_modified[endp_idx] = 0x00010001; + +end: + kfree(params_value); + return rc; +} + +static int msm_ds2_dap_send_cached_params(int dev_map_idx, + int commit) +{ + int32_t *update_params_value = NULL, *params_value = NULL; + uint32_t idx, i, j, ret = 0; + uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM + + (MAX_DS2_PARAMS - 1) * + DOLBY_PARAM_PAYLOAD_SIZE) * + sizeof(uint32_t); + int cache_device = 0; + struct ds2_dap_params_s *ds2_ap_params_obj = NULL; + int32_t *modified_param = NULL; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + ret = -EINVAL; + goto end; + } + cache_device = dev_map[dev_map_idx].cache_dev; + + /* Use off profile cache in only for soft bypass */ + if (ds2_dap_params_states.dap_bypass_type == DAP_SOFT_BYPASS && + ds2_dap_params_states.dap_bypass == true) { + pr_debug("%s: use bypass cache 0\n", __func__); + cache_device = dev_map[0].cache_dev; + } + + ds2_ap_params_obj = &ds2_dap_params[cache_device]; + pr_debug("%s: cached param - %pK %pK, cache_device %d\n", __func__, + &ds2_dap_params[cache_device], ds2_ap_params_obj, + cache_device); + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s: params memory alloc failed\n", __func__); + ret = -ENOMEM; + goto end; + } + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port id\n", __func__); + ret = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + ret = -EINVAL; + goto end; + } + + update_params_value = params_value; + params_length = 0; + for (i = 0; i < (MAX_DS2_PARAMS-1); i++) { + /*get the pointer to the param modified array in the cache*/ + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified param structure invalid\n", + __func__); + ret = -EINVAL; + goto end; + } + if (!msm_ds2_dap_check_is_param_modified(modified_param, i, + commit)) + continue; + *update_params_value++ = DOLBY_BUNDLE_MODULE_ID; + *update_params_value++ = ds2_dap_params_id[i]; + *update_params_value++ = ds2_dap_params_length[i] * + sizeof(uint32_t); + idx = ds2_dap_params_offset[i]; + for (j = 0; j < ds2_dap_params_length[i]; j++) { + *update_params_value++ = + ds2_ap_params_obj->params_val[idx+j]; + pr_debug("%s: id 0x%x,val %d\n", __func__, + ds2_dap_params_id[i], + ds2_ap_params_obj->params_val[idx+j]); + } + params_length += (DOLBY_PARAM_PAYLOAD_SIZE + + ds2_dap_params_length[i]) * sizeof(uint32_t); + } + + pr_debug("%s: valid param length: %d\n", __func__, params_length); + if (params_length) { + ret = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + (char *)params_value, + params_length); + if (ret) { + pr_err("%s: send dolby params failed ret %d\n", + __func__, ret); + ret = -EINVAL; + goto end; + } + for (i = 0; i < MAX_DS2_PARAMS-1; i++) { + /*get pointer to the param modified array in the cache*/ + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified param struct invalid\n", + __func__); + ret = -EINVAL; + goto end; + } + if (!msm_ds2_dap_check_is_param_modified( + modified_param, i, commit)) + continue; + ds2_ap_params_obj->dap_params_modified[i] = 0x00010001; + } + } +end: + kfree(params_value); + return ret; +} + +static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data, + int commit) +{ + int ret = 0, i, idx; + struct ds2_dap_params_s *ds2_ap_params_obj = NULL; + int32_t *modified_param = NULL; + + /* Do not commit params if in hard bypass */ + if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS && + ds2_dap_params_states.dap_bypass == true) { + pr_debug("%s: called in bypass", __func__); + ret = -EINVAL; + goto end; + } + for (idx = 0; idx < MAX_DS2_PARAMS; idx++) { + if (ds2_dap_params_id[idx] == DOLBY_PARAM_ID_INIT_ENDP) + break; + } + if (idx >= MAX_DS2_PARAMS || idx < 0) { + pr_err("%s: index of DS2 Param not found idx %d\n", + __func__, idx); + ret = -EINVAL; + goto end; + } + pr_debug("%s: found endp - idx %d 0x%x\n", __func__, idx, + ds2_dap_params_id[idx]); + for (i = 0; i < DS2_DEVICES_ALL; i++) { + pr_debug("%s:dev[0x%x,0x%x],i:%d,active:%d,bypass:%d,type:%d\n", + __func__, dolby_data->device_id, dev_map[i].device_id, + i, dev_map[i].active, ds2_dap_params_states.dap_bypass, + ds2_dap_params_states.dap_bypass_type); + + if (((dev_map[i].device_id & ds2_dap_params_states.device) || + ((ds2_dap_params_states.dap_bypass_type == + DAP_SOFT_BYPASS) && + (ds2_dap_params_states.dap_bypass == true))) && + (dev_map[i].active == true)) { + + /*get ptr to the cache storing the params for device*/ + if ((ds2_dap_params_states.dap_bypass_type == + DAP_SOFT_BYPASS) && + (ds2_dap_params_states.dap_bypass == true)) + ds2_ap_params_obj = + &ds2_dap_params[dev_map[0].cache_dev]; + else + ds2_ap_params_obj = + &ds2_dap_params[dev_map[i].cache_dev]; + + /*get the pointer to the param modified array in cache*/ + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified_param NULL\n", __func__); + ret = -EINVAL; + goto end; + } + + /* + * Send the endp param if use cache is set + * or if param is modified + */ + if (!commit || msm_ds2_dap_check_is_param_modified( + modified_param, idx, commit)) { + msm_ds2_dap_send_end_point(i, idx); + commit = 0; + } + ret = msm_ds2_dap_send_cached_params(i, commit); + if (ret < 0) { + pr_err("%s: send cached param %d\n", + __func__, ret); + goto end; + } + } + } +end: + return ret; +} + +static int msm_ds2_dap_handle_commands(u32 cmd, void *arg) +{ + int ret = 0, port_id = 0; + int32_t data; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + + if (get_user(data, &dolby_data->data[0])) { + pr_debug("%s error getting data\n", __func__); + ret = -EFAULT; + goto end; + } + + pr_debug("%s: param_id %d,be_id %d,device_id 0x%x,length %d,data %d\n", + __func__, dolby_data->param_id, dolby_data->be_id, + dolby_data->device_id, dolby_data->length, data); + + switch (dolby_data->param_id) { + case DAP_CMD_COMMIT_ALL: + msm_ds2_dap_commit_params(dolby_data, 0); + break; + + case DAP_CMD_COMMIT_CHANGED: + msm_ds2_dap_commit_params(dolby_data, 1); + break; + + case DAP_CMD_USE_CACHE_FOR_INIT: + ds2_dap_params_states.use_cache = data; + break; + + case DAP_CMD_SET_BYPASS: + pr_debug("%s: bypass %d bypass type %d, data %d\n", __func__, + ds2_dap_params_states.dap_bypass, + ds2_dap_params_states.dap_bypass_type, + data); + /* Do not perform bypass operation if bypass state is same*/ + if (ds2_dap_params_states.dap_bypass == data) + break; + ds2_dap_params_states.dap_bypass = data; + /* hard bypass */ + if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS) + msm_ds2_dap_handle_bypass(dolby_data); + /* soft bypass */ + msm_ds2_dap_commit_params(dolby_data, 0); + break; + + case DAP_CMD_SET_BYPASS_TYPE: + if (data == true) + ds2_dap_params_states.dap_bypass_type = + DAP_HARD_BYPASS; + else + ds2_dap_params_states.dap_bypass_type = + DAP_SOFT_BYPASS; + pr_debug("%s: bypass type %d", __func__, + ds2_dap_params_states.dap_bypass_type); + break; + + case DAP_CMD_SET_ACTIVE_DEVICE: + pr_debug("%s: DAP_CMD_SET_ACTIVE_DEVICE length %d\n", + __func__, dolby_data->length); + /* TODO: need to handle multiple instance*/ + ds2_dap_params_states.device |= dolby_data->device_id; + port_id = msm_ds2_dap_get_port_id( + dolby_data->device_id, + dolby_data->be_id); + pr_debug("%s: device id 0x%x all_dev 0x%x port_id %d\n", + __func__, dolby_data->device_id, + ds2_dap_params_states.device, port_id); + msm_ds2_dap_update_dev_map_port_id(dolby_data->device_id, + port_id); + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port id %d\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + break; + } +end: + return ret; + +} + +static int msm_ds2_dap_set_param(u32 cmd, void *arg) +{ + int rc = 0, idx, i, j, off, port_id = 0, cdev = 0; + int32_t num_device = 0; + int32_t data = 0; + int32_t dev_arr[DS2_DSP_SUPPORTED_ENDP_DEVICE] = {0}; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + + rc = msm_ds2_dap_update_num_devices(dolby_data, &num_device, dev_arr, + DS2_DSP_SUPPORTED_ENDP_DEVICE); + if (num_device == 0 || rc < 0) { + pr_err("%s: num devices 0\n", __func__); + rc = -EINVAL; + goto end; + } + for (i = 0; i < num_device; i++) { + port_id = msm_ds2_dap_get_port_id(dev_arr[i], + dolby_data->be_id); + if (port_id != DOLBY_INVALID_PORT_ID) + msm_ds2_dap_update_dev_map_port_id(dev_arr[i], port_id); + + cdev = msm_ds2_dap_map_device_to_dolby_cache_devices( + dev_arr[i]); + if (cdev < 0 || cdev >= DOLBY_MAX_CACHE) { + pr_err("%s: Invalid cache device %d for device 0x%x\n", + __func__, cdev, dev_arr[i]); + rc = -EINVAL; + goto end; + } + pr_debug("%s:port:%d,be:%d,dev:0x%x,cdev:%d,param:0x%x,len:%d\n" + , __func__, port_id, dolby_data->be_id, dev_arr[i], + cdev, dolby_data->param_id, dolby_data->length); + for (idx = 0; idx < MAX_DS2_PARAMS; idx++) { + /*paramid from user space*/ + if (dolby_data->param_id == ds2_dap_params_id[idx]) + break; + } + if (idx > MAX_DS2_PARAMS-1) { + pr_err("%s: invalid param id 0x%x at idx %d\n", + __func__, dolby_data->param_id, idx); + rc = -EINVAL; + goto end; + } + + off = ds2_dap_params_offset[idx]; + if ((dolby_data->length <= 0) || + (dolby_data->length > TOTAL_LENGTH_DS2_PARAM - off)) { + pr_err("%s: invalid length %d at idx %d\n", + __func__, dolby_data->length, idx); + rc = -EINVAL; + goto end; + } + + /* cache the parameters */ + ds2_dap_params[cdev].dap_params_modified[idx] += 1; + for (j = 0; j < dolby_data->length; j++) { + if (get_user(data, &dolby_data->data[j])) { + pr_debug("%s:error getting data\n", __func__); + rc = -EFAULT; + goto end; + } + ds2_dap_params[cdev].params_val[off + j] = data; + pr_debug("%s:off %d,val[i/p:o/p]-[%d / %d]\n", + __func__, off, data, + ds2_dap_params[cdev]. + params_val[off + j]); + } + } +end: + return rc; +} + +static int msm_ds2_dap_get_param(u32 cmd, void *arg) +{ + int rc = 0, i, port_id = 0, copp_idx = -1; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + int32_t *update_params_value = NULL, *params_value = NULL; + uint32_t params_length = DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM * + sizeof(uint32_t); + uint32_t param_payload_len = + DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + + /* Return error on get param in soft or hard bypass */ + if (ds2_dap_params_states.dap_bypass == true) { + pr_err("%s: called in bypass_type %d bypass %d\n", __func__, + ds2_dap_params_states.dap_bypass_type, + ds2_dap_params_states.dap_bypass); + rc = -EINVAL; + goto end; + } + + /* Return if invalid length */ + if ((dolby_data->length > + (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) || + (dolby_data->length <= 0)) { + pr_err("Invalid length %d", dolby_data->length); + rc = -EINVAL; + goto end; + } + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].active) && + (dev_map[i].device_id & dolby_data->device_id)) { + port_id = dev_map[i].port_id; + copp_idx = dev_map[i].copp_idx; + break; + } + } + + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: Invalid port\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = -EINVAL; + goto end; + } + + pr_debug("%s: port_id 0x%x, copp_idx %d, dev_map[i].device_id %x\n", + __func__, port_id, copp_idx, dev_map[i].device_id); + + params_value = kzalloc(params_length + param_payload_len, + GFP_KERNEL); + if (!params_value) { + rc = -ENOMEM; + goto end; + } + + if (dolby_data->param_id == DOLBY_PARAM_ID_VER) { + rc = adm_get_params(port_id, copp_idx, + DOLBY_BUNDLE_MODULE_ID, + DOLBY_PARAM_ID_VER, + params_length + param_payload_len, + (char *)params_value); + } else { + for (i = 0; i < MAX_DS2_PARAMS; i++) + if (ds2_dap_params_id[i] == + dolby_data->param_id) + break; + if (i > MAX_DS2_PARAMS-1) { + pr_err("%s: invalid param id 0x%x at id %d\n", __func__, + dolby_data->param_id, i); + rc = -EINVAL; + goto end; + } else { + params_length = + ds2_dap_params_length[i] * sizeof(uint32_t); + + rc = adm_get_params(port_id, copp_idx, + DOLBY_BUNDLE_MODULE_ID, + ds2_dap_params_id[i], + params_length + + param_payload_len, + (char *)params_value); + } + } + if (rc) { + pr_err("%s: get parameters failed rc %d\n", __func__, rc); + rc = -EINVAL; + goto end; + } + update_params_value = params_value; + if (copy_to_user((void *)dolby_data->data, + &update_params_value[DOLBY_PARAM_PAYLOAD_SIZE], + (dolby_data->length * sizeof(uint32_t)))) { + pr_err("%s: error getting param\n", __func__); + rc = -EFAULT; + goto end; + } +end: + kfree(params_value); + return rc; +} + +static int msm_ds2_dap_param_visualizer_control_get(u32 cmd, void *arg) +{ + int32_t *visualizer_data = NULL; + int i = 0, ret = 0, port_id = -1, cache_dev = -1, copp_idx = -1; + int32_t *update_visualizer_data = NULL; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + uint32_t offset, length, params_length; + uint32_t param_payload_len = + DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].active)) { + port_id = dev_map[i].port_id; + cache_dev = dev_map[i].cache_dev; + copp_idx = dev_map[i].copp_idx; + break; + } + } + + if (port_id == DOLBY_INVALID_PORT_ID || + (copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + ret = 0; + dolby_data->length = 0; + pr_err("%s: no device active\n", __func__); + goto end; + } + + length = ds2_dap_params[cache_dev].params_val[DOLBY_PARAM_VCNB_OFFSET]; + + if (length > DOLBY_PARAM_VCNB_MAX_LENGTH || length <= 0) { + ret = 0; + dolby_data->length = 0; + pr_err("%s Incorrect VCNB length", __func__); + } + + params_length = (2*length + DOLBY_VIS_PARAM_HEADER_SIZE) * + sizeof(uint32_t); + + visualizer_data = kzalloc(params_length, GFP_KERNEL); + if (!visualizer_data) { + ret = -ENOMEM; + dolby_data->length = 0; + goto end; + } + memset(visualizer_data, 0x0, params_length); + + /* Return error on get param in soft or hard bypass */ + if (ds2_dap_params_states.dap_bypass == true) { + pr_debug("%s: visualizer called in bypass, return 0\n", + __func__); + ret = 0; + dolby_data->length = 0; + goto end; + } + + offset = 0; + params_length = length * sizeof(uint32_t); + ret = adm_get_params(port_id, copp_idx, + DOLBY_BUNDLE_MODULE_ID, + DOLBY_PARAM_ID_VCBG, + params_length + param_payload_len, + (((char *)(visualizer_data)) + offset)); + if (ret) { + pr_err("%s: get parameters failed ret %d\n", __func__, ret); + ret = -EINVAL; + dolby_data->length = 0; + goto end; + } + offset = length * sizeof(uint32_t); + ret = adm_get_params(port_id, copp_idx, + DOLBY_BUNDLE_MODULE_ID, + DOLBY_PARAM_ID_VCBE, + params_length + param_payload_len, + (((char *)(visualizer_data)) + offset)); + if (ret) { + pr_err("%s: get parameters failed ret %d\n", __func__, ret); + ret = -EINVAL; + dolby_data->length = 0; + goto end; + } + update_visualizer_data = visualizer_data; + dolby_data->length = 2 * length; + + if (copy_to_user((void *)dolby_data->data, + (void *)update_visualizer_data, + (dolby_data->length * sizeof(uint32_t)))) { + pr_err("%s: copy to user failed for data\n", __func__); + dolby_data->length = 0; + ret = -EFAULT; + goto end; + } + +end: + kfree(visualizer_data); + return ret; +} + +int msm_ds2_dap_set_security_control(u32 cmd, void *arg) +{ + struct dolby_param_license *dolby_license = + ((struct dolby_param_license *)arg); + pr_debug("%s: dmid %d license key %d\n", __func__, + dolby_license->dmid, dolby_license->license_key); + core_set_dolby_manufacturer_id(dolby_license->dmid); + core_set_license(dolby_license->license_key, DOLBY_DS1_LICENSE_ID); + return 0; +} + +int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, struct file *file, + bool open) +{ + int i = 0, dev_id = 0; + + pr_debug("%s: open %d\n", __func__, open); + ds2_dap_params_states.node_opened = open; + ds2_dap_params_states.dap_bypass = true; + ds2_dap_params_states.dap_bypass_type = 0; + ds2_dap_params_states.use_cache = 0; + ds2_dap_params_states.device = 0; + ds2_dap_params_states.custom_stereo_onoff = 0; + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if (i == 0) + dev_map[i].device_id = 0; + else { + dev_id = (1 << (i-1)); + if (all_supported_devices & dev_id) + dev_map[i].device_id = dev_id; + else + continue; + } + dev_map[i].cache_dev = + msm_ds2_dap_map_device_to_dolby_cache_devices( + dev_map[i].device_id); + if (dev_map[i].cache_dev < 0 || + dev_map[i].cache_dev >= DOLBY_MAX_CACHE) + pr_err("%s: Invalid cache device %d for device 0x%x\n", + __func__, + dev_map[i].cache_dev, + dev_map[i].device_id); + dev_map[i].port_id = -1; + dev_map[i].active = false; + dev_map[i].stream_ref_count = 0; + dev_map[i].cal_data = NULL; + dev_map[i].copp_idx = -1; + pr_debug("%s: device_id 0x%x, cache_dev %d act %d\n", __func__, + dev_map[i].device_id, dev_map[i].cache_dev, + dev_map[i].active); + } + return 0; + +} + +int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + int ret = 0; + + pr_debug("%s: cmd: 0x%x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM: + ret = msm_ds2_dap_set_param(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM: + ret = msm_ds2_dap_get_param(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: + ret = msm_ds2_dap_handle_commands(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: + ret = msm_ds2_dap_set_security_control(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: + ret = msm_ds2_dap_param_visualizer_control_get(cmd, arg); + break; + default: + pr_err("%s: called with invalid control 0x%x\n", __func__, cmd); + ret = -EINVAL; + } + return ret; +} + +int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + + int ret = 0; + + pr_debug("%s: cmd: 0x%x\n", __func__, cmd); + if (!arg) { + pr_err("%s: Invalid params event status\n", __func__); + ret = -EINVAL; + goto end; + } + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: { + struct dolby_param_data dolby_data; + + if (copy_from_user((void *)&dolby_data, (void *)arg, + sizeof(struct dolby_param_data))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: { + struct dolby_param_license dolby_license; + + if (copy_from_user((void *)&dolby_license, (void *)arg, + sizeof(struct dolby_param_license))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license); + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: { + struct dolby_param_data dolby_data; + + if (copy_from_user((void *)&dolby_data, (void *)arg, + sizeof(struct dolby_param_data))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + if (ret < 0) + pr_err("%s: ioctl cmd %d returned err %d\n", + __func__, cmd, ret); + if (copy_to_user((void *)arg, &dolby_data, + sizeof(struct dolby_param_data))) { + pr_err("%s: Copy to user failed\n", __func__); + ret = -EFAULT; + goto end; + } + break; + } + default: + pr_err("%s: called with invalid control 0x%x\n", __func__, cmd); + ret = -EINVAL; + } +end: + return ret; + +} +#ifdef CONFIG_COMPAT +int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + int ret = 0; + + pr_debug("%s: cmd: 0x%x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM; + goto handle_set_ioctl; + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND; +handle_set_ioctl: + { + struct dolby_param_data32 dolby_data32; + struct dolby_param_data dolby_data; + + memset(&dolby_data32, 0, sizeof(dolby_data32)); + memset(&dolby_data, 0, sizeof(dolby_data)); + if (copy_from_user(&dolby_data32, (void *)arg, + sizeof(struct dolby_param_data32))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + dolby_data.version = dolby_data32.version; + dolby_data.device_id = dolby_data32.device_id; + dolby_data.be_id = dolby_data32.be_id; + dolby_data.param_id = dolby_data32.param_id; + dolby_data.length = dolby_data32.length; + dolby_data.data = compat_ptr(dolby_data32.data); + + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM; + goto handle_get_ioctl; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER; +handle_get_ioctl: + { + struct dolby_param_data32 dolby_data32; + struct dolby_param_data dolby_data; + + memset(&dolby_data32, 0, sizeof(dolby_data32)); + memset(&dolby_data, 0, sizeof(dolby_data)); + if (copy_from_user(&dolby_data32, (void *)arg, + sizeof(struct dolby_param_data32))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + dolby_data.version = dolby_data32.version; + dolby_data.device_id = dolby_data32.device_id; + dolby_data.be_id = dolby_data32.be_id; + dolby_data.param_id = dolby_data32.param_id; + dolby_data.length = dolby_data32.length; + dolby_data.data = compat_ptr(dolby_data32.data); + + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + if (ret < 0) + pr_err("%s: ioctl cmd %d, returned err %d\n", + __func__, cmd, ret); + dolby_data32.length = dolby_data.length; + if (copy_to_user((void *)arg, &dolby_data32, + sizeof(struct dolby_param_data32))) { + pr_err("%s: Copy to user failed\n", __func__); + ret = -EFAULT; + goto end; + } + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32: { + struct dolby_param_license32 dolby_license32; + struct dolby_param_license dolby_license; + + cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE; + if (copy_from_user((void *)&dolby_license32, (void *)arg, + sizeof(struct dolby_param_license32))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + dolby_license.dmid = dolby_license32.dmid; + dolby_license.license_key = dolby_license32.license_key; + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license); + break; + } + default: + pr_err("%s: called with invalid control 0x%x\n", + __func__, cmd); + ret = -EINVAL; + } +end: + return ret; + +} +#endif + +int msm_ds2_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + int ret = 0, idx = -1, i; + struct dolby_param_data dolby_data; + + struct audproc_softvolume_params softvol = { + .period = DOLBY_SOFT_VOLUME_PERIOD, + .step = DOLBY_SOFT_VOLUME_STEP, + .rampingcurve = DOLBY_SOFT_VOLUME_CURVE_EXP, + }; + + pr_debug("%s: port id %d, copp_idx %d\n", __func__, port_id, copp_idx); + + if (port_id != DOLBY_INVALID_PORT_ID) { + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].port_id == port_id) && + /* device part of active device */ + (dev_map[i].device_id & + ds2_dap_params_states.device)) { + idx = i; + /* Give priority to headset in case of + * combo device + */ + if (dev_map[i].device_id == SPEAKER) + continue; + else + break; + } + } + if (idx < 0) { + pr_err("%s: invalid index for port %d\n", + __func__, port_id); + ret = -EINVAL; + goto end; + } + pr_debug("%s:index %d, dev[0x%x,0x%x]\n", __func__, idx, + dev_map[idx].device_id, ds2_dap_params_states.device); + dev_map[idx].active = true; + dev_map[idx].copp_idx = copp_idx; + dolby_data.param_id = DOLBY_COMMIT_ALL_TO_DSP; + dolby_data.length = 0; + dolby_data.data = NULL; + dolby_data.device_id = dev_map[idx].device_id; + pr_debug("%s: idx %d, active %d, dev id 0x%x, ref count %d\n", + __func__, idx, dev_map[idx].active, + dev_map[idx].device_id, + dev_map[idx].stream_ref_count); + if (dev_map[idx].stream_ref_count == 0) { + /*perform next 3 func only if hard bypass enabled*/ + if (ds2_dap_params_states.dap_bypass_type == + DAP_HARD_BYPASS) { + ret = msm_ds2_dap_alloc_and_store_cal_data(idx, + ADM_PATH_PLAYBACK, 0); + if (ret < 0) { + pr_err("%s: Failed to alloc and store cal data for idx %d, device %d, copp_idx %d", + __func__, + idx, dev_map[idx].device_id, + dev_map[idx].copp_idx); + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + goto end; + } + + ret = adm_set_softvolume(port_id, copp_idx, + &softvol); + if (ret < 0) { + pr_err("%s: Soft volume ret error %d\n", + __func__, ret); + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + goto end; + } + + ret = msm_ds2_dap_init_modules_in_topology( + idx); + if (ret < 0) { + pr_err("%s: Failed to init modules in topolofy for idx %d, device %d, copp_idx %d\n", + __func__, idx, + dev_map[idx].device_id, + dev_map[idx].copp_idx); + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + goto end; + } + } + + ret = msm_ds2_dap_commit_params(&dolby_data, 0); + if (ret < 0) { + pr_debug("%s: commit params ret %d\n", + __func__, ret); + ret = 0; + } + } + dev_map[idx].stream_ref_count++; + if (is_custom_stereo_on) { + ds2_dap_params_states.custom_stereo_onoff = + is_custom_stereo_on; + set_custom_stereo_onoff(idx, + is_custom_stereo_on); + } + } + +end: + return ret; +} + +void msm_ds2_dap_deinit(int port_id) +{ + /* + * Get the active port corrresponding to the active device + * Check if this is same as incoming port + * Set it to invalid + */ + int idx = -1, i; + + pr_debug("%s: port_id %d\n", __func__, port_id); + if (port_id != DOLBY_INVALID_PORT_ID) { + for (i = 0; i < DS2_DEVICES_ALL; i++) { + /* Active port */ + if ((dev_map[i].port_id == port_id) && + /* device part of active device */ + (dev_map[i].device_id & + ds2_dap_params_states.device) && + /* + * Need this check to avoid race condition of + * active device being set and playback + * instance opened + */ + /* active device*/ + dev_map[i].active) { + idx = i; + if (dev_map[i].device_id == SPEAKER) + continue; + else + break; + } + } + if (idx < 0) { + pr_err("%s: invalid index for port %d\n", + __func__, port_id); + return; + } + pr_debug("%s:index %d, dev [0x%x, 0x%x]\n", __func__, idx, + dev_map[idx].device_id, ds2_dap_params_states.device); + dev_map[idx].stream_ref_count--; + if (dev_map[idx].stream_ref_count == 0) { + /*perform next func only if hard bypass enabled*/ + if (ds2_dap_params_states.dap_bypass_type == + DAP_HARD_BYPASS) { + msm_ds2_dap_free_cal_data(idx); + } + ds2_dap_params_states.device &= ~dev_map[idx].device_id; + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + } + pr_debug("%s:idx %d, active %d, dev id 0x%x ref count %d\n", + __func__, idx, dev_map[idx].active, + dev_map[idx].device_id, dev_map[idx].stream_ref_count); + } +} + +int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + int idx = -1, rc = 0, i; + + pr_debug("%s: port_id %d\n", __func__, port_id); + if (port_id != DOLBY_INVALID_PORT_ID) { + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].port_id == port_id) && + /* device part of active device */ + (dev_map[i].device_id & + ds2_dap_params_states.device)) { + idx = i; + if (dev_map[i].device_id == SPEAKER) + continue; + else + break; + } + } + if (idx < 0) { + pr_err("%s: invalid index for port %d\n", + __func__, port_id); + return rc; + } + ds2_dap_params_states.custom_stereo_onoff = + is_custom_stereo_enabled; + rc = set_custom_stereo_onoff(idx, + is_custom_stereo_enabled); + if (rc < 0) { + pr_err("%s: Custom stereo err %d on port %d\n", + __func__, rc, port_id); + } + } + return rc; +} + +#else + +static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path, + int perf_mode) +{ + return 0; +} + +static int msm_ds2_dap_free_cal_data(int dev_map_idx) +{ + return 0; +} + +static int msm_ds2_dap_send_cal_data(int dev_map_idx) +{ + return 0; +} + +static int msm_ds2_dap_can_enable_module(int32_t module_id) +{ + return 0; +} + +static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx) +{ + return 0; +} + +static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified, + int32_t idx, int32_t commit) +{ + return false; +} + + +static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id) +{ + return 0; +} + +static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data, + int32_t *num_device, int32_t *dev_arr, + int32_t array_size) +{ + return 0; +} + +static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data, + int commit) +{ + return 0; +} + +static int msm_ds2_dap_handle_commands(u32 cmd, void *arg) +{ + return 0; +} + +static int msm_ds2_dap_set_param(u32 cmd, void *arg) +{ + return 0; +} + +static int msm_ds2_dap_get_param(u32 cmd, void *arg) +{ + return 0; +} + +static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx) +{ + return 0; +} + +static int msm_ds2_dap_send_cached_params(int dev_map_idx, + int commit) +{ + return 0; +} + +static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} + +static int msm_ds2_dap_param_visualizer_control_get( + u32 cmd, void *arg, + struct msm_pcm_routing_bdai_data *bedais) +{ + return 0; +} + +static int msm_ds2_dap_set_security_control(u32 cmd, void *arg) +{ + return 0 +} + +static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id) +{ + return 0; +} + +static int32_t msm_ds2_dap_get_port_id( + int32_t device_id, int32_t be_id) +{ + return 0; +} + +static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data) +{ + return 0; +} + +static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx, + int wait_time) +{ + return 0; +} + +static int dap_set_custom_stereo_onoff(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} +int qti_set_custom_stereo_on(int port_id, int copp_idx, + bool is_custom_stereo_on) +{ + return 0; +} +int set_custom_stereo_onoff(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} +int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + return 0; +} +#endif /*CONFIG_DOLBY_DS2*/ diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h new file mode 100644 index 000000000000..f2c206908264 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_DS2_DAP_CONFIG_H_ +#define _MSM_DS2_DAP_CONFIG_H_ + +#include +#include "msm-dolby-common.h" +#include +#include + +#ifdef CONFIG_COMPAT +struct dolby_param_data32 { + s32 version; + s32 device_id; + s32 be_id; + s32 param_id; + s32 length; + compat_uptr_t data; +}; + +struct dolby_param_license32 { + compat_uptr_t dmid; + compat_uptr_t license_key; +}; + + +#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32\ + _IOWR('U', 0x10, struct dolby_param_data32) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32\ + _IOR('U', 0x11, struct dolby_param_data32) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32\ + _IOWR('U', 0x13, struct dolby_param_data32) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32\ + _IOWR('U', 0x14, struct dolby_param_license32) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32\ + _IOR('U', 0x15, struct dolby_param_data32) +#endif + +#ifdef CONFIG_DOLBY_DS2 +/* DOLBY DOLBY GUIDS */ +#define DS2_MODULE_ID 0x00010775 + +#define DS2_DSP_SUPPORTED_ENDP_DEVICE 17 +#define DS2_DEVICES_ALL 32 /* enum val is 4 bytes */ + +enum { + + DAP_CMD_COMMIT_ALL = 0, + DAP_CMD_COMMIT_CHANGED = 1, + DAP_CMD_USE_CACHE_FOR_INIT = 2, + DAP_CMD_SET_BYPASS = 3, + DAP_CMD_SET_ACTIVE_DEVICE = 4, + DAP_CMD_SET_BYPASS_TYPE = 5, +}; + +#define DOLBY_PARAM_INT_ENDP_LENGTH 1 +#define DOLBY_PARAM_INT_ENDP_OFFSET (DOLBY_PARAM_PSTG_OFFSET + \ + DOLBY_PARAM_PSTG_LENGTH) +#define MAX_DS2_PARAMS 48 +#define MAX_DS2_CTRL_PARAMS 4 +#define ALL_DS2_PARAMS (MAX_DS2_PARAMS + \ + MAX_DS2_CTRL_PARAMS) +#define TOTAL_LENGTH_DS2_PARAM (TOTAL_LENGTH_DOLBY_PARAM + 1) + +int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, struct file *file, + bool open); +int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg); +int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + u32 cmd, void *arg); +int msm_ds2_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on); +void msm_ds2_dap_deinit(int port_id); +int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled); +/* Dolby DOLBY end */ +#else + +static inline void msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, + struct file *file, + bool open) +{ +} + +static inline int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + return 0; +} + +static inline int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + u32 cmd, void *arg) +{ + return 0; +} +static inline int msm_ds2_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + return 0; +} + +static inline void msm_ds2_dap_deinit(int port_id) { } + +static inline int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} +#endif +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-dts-eagle.c b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c new file mode 100644 index 000000000000..5ad55dc48afd --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c @@ -0,0 +1,1659 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-v2.h" + +#define ION_MEM_SIZE 131072 +#define DEPC_MAX_SIZE 524288 + +#define MPST AUDPROC_MODULE_ID_DTS_HPX_POSTMIX +#define MPRE AUDPROC_MODULE_ID_DTS_HPX_PREMIX + +#define eagle_vol_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__) +#define eagle_vol_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__) +#define eagle_drv_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__) +#define eagle_drv_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__) +#define eagle_precache_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__) +#define eagle_precache_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__) +#define eagle_postcache_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__) +#define eagle_postcache_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__) +#define eagle_ioctl_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__) +#define eagle_ioctl_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__) +#define eagle_asm_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__) +#define eagle_asm_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__) +#define eagle_adm_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__) +#define eagle_adm_err(fmt, ...) \ + pr_err("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__) +#define eagle_enable_dbg(fmt, ...) \ + pr_debug("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__) +#define eagle_enable_err(fmt, ...) \ + pr_err("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__) +#define eagle_ioctl_info(fmt, ...) \ + pr_err("DTS_EAGLE_IOCTL: " fmt "\n", ##__VA_ARGS__) + +enum { + AUDIO_DEVICE_OUT_EARPIECE = 0, + AUDIO_DEVICE_OUT_SPEAKER, + AUDIO_DEVICE_OUT_WIRED_HEADSET, + AUDIO_DEVICE_OUT_WIRED_HEADPHONE, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, + AUDIO_DEVICE_OUT_AUX_DIGITAL, + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, + AUDIO_DEVICE_OUT_USB_ACCESSORY, + AUDIO_DEVICE_OUT_USB_DEVICE, + AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_DEVICE_OUT_ANC_HEADSET, + AUDIO_DEVICE_OUT_ANC_HEADPHONE, + AUDIO_DEVICE_OUT_PROXY, + AUDIO_DEVICE_OUT_FM, + AUDIO_DEVICE_OUT_FM_TX, + + AUDIO_DEVICE_OUT_COUNT +}; + +#define AUDIO_DEVICE_COMBO 0x400000 /* bit 23 */ + +enum { /* cache block */ + CB_0 = 0, + CB_1, + CB_2, + CB_3, + CB_4, + CB_5, + CB_6, + CB_7, + + CB_COUNT +}; + +enum { /* cache block description */ + CBD_DEV_MASK = 0, + CBD_OFFSG, + CBD_CMD0, + CBD_SZ0, + CBD_OFFS1, + CBD_CMD1, + CBD_SZ1, + CBD_OFFS2, + CBD_CMD2, + CBD_SZ2, + CBD_OFFS3, + CBD_CMD3, + CBD_SZ3, + + CBD_COUNT, +}; + +static s32 _fx_logN(s32 x) +{ + s32 t, y = 0xa65af; + + if (x < 0x00008000) { + x <<= 16; y -= 0xb1721; } + if (x < 0x00800000) { + x <<= 8; y -= 0x58b91; } + if (x < 0x08000000) { + x <<= 4; y -= 0x2c5c8; } + if (x < 0x20000000) { + x <<= 2; y -= 0x162e4; } + if (x < 0x40000000) { + x <<= 1; y -= 0x0b172; } + t = x + (x >> 1); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x067cd; } + t = x + (x >> 2); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x03920; } + t = x + (x >> 3); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x01e27; } + t = x + (x >> 4); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x00f85; } + t = x + (x >> 5); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x007e1; } + t = x + (x >> 6); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x003f8; } + t = x + (x >> 7); + if ((t & 0x80000000) == 0) { + x = t; y -= 0x001fe; } + x = 0x80000000 - x; + y -= x >> 15; + return y; +} + +static inline void *_getd(struct dts_eagle_param_desc *depd) +{ + return (void *)(((char *)depd) + sizeof(struct dts_eagle_param_desc)); +} + +static int _ref_cnt; +/* dts eagle parameter cache */ +static char *_depc; +static u32 _depc_size; +static s32 _c_bl[CB_COUNT][CBD_COUNT]; +static u32 _device_primary; +static u32 _device_all; +/* ION states */ +static struct ion_client *_ion_client; +static struct ion_handle *_ion_handle; +static struct param_outband _po; +static struct audio_client *_ac_NT; +static struct ion_client *_ion_client_NT; +static struct ion_handle *_ion_handle_NT; +static struct param_outband _po_NT; + +#define SEC_BLOB_MAX_CNT 10 +#define SEC_BLOB_MAX_SIZE 0x4004 /*extra 4 for size*/ +static char *_sec_blob[SEC_BLOB_MAX_CNT]; + +/* multi-copp support */ +static int _cidx[AFE_MAX_PORTS] = {-1}; + +/* volume controls */ +#define VOL_CMD_CNT_MAX 10 +static u32 _vol_cmd_cnt; +static s32 **_vol_cmds; +struct vol_cmds_d { + s32 d[4]; +}; +static struct vol_cmds_d *_vol_cmds_d; +static const s32 _log10_10_inv_x20 = 0x0008af84; + +/* hpx master control */ +static u32 _is_hpx_enabled; + +static void _volume_cmds_free(void) +{ + int i; + + for (i = 0; i < _vol_cmd_cnt; i++) + kfree(_vol_cmds[i]); + _vol_cmd_cnt = 0; + kfree(_vol_cmds); + kfree(_vol_cmds_d); + _vol_cmds = NULL; + _vol_cmds_d = NULL; +} + +static s32 _volume_cmds_alloc1(s32 size) +{ + _volume_cmds_free(); + _vol_cmd_cnt = size; + _vol_cmds = kzalloc(_vol_cmd_cnt * sizeof(int *), GFP_KERNEL); + if (_vol_cmds) { + _vol_cmds_d = kzalloc(_vol_cmd_cnt * sizeof(struct vol_cmds_d), + GFP_KERNEL); + } + if (_vol_cmds_d) + return 0; + _volume_cmds_free(); + return -ENOMEM; +} + +/* assumes size is equal or less than 0xFFF */ +static s32 _volume_cmds_alloc2(s32 idx, s32 size) +{ + kfree(_vol_cmds[idx]); + _vol_cmds[idx] = kzalloc(size, GFP_KERNEL); + if (_vol_cmds[idx]) + return 0; + _vol_cmds_d[idx].d[0] = 0; + return -ENOMEM; +} + +static void _init_cb_descs(void) +{ + int i; + + for (i = 0; i < CB_COUNT; i++) { + _c_bl[i][CBD_DEV_MASK] = 0; + _c_bl[i][CBD_OFFSG] = _c_bl[i][CBD_OFFS1] = + _c_bl[i][CBD_OFFS2] = _c_bl[i][CBD_OFFS3] = + 0xFFFFFFFF; + _c_bl[i][CBD_CMD0] = _c_bl[i][CBD_SZ0] = + _c_bl[i][CBD_CMD1] = _c_bl[i][CBD_SZ1] = + _c_bl[i][CBD_CMD2] = _c_bl[i][CBD_SZ2] = + _c_bl[i][CBD_CMD3] = _c_bl[i][CBD_SZ3] = 0; + } +} + +static u32 _get_dev_mask_for_pid(int pid) +{ + switch (pid) { + case SLIMBUS_0_RX: + return (1 << AUDIO_DEVICE_OUT_EARPIECE) | + (1 << AUDIO_DEVICE_OUT_SPEAKER) | + (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) | + (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) | + (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) | + (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE); + /* fallthrough */ + case INT_BT_SCO_RX: + return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT); + /* fallthrough */ + case RT_PROXY_PORT_001_RX: + return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) | + (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) | + (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) | + (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) | + (1 << AUDIO_DEVICE_OUT_USB_DEVICE) | + (1 << AUDIO_DEVICE_OUT_PROXY); + /* fallthrough */ + case HDMI_RX: + return 1 << AUDIO_DEVICE_OUT_AUX_DIGITAL; + case INT_FM_RX: + return 1 << AUDIO_DEVICE_OUT_FM; + case INT_FM_TX: + return 1 << AUDIO_DEVICE_OUT_FM_TX; + default: + return 0; + } +} + +static int _get_pid_from_dev(u32 device) +{ + if (device & (1 << AUDIO_DEVICE_OUT_EARPIECE) || + device & (1 << AUDIO_DEVICE_OUT_SPEAKER) || + device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) || + device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) || + device & (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) || + device & (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE)) { + return SLIMBUS_0_RX; + } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) || + device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) || + device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) { + return INT_BT_SCO_RX; + } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) || + device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) || + device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) || + device & (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) || + device & (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) || + device & (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) || + device & (1 << AUDIO_DEVICE_OUT_USB_DEVICE) || + device & (1 << AUDIO_DEVICE_OUT_PROXY)) { + return RT_PROXY_PORT_001_RX; + } else if (device & (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)) { + return HDMI_RX; + } else if (device & (1 << AUDIO_DEVICE_OUT_FM)) { + return INT_FM_RX; + } else if (device & (1 << AUDIO_DEVICE_OUT_FM_TX)) { + return INT_FM_TX; + } + return 0; +} + +static s32 _get_cb_for_dev(int device) +{ + s32 i; + + if (device & AUDIO_DEVICE_COMBO) { + for (i = 0; i < CB_COUNT; i++) { + if ((_c_bl[i][CBD_DEV_MASK] & device) == device) + return i; + } + } else { + for (i = 0; i < CB_COUNT; i++) { + if ((_c_bl[i][CBD_DEV_MASK] & device) && + !(_c_bl[i][CBD_DEV_MASK] & AUDIO_DEVICE_COMBO)) + return i; + } + } + eagle_drv_err("%s: device %i not found", __func__, device); + return -EINVAL; +} + +static int _is_port_open_and_eagle(int pid) +{ + if (msm_routing_check_backend_enabled(pid)) + return 1; + return 1; +} + +static int _isNTDevice(u32 device) +{ + if (device & + ((1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) | + (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) | + (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL))) + return 1; + return 0; +} + +static void _reg_ion_mem(void) +{ + int rc; + + rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client, &_ion_handle, + ION_MEM_SIZE, &_po.paddr, &_po.size, &_po.kvaddr); + if (rc) + eagle_drv_err("%s: msm audio ion alloc failed with %i", + __func__, rc); +} + +static void _unreg_ion_mem(void) +{ + int rc; + + rc = msm_audio_ion_free(_ion_client, _ion_handle); + if (rc) + eagle_drv_err("%s: msm audio ion alloc failed with %i", + __func__, rc); +} + +static void _reg_ion_mem_NT(void) +{ + int rc; + + eagle_drv_dbg("%s: NT ion mem", __func__); + rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client_NT, + &_ion_handle_NT, ION_MEM_SIZE, + &_po_NT.paddr, &_po_NT.size, &_po_NT.kvaddr); + if (rc) { + eagle_drv_err("%s: msm audio ion alloc failed", __func__); + return; + } + rc = q6asm_memory_map(_ac_NT, _po_NT.paddr, + IN, _po_NT.size, 1); + if (rc < 0) { + eagle_drv_err("%s: memory map failed", __func__); + msm_audio_ion_free(_ion_client_NT, _ion_handle_NT); + _ion_client_NT = NULL; + _ion_handle_NT = NULL; + } +} + +static void _unreg_ion_mem_NT(void) +{ + int rc; + + rc = q6asm_memory_unmap(_ac_NT, _po_NT.paddr, IN); + if (rc < 0) + eagle_drv_err("%s: mem unmap failed", __func__); + rc = msm_audio_ion_free(_ion_client_NT, _ion_handle_NT); + if (rc < 0) + eagle_drv_err("%s: mem free failed", __func__); + + _ion_client_NT = NULL; + _ion_handle_NT = NULL; +} + +static struct audio_client *_getNTDeviceAC(void) +{ + return _ac_NT; +} + +static void _set_audioclient(struct audio_client *ac) +{ + _ac_NT = ac; + _reg_ion_mem_NT(); +} + +static void _clear_audioclient(void) +{ + _unreg_ion_mem_NT(); + _ac_NT = NULL; +} + + +static int _sendcache_pre(struct audio_client *ac) +{ + uint32_t offset, size; + int32_t cidx, cmd, err = 0; + + cidx = _get_cb_for_dev(_device_primary); + if (cidx < 0) { + eagle_precache_err("%s: no cache for primary device %i found", + __func__, _device_primary); + return -EINVAL; + } + offset = _c_bl[cidx][CBD_OFFSG]; + cmd = _c_bl[cidx][CBD_CMD0]; + size = _c_bl[cidx][CBD_SZ0]; + /* check for integer overflow */ + if (offset > (UINT_MAX - size)) + err = -EINVAL; + if ((_depc_size == 0) || !_depc || (size == 0) || + cmd == 0 || ((offset + size) > _depc_size) || (err != 0)) { + eagle_precache_err("%s: primary device %i cache index %i general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i", + __func__, _device_primary, cidx, _depc_size, _depc, + offset, size, cmd); + return -EINVAL; + } + + if ((offset < (UINT_MAX - 124)) && ((offset + 124) < _depc_size)) + eagle_precache_dbg("%s: first 6 integers %i %i %i %i %i %i (30th %i)", + __func__, *((int *)&_depc[offset]), + *((int *)&_depc[offset+4]), + *((int *)&_depc[offset+8]), + *((int *)&_depc[offset+12]), + *((int *)&_depc[offset+16]), + *((int *)&_depc[offset+20]), + *((int *)&_depc[offset+120])); + eagle_precache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, param = 0x%X, offset = %u, and size = %u", + __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], cmd, offset, size); + + if (q6asm_dts_eagle_set(ac, cmd, size, (void *)&_depc[offset], + NULL, MPRE)) + eagle_precache_err("%s: q6asm_dts_eagle_set failed with id = %d and size = %u", + __func__, cmd, size); + else + eagle_precache_dbg("%s: q6asm_dts_eagle_set succeeded with id = %d and size = %u", + __func__, cmd, size); + return 0; +} + +static int _sendcache_post(int port_id, int copp_idx, int topology) +{ + int cidx = -1, cmd, mask, index, err = 0; + uint32_t offset, size; + + if (port_id == -1) { + cidx = _get_cb_for_dev(_device_primary); + if (cidx < 0) { + eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X", + __func__, _device_primary, port_id); + return -EINVAL; + } + goto NT_MODE_GOTO; + } + + index = adm_validate_and_get_port_index(port_id); + if (index < 0) { + eagle_postcache_err("%s: Invalid port idx %d port_id %#x", + __func__, index, port_id); + return -EINVAL; + } + eagle_postcache_dbg("%s: valid port idx %d for port_id %#x set to %i", + __func__, index, port_id, copp_idx); + _cidx[index] = copp_idx; + + mask = _get_dev_mask_for_pid(port_id); + if (mask & _device_primary) { + cidx = _get_cb_for_dev(_device_primary); + if (cidx < 0) { + eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X", + __func__, _device_primary, port_id); + return -EINVAL; + } + } else if (mask & _device_all) { + cidx = _get_cb_for_dev(_device_all); + if (cidx < 0) { + eagle_postcache_err("%s: no cache for combo device %i found. Port id was 0x%X", + __func__, _device_all, port_id); + return -EINVAL; + } + } else { + eagle_postcache_err("%s: port id 0x%X not for primary or combo device %i", + __func__, port_id, _device_primary); + return -EINVAL; + } + +NT_MODE_GOTO: + offset = _c_bl[cidx][CBD_OFFSG] + _c_bl[cidx][CBD_OFFS2]; + cmd = _c_bl[cidx][CBD_CMD2]; + size = _c_bl[cidx][CBD_SZ2]; + + /* check for integer overflow */ + if (offset > (UINT_MAX - size)) + err = -EINVAL; + if ((_depc_size == 0) || !_depc || (err != 0) || (size == 0) || + (cmd == 0) || (offset + size) > _depc_size) { + eagle_postcache_err("%s: primary device %i cache index %i port_id 0x%X general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i", + __func__, _device_primary, cidx, port_id, + _depc_size, _depc, offset, size, cmd); + return -EINVAL; + } + + if ((offset < (UINT_MAX - 24)) && ((offset + 24) < _depc_size)) + eagle_postcache_dbg("%s: first 6 integers %i %i %i %i %i %i", + __func__, *((int *)&_depc[offset]), + *((int *)&_depc[offset+4]), + *((int *)&_depc[offset+8]), + *((int *)&_depc[offset+12]), + *((int *)&_depc[offset+16]), + *((int *)&_depc[offset+20])); + eagle_postcache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, port_id = 0x%X, param = 0x%X, offset = %u, and size = %u", + __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], port_id, cmd, + offset, size); + + if (_ac_NT) { + eagle_postcache_dbg("%s: NT Route detected", __func__); + if (q6asm_dts_eagle_set(_getNTDeviceAC(), cmd, size, + (void *)&_depc[offset], + &_po_NT, MPST)) + eagle_postcache_err("%s: q6asm_dts_eagle_set failed with id = 0x%X and size = %u", + __func__, cmd, size); + } else if (adm_dts_eagle_set(port_id, copp_idx, cmd, + (void *)&_depc[offset], size) < 0) + eagle_postcache_err("%s: adm_dts_eagle_set failed with id = 0x%X and size = %u", + __func__, cmd, size); + else + eagle_postcache_dbg("%s: adm_dts_eagle_set succeeded with id = 0x%X and size = %u", + __func__, cmd, size); + return 0; +} + +static int _enable_post_get_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = _is_hpx_enabled; + return 0; +} + +static int _enable_post_put_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = 0, be_index = 0, port_id, topology; + int flag = ucontrol->value.integer.value[0]; + struct msm_pcm_routing_bdai_data msm_bedai; + + eagle_drv_dbg("%s: flag %d", __func__, flag); + + _is_hpx_enabled = flag ? true : false; + msm_pcm_routing_acquire_lock(); + /* send cache postmix params when hpx is set On */ + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + msm_pcm_routing_get_bedai_info(be_index, &msm_bedai); + port_id = msm_bedai.port_id; + if (!(((port_id == SLIMBUS_0_RX) || + (port_id == RT_PROXY_PORT_001_RX)) && + msm_bedai.active)) + continue; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + topology = adm_get_topology_for_port_copp_idx( + port_id, idx); + if (topology == + ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) { + msm_dts_eagle_enable_adm(port_id, idx, + _is_hpx_enabled); + } + } + } + msm_pcm_routing_release_lock(); + return 0; +} + +static const struct snd_kcontrol_new _hpx_enabled_controls[] = { + SOC_SINGLE_EXT("Set HPX OnOff", SND_SOC_NOPM, 0, 1, 0, + _enable_post_get_control, _enable_post_put_control) +}; + +/** + * msm_dts_ion_memmap() - helper function to map ION memory + * @po_: Out of band memory structure used as memory. + * + * Assign already allocated ION memory for mapping it to dsp. + * + * Return: No return value. + */ +void msm_dts_ion_memmap(struct param_outband *po_) +{ + po_->size = ION_MEM_SIZE; + po_->kvaddr = _po.kvaddr; + po_->paddr = _po.paddr; +} + +/** + * msm_dts_eagle_enable_asm() - Enable/disable dts module + * @ac: Enable/disable module in ASM session associated with this audio client. + * @enable: Enable/disable the dts module. + * @module: module id. + * + * Enable/disable specified dts module id in asm. + * + * Return: Return failure if any. + */ +int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module) +{ + int ret = 0; + + eagle_enable_dbg("%s: enable = %i on module %i", + __func__, enable, module); + _is_hpx_enabled = enable; + ret = q6asm_dts_eagle_set(ac, AUDPROC_PARAM_ID_ENABLE, + sizeof(enable), &enable, + NULL, module); + if (_is_hpx_enabled) { + if (module == MPRE) + _sendcache_pre(ac); + else if (module == MPST) + _sendcache_post(-1, 0, 0); + } + return ret; +} + +/** + * msm_dts_eagle_enable_adm() - Enable/disable dts module in adm + * @port_id: Send enable/disable param to this port id. + * @copp_idx: Send enable/disable param to the relevant copp. + * @enable: Enable/disable the dts module. + * + * Enable/disable dts module in adm. + * + * Return: Return failure if any. + */ +int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable) +{ + int ret = 0; + + eagle_enable_dbg("%s: enable = %i", __func__, enable); + _is_hpx_enabled = enable; + ret = adm_dts_eagle_set(port_id, copp_idx, AUDPROC_PARAM_ID_ENABLE, + (char *)&enable, sizeof(enable)); + if (_is_hpx_enabled) + _sendcache_post(port_id, copp_idx, MPST); + return ret; +} + +/** + * msm_dts_eagle_add_controls() - Add mixer control to Enable/Disable DTS HPX + * @platform: Add mixer controls to this platform. + * + * Add mixer control to Enable/Disable DTS HPX module in ADM. + * + * Return: No return value. + */ +void msm_dts_eagle_add_controls(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, _hpx_enabled_controls, + ARRAY_SIZE(_hpx_enabled_controls)); +} + +/** + * msm_dts_eagle_set_stream_gain() - Set stream gain to DTS Premix module + * @ac: Set stream gain to ASM session associated with this audio client. + * @lgain: Left gain value. + * @rgain: Right gain value. + * + * Set stream gain to DTS Premix module in ASM. + * + * Return: failure or success. + */ +int msm_dts_eagle_set_stream_gain(struct audio_client *ac, int lgain, int rgain) +{ + u32 i, val; + s32 idx, err = 0; + + eagle_vol_dbg("%s: - entry: vol_cmd_cnt = %u, lgain = %i, rgain = %i", + __func__, _vol_cmd_cnt, lgain, rgain); + + if (_depc_size == 0) { + eagle_vol_dbg("%s: driver cache not initialized", __func__); + return -EINVAL; + } + + for (i = 0; i < _vol_cmd_cnt; i++) { + if (_vol_cmds_d[i].d[0] & 0x8000) { + idx = (sizeof(struct dts_eagle_param_desc)/sizeof(int)) + + (_vol_cmds_d[i].d[0] & 0x3FF); + val = _fx_logN(((s32)(lgain+rgain)) << 2); + val = ((long long)val * _log10_10_inv_x20) >> 16; + _vol_cmds[i][idx] = (s32)clamp((int)(((long long)val * + _vol_cmds_d[i].d[1]) >> 16), + _vol_cmds_d[i].d[2], + _vol_cmds_d[i].d[3]); + eagle_vol_dbg("%s: loop %u cmd desc found %i, idx = %i. volume info: lgain = %i, rgain = %i, volume = %i (scale %i, min %i, max %i)", + __func__, i, _vol_cmds_d[i].d[0], idx, lgain, + rgain, _vol_cmds[i][idx], _vol_cmds_d[i].d[1], + _vol_cmds_d[i].d[2], _vol_cmds_d[i].d[3]); + } + idx = _get_cb_for_dev(_device_primary); + if (idx < 0) { + eagle_vol_err("%s: no cache for primary device %i found", + __func__, _device_primary); + return -EINVAL; + } + val = _c_bl[idx][CBD_OFFSG] + _vol_cmds[i][2]; + /* check for integer overflow */ + if (val > (UINT_MAX - _vol_cmds[i][1])) + err = -EINVAL; + if ((err != 0) || ((val + _vol_cmds[i][1]) > _depc_size)) { + eagle_vol_err("%s: volume size (%u) + offset (%i) out of bounds %i", + __func__, val, _vol_cmds[i][1], _depc_size); + return -EINVAL; + } + memcpy((void *)&_depc[val], &_vol_cmds[i][4], _vol_cmds[i][1]); + if (q6asm_dts_eagle_set(ac, _vol_cmds[i][0], + _vol_cmds[i][1], (void *)&_depc[val], NULL, MPRE)) + eagle_vol_err("%s: loop %u - volume set failed with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i", + __func__, i, _vol_cmds[i][0], _vol_cmds[i][1], + _vol_cmds[i][2], _vol_cmds_d[i].d[0], + _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2], + _vol_cmds_d[i].d[3], _vol_cmds[i][4]); + else + eagle_vol_dbg("%s: loop %u - volume set succeeded with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i", + __func__, i, _vol_cmds[i][0], _vol_cmds[i][1], + _vol_cmds[i][2], _vol_cmds_d[i].d[0], + _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2], + _vol_cmds_d[i].d[3], _vol_cmds[i][4]); + } + return 0; +} + +/** + * msm_dts_eagle_handle_asm() - Set or Get params from ASM + * @depd: DTS Eagle Params structure. + * @buf: Buffer to get queried param value. + * @for_pre: For premix module or postmix module. + * @get: Getting param from DSP or setting param. + * @ac: Set/Get from ASM session associated with this audio client. + * @po: Out of band memory to set or get postmix params. + * + * Set or Get params from modules in ASM session. + * + * Return: Return failure if any. + */ +int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf, + bool for_pre, bool get, struct audio_client *ac, + struct param_outband *po) +{ + struct dts_eagle_param_desc depd_ = {0}; + s32 ret = 0, isALSA = 0, err = 0, i, mod = for_pre ? MPRE : MPST; + u32 offset; + + eagle_asm_dbg("%s: set/get asm", __func__); + + /* special handling for ALSA route, to accommodate 64 bit platforms */ + if (depd == NULL) { + long *arg_ = (long *)buf; + + depd = &depd_; + depd->id = (u32)*arg_++; + depd->size = (u32)*arg_++; + depd->offset = (s32)*arg_++; + depd->device = (u32)*arg_++; + buf = (char *)arg_; + isALSA = 1; + } + + if (depd->size & 1) { + eagle_asm_err("%s: parameter size %u is not a multiple of 2", + __func__, depd->size); + return -EINVAL; + } + + if (get) { + void *buf_, *buf_m = NULL; + + eagle_asm_dbg("%s: get requested", __func__); + if (depd->offset == -1) { + eagle_asm_dbg("%s: get from dsp requested", __func__); + if (depd->size > 0 && depd->size <= DEPC_MAX_SIZE) { + buf_ = buf_m = vzalloc(depd->size); + } else { + eagle_asm_err("%s: get size %u invalid", + __func__, depd->size); + return -EINVAL; + } + if (!buf_m) { + eagle_asm_err("%s: out of memory", __func__); + return -ENOMEM; + } else if (q6asm_dts_eagle_get(ac, depd->id, + depd->size, buf_m, + po, mod) < 0) { + eagle_asm_err("%s: asm get failed", __func__); + ret = -EFAULT; + goto DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT; + } + eagle_asm_dbg("%s: get result: param id 0x%x value %d size %u", + __func__, depd->id, *(int *)buf_m, depd->size); + } else { + s32 tgt = _get_cb_for_dev(depd->device); + + if (tgt < 0) { + eagle_asm_err("%s: no cache for device %u found", + __func__, depd->device); + return -EINVAL; + } + offset = _c_bl[tgt][CBD_OFFSG] + depd->offset; + /* check for integer overflow */ + if (offset > (UINT_MAX - depd->size)) + err = -EINVAL; + if ((err != 0) || (offset + depd->size) > _depc_size) { + eagle_asm_err("%s: invalid size %u and/or offset %u", + __func__, depd->size, offset); + return -EINVAL; + } + buf_ = (u32 *)&_depc[offset]; + } + if (isALSA) { + if (depd->size == 2) { + *(long *)buf = (long)*(__u16 *)buf_; + eagle_asm_dbg("%s: asm out 16 bit value %li", + __func__, *(long *)buf); + } else { + s32 *pbuf = (s32 *)buf_; + long *bufl = (long *)buf; + + for (i = 0; i < (depd->size >> 2); i++) { + *bufl++ = (long)*pbuf++; + eagle_asm_dbg("%s: asm out value %li", + __func__, *(bufl-1)); + } + } + } else { + memcpy(buf, buf_, depd->size); + } +DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT: + vfree(buf_m); + return (int)ret; + } else { + s32 tgt = _get_cb_for_dev(depd->device); + + if (tgt < 0) { + eagle_asm_err("%s: no cache for device %u found", + __func__, depd->device); + return -EINVAL; + } + offset = _c_bl[tgt][CBD_OFFSG] + depd->offset; + /* check for integer overflow */ + if (offset > (UINT_MAX - depd->size)) + err = -EINVAL; + if ((err != 0) || ((offset + depd->size) > _depc_size)) { + eagle_asm_err("%s: invalid size %u and/or offset %u for parameter (cache is size %u)", + __func__, depd->size, offset, _depc_size); + return -EINVAL; + } + if (isALSA) { + if (depd->size == 2) { + *(__u16 *)&_depc[offset] = (__u16)*(long *)buf; + eagle_asm_dbg("%s: asm in 16 bit value %li", + __func__, *(long *)buf); + } else { + s32 *pbuf = (s32 *)&_depc[offset]; + long *bufl = (long *)buf; + + for (i = 0; i < (depd->size >> 2); i++) { + *pbuf++ = (s32)*bufl++; + eagle_asm_dbg("%s: asm in value %i", + __func__, *(pbuf-1)); + } + } + } else { + memcpy(&_depc[offset], buf, depd->size); + } + eagle_asm_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i", + __func__, depd->id, depd->size, depd->offset, + depd->device, + tgt, offset, *(int *)&_depc[offset]); + if (q6asm_dts_eagle_set(ac, depd->id, depd->size, + (void *)&_depc[offset], po, mod)) + eagle_asm_err("%s: q6asm_dts_eagle_set failed with id = 0x%X, size = %u, offset = %d", + __func__, depd->id, depd->size, depd->offset); + else + eagle_asm_dbg("%s: q6asm_dts_eagle_set succeeded with id = 0x%X, size = %u, offset = %d", + __func__, depd->id, depd->size, depd->offset); + } + + return (int)ret; +} + +/** + * msm_dts_eagle_handle_adm() - Set or Get params from ADM + * @depd: DTS Eagle Params structure used to set or get. + * @buf: Buffer to get queried param value in NT mode. + * @for_pre: For premix module or postmix module. + * @get: Getting param from DSP or setting param. + * + * Set or Get params from modules in ADM session. + * + * Return: Return failure if any. + */ +int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf, + bool for_pre, bool get) +{ + u32 pid = _get_pid_from_dev(depd->device), cidx; + s32 ret = 0; + + eagle_adm_dbg("%s: set/get adm", __func__); + + if (_isNTDevice(depd->device)) { + eagle_adm_dbg("%s: NT Route detected", __func__); + ret = msm_dts_eagle_handle_asm(depd, buf, for_pre, get, + _getNTDeviceAC(), &_po_NT); + if (ret < 0) + eagle_adm_err("%s: NT Route set failed with id = 0x%X, size = %u, offset = %i, device = %u", + __func__, depd->id, depd->size, depd->offset, + depd->device); + } else if (get) { + cidx = adm_validate_and_get_port_index(pid); + eagle_adm_dbg("%s: get from qdsp requested (port id 0x%X)", + __func__, pid); + if (adm_dts_eagle_get(pid, _cidx[cidx], depd->id, + buf, depd->size) < 0) { + eagle_adm_err("%s: get from qdsp via adm with port id 0x%X failed", + __func__, pid); + return -EFAULT; + } + } else if (_is_port_open_and_eagle(pid)) { + cidx = adm_validate_and_get_port_index(pid); + eagle_adm_dbg("%s: adm_dts_eagle_set called with id = 0x%X, size = %u, offset = %i, device = %u, port id = %u, copp index = %u", + __func__, depd->id, depd->size, depd->offset, + depd->device, pid, cidx); + ret = adm_dts_eagle_set(pid, _cidx[cidx], depd->id, + (void *)buf, depd->size); + if (ret < 0) + eagle_adm_err("%s: adm_dts_eagle_set failed", __func__); + else + eagle_adm_dbg("%s: adm_dts_eagle_set succeeded", + __func__); + } else { + ret = -EINVAL; + eagle_adm_dbg("%s: port id 0x%X not active or not Eagle", + __func__, pid); + } + return (int)ret; +} + +/** + * msm_dts_eagle_ioctl() - ioctl handler function + * @cmd: cmd to handle. + * @arg: argument to the cmd. + * + * Handle DTS Eagle ioctl cmds. + * + * Return: Return failure if any. + */ +int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg) +{ + s32 ret = 0; + + switch (cmd) { + case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: { + eagle_ioctl_info("%s: called with control 0x%X (get param cache size)", + __func__, cmd); + if (copy_to_user((void *)arg, &_depc_size, + sizeof(_depc_size))) { + eagle_ioctl_err("%s: error writing size", __func__); + return -EFAULT; + } + break; + } + case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: { + u32 size = 0; + + eagle_ioctl_info("%s: called with control 0x%X (allocate param cache)", + __func__, cmd); + if (copy_from_user((void *)&size, (void *)arg, sizeof(size))) { + eagle_ioctl_err("%s: error copying size (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, &size, sizeof(size)); + return -EFAULT; + } else if (size > DEPC_MAX_SIZE) { + eagle_ioctl_err("%s: cache size %u not allowed (min 0, max %u)", + __func__, size, DEPC_MAX_SIZE); + return -EINVAL; + } + if (_depc) { + eagle_ioctl_dbg("%s: previous param cache of size %u freed", + __func__, _depc_size); + _depc_size = 0; + vfree(_depc); + _depc = NULL; + } + if (size) + _depc = vzalloc(size); + else + eagle_ioctl_dbg("%s: %u bytes requested for param cache, nothing allocated", + __func__, size); + if (_depc) { + eagle_ioctl_dbg("%s: %u bytes allocated for param cache", + __func__, size); + _depc_size = size; + } else { + eagle_ioctl_err("%s: error allocating param cache (vzalloc failed on %u bytes)", + __func__, size); + _depc_size = 0; + return -ENOMEM; + } + break; + } + case DTS_EAGLE_IOCTL_GET_PARAM: { + struct dts_eagle_param_desc depd; + s32 for_pre = 0, get_from_core = 0, err = 0; + u32 offset; + void *buf, *buf_m = NULL; + + eagle_ioctl_info("%s: control 0x%X (get param)", + __func__, cmd); + if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) { + eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, &depd, sizeof(depd)); + return -EFAULT; + } + if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) { + eagle_ioctl_dbg("%s: using for premix", __func__); + for_pre = 1; + } + if (depd.device & DTS_EAGLE_FLAG_IOCTL_GETFROMCORE) { + eagle_ioctl_dbg("%s: 'get from core' requested", + __func__); + get_from_core = 1; + depd.offset = -1; + } + depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK; + if (depd.offset == -1) { + if (depd.size > 0 && depd.size <= DEPC_MAX_SIZE) { + buf = buf_m = vzalloc(depd.size); + } else { + eagle_ioctl_err("%s: get size %u invalid", + __func__, depd.size); + return -EINVAL; + } + if (!buf_m) { + eagle_ioctl_err("%s: out of memory", __func__); + return -ENOMEM; + } + if (get_from_core) + ret = core_dts_eagle_get(depd.id, depd.size, + buf); + else + ret = msm_dts_eagle_handle_adm(&depd, buf, + for_pre, true); + } else { + s32 cb = _get_cb_for_dev(depd.device); + + if (cb < 0) { + eagle_ioctl_err("%s: no cache for device %u found", + __func__, depd.device); + return -EINVAL; + } + offset = _c_bl[cb][CBD_OFFSG] + depd.offset; + /* check for integer overflow */ + if (offset > (UINT_MAX - depd.size)) + err = -EINVAL; + if ((err != 0) || + ((offset + depd.size) > _depc_size)) { + eagle_ioctl_err("%s: invalid size %u and/or offset %u", + __func__, depd.size, offset); + return -EINVAL; + } + buf = (void *)&_depc[offset]; + } + if (ret < 0) + eagle_ioctl_err("%s: error %i getting data", __func__, + ret); + else if (copy_to_user((void *)(((char *)arg)+sizeof(depd)), + buf, depd.size)) { + eagle_ioctl_err("%s: error copying get data", __func__); + ret = -EFAULT; + } + vfree(buf_m); + break; + } + case DTS_EAGLE_IOCTL_SET_PARAM: { + struct dts_eagle_param_desc depd; + s32 just_set_cache = 0, for_pre = 0, err = 0; + u32 offset; + s32 tgt; + + eagle_ioctl_info("%s: control 0x%X (set param)", + __func__, cmd); + if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) { + eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, &depd, sizeof(depd)); + return -EFAULT; + } + if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) { + eagle_ioctl_dbg("%s: using for premix", __func__); + for_pre = 1; + } + if (depd.device & DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE) { + eagle_ioctl_dbg("%s: 'just set cache' requested", + __func__); + just_set_cache = 1; + } + depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK; + tgt = _get_cb_for_dev(depd.device); + if (tgt < 0) { + eagle_ioctl_err("%s: no cache for device %u found", + __func__, depd.device); + return -EINVAL; + } + offset = _c_bl[tgt][CBD_OFFSG] + depd.offset; + /* check for integer overflow */ + if (offset > (UINT_MAX - depd.size)) + err = -EINVAL; + if ((err != 0) || ((offset + depd.size) > _depc_size)) { + eagle_ioctl_err("%s: invalid size %u and/or offset %u for parameter (target cache block %i with offset %i, global cache is size %u)", + __func__, depd.size, offset, tgt, + _c_bl[tgt][CBD_OFFSG], _depc_size); + return -EINVAL; + } + if (copy_from_user((void *)&_depc[offset], + (void *)(((char *)arg)+sizeof(depd)), + depd.size)) { + eagle_ioctl_err("%s: error copying param to cache (src:%pK, tgt:%pK, size:%u)", + __func__, ((char *)arg)+sizeof(depd), + &_depc[offset], depd.size); + return -EFAULT; + } + eagle_ioctl_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i", + __func__, depd.id, depd.size, depd.offset, + depd.device, tgt, offset, *(int *)&_depc[offset]); + if (!just_set_cache) { + ret = msm_dts_eagle_handle_adm(&depd, &_depc[offset], + for_pre, false); + } + break; + } + case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: { + u32 b_[CBD_COUNT+1], *b = &b_[1], cb; + + eagle_ioctl_info("%s: with control 0x%X (set param cache block)", + __func__, cmd); + if (copy_from_user((void *)b_, (void *)arg, sizeof(b_))) { + eagle_ioctl_err("%s: error copying cache block data (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, b_, sizeof(b_)); + return -EFAULT; + } + cb = b_[0]; + if (cb >= CB_COUNT) { + eagle_ioctl_err("%s: cache block %u out of range (max %u)", + __func__, cb, CB_COUNT-1); + return -EINVAL; + } + eagle_ioctl_dbg("%s: cache block %i set: devices 0x%X, global offset %i, offsets 1:%u 2:%u 3:%u, cmds/sizes 0:0x%X %u 1:0x%X %u 2:0x%X %u 3:0x%X %u", + __func__, cb, _c_bl[cb][CBD_DEV_MASK], _c_bl[cb][CBD_OFFSG], + _c_bl[cb][CBD_OFFS1], _c_bl[cb][CBD_OFFS2], + _c_bl[cb][CBD_OFFS3], _c_bl[cb][CBD_CMD0], _c_bl[cb][CBD_SZ0], + _c_bl[cb][CBD_CMD1], _c_bl[cb][CBD_SZ1], _c_bl[cb][CBD_CMD2], + _c_bl[cb][CBD_SZ2], _c_bl[cb][CBD_CMD3], _c_bl[cb][CBD_SZ3]); + if ((b[CBD_OFFSG]+b[CBD_OFFS1]+b[CBD_SZ1]) > _depc_size || + (b[CBD_OFFSG]+b[CBD_OFFS2]+b[CBD_SZ2]) > _depc_size || + (b[CBD_OFFSG]+b[CBD_OFFS3]+b[CBD_SZ3]) > _depc_size) { + eagle_ioctl_err("%s: cache block bounds out of range", + __func__); + return -EINVAL; + } + memcpy(_c_bl[cb], b, sizeof(_c_bl[cb])); + break; + } + case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: { + u32 data[2]; + + eagle_ioctl_dbg("%s: with control 0x%X (set active device)", + __func__, cmd); + if (copy_from_user((void *)data, (void *)arg, sizeof(data))) { + eagle_ioctl_err("%s: error copying active device data (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, data, sizeof(data)); + return -EFAULT; + } + if (data[1] != 0) { + _device_primary = data[0]; + eagle_ioctl_dbg("%s: primary device %i", __func__, + data[0]); + } else { + _device_all = data[0]; + eagle_ioctl_dbg("%s: all devices 0x%X", __func__, + data[0]); + } + break; + } + case DTS_EAGLE_IOCTL_GET_LICENSE: { + u32 target = 0, size = 0; + s32 size_only; + + eagle_ioctl_dbg("%s: with control 0x%X (get license)", + __func__, cmd); + if (copy_from_user((void *)&target, (void *)arg, + sizeof(target))) { + eagle_ioctl_err("%s: error reading license index. (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, &target, sizeof(target)); + return -EFAULT; + } + size_only = target & (1<<31) ? 1 : 0; + target &= 0x7FFFFFFF; + if (target >= SEC_BLOB_MAX_CNT) { + eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)", + __func__, target, SEC_BLOB_MAX_CNT); + return -EINVAL; + } + if (_sec_blob[target] == NULL) { + eagle_ioctl_err("%s: license index %u never initialized", + __func__, target); + return -EINVAL; + } + size = ((u32 *)_sec_blob[target])[0]; + if ((size == 0) || (size > SEC_BLOB_MAX_SIZE)) { + eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)", + __func__, size, target, SEC_BLOB_MAX_SIZE); + return -EINVAL; + } + if (size_only) { + eagle_ioctl_dbg("%s: reporting size of license data only", + __func__); + if (copy_to_user((void *)(((char *)arg)+sizeof(target)), + (void *)&size, sizeof(size))) { + eagle_ioctl_err("%s: error copying license size", + __func__); + return -EFAULT; + } + } else if (copy_to_user((void *)(((char *)arg)+sizeof(target)), + (void *)&(((s32 *)_sec_blob[target])[1]), size)) { + eagle_ioctl_err("%s: error copying license data", + __func__); + return -EFAULT; + } else { + eagle_ioctl_info("%s: license file %u bytes long from license index %u returned to user", + __func__, size, target); + } + break; + } + case DTS_EAGLE_IOCTL_SET_LICENSE: { + u32 target[2] = {0, 0}; + + eagle_ioctl_dbg("%s: control 0x%X (set license)", __func__, + cmd); + if (copy_from_user((void *)target, (void *)arg, + sizeof(target))) { + eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, target, sizeof(target)); + return -EFAULT; + } + if (target[0] >= SEC_BLOB_MAX_CNT) { + eagle_ioctl_err("%s: license index %u out of bounds (max index is %u)", + __func__, target[0], SEC_BLOB_MAX_CNT-1); + return -EINVAL; + } + if (target[1] == 0) { + eagle_ioctl_dbg("%s: request to free license index %u", + __func__, target[0]); + kfree(_sec_blob[target[0]]); + _sec_blob[target[0]] = NULL; + break; + } + if ((target[1] == 0) || (target[1] >= SEC_BLOB_MAX_SIZE)) { + eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)", + __func__, target[1], target[0], + SEC_BLOB_MAX_SIZE); + return -EINVAL; + } + if (_sec_blob[target[0]] != NULL) { + if (((u32 *)_sec_blob[target[0]])[1] != target[1]) { + eagle_ioctl_dbg("%s: request new size for already allocated license index %u", + __func__, target[0]); + kfree(_sec_blob[target[0]]); + _sec_blob[target[0]] = NULL; + } + } + eagle_ioctl_dbg("%s: allocating %u bytes for license index %u", + __func__, target[1], target[0]); + _sec_blob[target[0]] = kzalloc(target[1] + 4, GFP_KERNEL); + if (!_sec_blob[target[0]]) { + eagle_ioctl_err("%s: error allocating license index %u (kzalloc failed on %u bytes)", + __func__, target[0], target[1]); + return -ENOMEM; + } + ((u32 *)_sec_blob[target[0]])[0] = target[1]; + if (copy_from_user( + (void *)&(((u32 *)_sec_blob[target[0]])[1]), + (void *)(((char *)arg)+sizeof(target)), + target[1])) { + eagle_ioctl_err("%s: error copying license to index %u, size %u (src:%pK, tgt:%pK, size:%u)", + __func__, target[0], target[1], + ((char *)arg)+sizeof(target), + &(((u32 *)_sec_blob[target[0]])[1]), + target[1]); + return -EFAULT; + } + eagle_ioctl_info("%s: license file %u bytes long copied to index license index %u", + __func__, target[1], target[0]); + break; + } + case DTS_EAGLE_IOCTL_SEND_LICENSE: { + u32 target = 0; + + eagle_ioctl_dbg("%s: control 0x%X (send license)", __func__, + cmd); + if (copy_from_user((void *)&target, (void *)arg, + sizeof(target))) { + eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, &target, sizeof(target)); + return -EFAULT; + } + if (target >= SEC_BLOB_MAX_CNT) { + eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)", + __func__, target, SEC_BLOB_MAX_CNT-1); + return -EINVAL; + } + if (!_sec_blob[target] || + ((u32 *)_sec_blob[target])[0] == 0) { + eagle_ioctl_err("%s: license index %u is invalid", + __func__, target); + return -EINVAL; + } + if (core_dts_eagle_set(((s32 *)_sec_blob[target])[0], + (char *)&((s32 *)_sec_blob[target])[1]) < 0) + eagle_ioctl_err("%s: core_dts_eagle_set failed with id = %u", + __func__, target); + else + eagle_ioctl_info("%s: core_dts_eagle_set succeeded with id = %u", + __func__, target); + break; + } + case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: { + s32 spec = 0; + + eagle_ioctl_info("%s: control 0x%X (set volume commands)", + __func__, cmd); + if (copy_from_user((void *)&spec, (void *)arg, + sizeof(spec))) { + eagle_ioctl_err("%s: error reading volume command specifier (src:%pK, tgt:%pK, size:%zu)", + __func__, (void *)arg, &spec, sizeof(spec)); + return -EFAULT; + } + if (spec & 0x80000000) { + u32 idx = (spec & 0x0000F000) >> 12; + s32 size = spec & 0x00000FFF; + + eagle_ioctl_dbg("%s: setting volume command %i size: %i", + __func__, idx, size); + if (idx >= _vol_cmd_cnt) { + eagle_ioctl_err("%s: volume command index %u out of bounds (only %u allocated)", + __func__, idx, _vol_cmd_cnt); + return -EINVAL; + } + if (_volume_cmds_alloc2(idx, size) < 0) { + eagle_ioctl_err("%s: error allocating memory for volume controls", + __func__); + return -ENOMEM; + } + if (copy_from_user((void *)&_vol_cmds_d[idx], + (void *)(((char *)arg) + sizeof(int)), + sizeof(struct vol_cmds_d))) { + eagle_ioctl_err("%s: error reading volume command descriptor (src:%pK, tgt:%pK, size:%zu)", + __func__, ((char *)arg) + sizeof(int), + &_vol_cmds_d[idx], + sizeof(struct vol_cmds_d)); + return -EFAULT; + } + eagle_ioctl_dbg("%s: setting volume command %i spec (size %zu): %i %i %i %i", + __func__, idx, sizeof(struct vol_cmds_d), + _vol_cmds_d[idx].d[0], _vol_cmds_d[idx].d[1], + _vol_cmds_d[idx].d[2], _vol_cmds_d[idx].d[3]); + if (copy_from_user((void *)_vol_cmds[idx], + (void *)(((char *)arg) + (sizeof(int) + + sizeof(struct vol_cmds_d))), size)) { + eagle_ioctl_err("%s: error reading volume command string (src:%pK, tgt:%pK, size:%i)", + __func__, ((char *)arg) + (sizeof(int) + + sizeof(struct vol_cmds_d)), + _vol_cmds[idx], size); + return -EFAULT; + } + } else { + eagle_ioctl_dbg("%s: setting volume command size", + __func__); + if (spec < 0 || spec > VOL_CMD_CNT_MAX) { + eagle_ioctl_err("%s: volume command count %i out of bounds (min 0, max %i)", + __func__, spec, VOL_CMD_CNT_MAX); + return -EINVAL; + } else if (spec == 0) { + eagle_ioctl_dbg("%s: request to free volume commands", + __func__); + _volume_cmds_free(); + break; + } + eagle_ioctl_dbg("%s: setting volume command size requested = %i", + __func__, spec); + if (_volume_cmds_alloc1(spec) < 0) { + eagle_ioctl_err("%s: error allocating memory for volume controls", + __func__); + return -ENOMEM; + } + } + break; + } + default: { + eagle_ioctl_err("%s: control 0x%X (invalid control)", + __func__, cmd); + ret = -EINVAL; + } + } + return (int)ret; +} + +/** + * msm_dts_eagle_compat_ioctl() - To handle 32bit to 64bit ioctl compatibility + * @cmd: cmd to handle. + * @arg: argument to the cmd. + * + * Handle DTS Eagle ioctl cmds from 32bit userspace. + * + * Return: Return failure if any. + */ +#ifdef CONFIG_COMPAT +int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32: + cmd = DTS_EAGLE_IOCTL_GET_CACHE_SIZE; + break; + case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32: + cmd = DTS_EAGLE_IOCTL_SET_CACHE_SIZE; + break; + case DTS_EAGLE_IOCTL_GET_PARAM32: + cmd = DTS_EAGLE_IOCTL_GET_PARAM; + break; + case DTS_EAGLE_IOCTL_SET_PARAM32: + cmd = DTS_EAGLE_IOCTL_SET_PARAM; + break; + case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32: + cmd = DTS_EAGLE_IOCTL_SET_CACHE_BLOCK; + break; + case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32: + cmd = DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE; + break; + case DTS_EAGLE_IOCTL_GET_LICENSE32: + cmd = DTS_EAGLE_IOCTL_GET_LICENSE; + break; + case DTS_EAGLE_IOCTL_SET_LICENSE32: + cmd = DTS_EAGLE_IOCTL_SET_LICENSE; + break; + case DTS_EAGLE_IOCTL_SEND_LICENSE32: + cmd = DTS_EAGLE_IOCTL_SEND_LICENSE; + break; + case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32: + cmd = DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS; + break; + default: + break; + } + return msm_dts_eagle_ioctl(cmd, arg); +} +#endif +/** + * msm_dts_eagle_init_pre() - Initialize DTS premix module + * @ac: Initialize premix module in the ASM session. + * + * Initialize DTS premix module on provided ASM session + * + * Return: Return failure if any. + */ +int msm_dts_eagle_init_pre(struct audio_client *ac) +{ + return msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, + AUDPROC_MODULE_ID_DTS_HPX_PREMIX); +} + +/** + * msm_dts_eagle_deinit_pre() - Deinitialize DTS premix module + * @ac: Deinitialize premix module in the ASM session. + * + * Deinitialize DTS premix module on provided ASM session + * + * Return: Currently does nothing so 0. + */ +int msm_dts_eagle_deinit_pre(struct audio_client *ac) +{ + return 0; +} + +/** + * msm_dts_eagle_init_post() - Initialize DTS postmix module + * @port_id: Port id for the ADM session. + * @copp_idx: Copp idx for the ADM session. + * + * Initialize DTS postmix module on ADM session + * + * Return: Return failure if any. + */ +int msm_dts_eagle_init_post(int port_id, int copp_idx) +{ + return msm_dts_eagle_enable_adm(port_id, copp_idx, _is_hpx_enabled); +} + +/** + * msm_dts_eagle_deinit_post() - Deinitialize DTS postmix module + * @port_id: Port id for the ADM session. + * @topology: Topology in use. + * + * Deinitialize DTS postmix module on ADM session + * + * Return: Currently does nothing so 0. + */ +int msm_dts_eagle_deinit_post(int port_id, int topology) +{ + return 0; +} + +/** + * msm_dts_eagle_init_master_module() - Initialize both DTS modules + * @ac: Initialize modules in the ASM session. + * + * Initialize DTS modules on ASM session + * + * Return: Success. + */ +int msm_dts_eagle_init_master_module(struct audio_client *ac) +{ + _set_audioclient(ac); + msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, + AUDPROC_MODULE_ID_DTS_HPX_PREMIX); + msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, + AUDPROC_MODULE_ID_DTS_HPX_POSTMIX); + return 0; +} + +/** + * msm_dts_eagle_deinit_master_module() - Deinitialize both DTS modules + * @ac: Deinitialize modules in the ASM session. + * + * Deinitialize DTS modules on ASM session + * + * Return: Success. + */ +int msm_dts_eagle_deinit_master_module(struct audio_client *ac) +{ + msm_dts_eagle_deinit_pre(ac); + msm_dts_eagle_deinit_post(-1, 0); + _clear_audioclient(); + return 0; +} + +/** + * msm_dts_eagle_is_hpx_on() - Check if HPX effects are On + * + * Check if HPX effects are On + * + * Return: On/Off. + */ +int msm_dts_eagle_is_hpx_on(void) +{ + return _is_hpx_enabled; +} + +/** + * msm_dts_eagle_pcm_new() - Create hwdep node + * @runtime: snd_soc_pcm_runtime structure. + * + * Create hwdep node + * + * Return: Success. + */ +int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime) +{ + if (!_ref_cnt++) { + _init_cb_descs(); + _reg_ion_mem(); + } + return 0; +} + +/** + * msm_dts_eagle_pcm_free() - remove hwdep node + * @runtime: snd_soc_pcm_runtime structure. + * + * Remove hwdep node + * + * Return: void. + */ +void msm_dts_eagle_pcm_free(struct snd_pcm *pcm) +{ + if (!--_ref_cnt) + _unreg_ion_mem(); + vfree(_depc); +} + +MODULE_DESCRIPTION("DTS EAGLE platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c new file mode 100644 index 000000000000..5c6f1dfe4a24 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c @@ -0,0 +1,358 @@ +/* Copyright (c) 2012-2014, 2016-2017, The Linux Foundation. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-dts-srs-tm-config.h" +#include "msm-pcm-routing-v2.h" + +static int srs_port_id[AFE_MAX_PORTS] = {-1}; +static int srs_copp_idx[AFE_MAX_PORTS] = {-1}; +static union srs_trumedia_params_u msm_srs_trumedia_params; +static struct ion_client *ion_client; +static struct ion_handle *ion_handle; +static struct param_outband po; +static atomic_t ref_cnt; +#define ION_MEM_SIZE (8 * 1024) + +static int set_port_id(int port_id, int copp_idx) +{ + int index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return -EINVAL; + } + srs_port_id[index] = port_id; + srs_copp_idx[index] = copp_idx; + return 0; +} + +static void msm_dts_srs_tm_send_params(__s32 port_id, __u32 techs) +{ + __s32 index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id 0x%x\n", + __func__, index, port_id); + return; + } + if ((srs_copp_idx[index] < 0) || + (srs_copp_idx[index] >= MAX_COPPS_PER_PORT)) { + pr_debug("%s: send params called before copp open. so, caching\n", + __func__); + return; + } + pr_debug("SRS %s: called, port_id = %d, techs flags = %u\n", + __func__, port_id, techs); + /* force all if techs is set to 1 */ + if (techs == 1) + techs = 0xFFFFFFFF; + + if (techs & (1 << SRS_ID_WOWHD)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_WOWHD, + (void *)&msm_srs_trumedia_params.srs_params.wowhd); + if (techs & (1 << SRS_ID_CSHP)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_CSHP, + (void *)&msm_srs_trumedia_params.srs_params.cshp); + if (techs & (1 << SRS_ID_HPF)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HPF, + (void *)&msm_srs_trumedia_params.srs_params.hpf); + if (techs & (1 << SRS_ID_AEQ)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_AEQ, + (void *)&msm_srs_trumedia_params.srs_params.aeq); + if (techs & (1 << SRS_ID_HL)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HL, + (void *)&msm_srs_trumedia_params.srs_params.hl); + if (techs & (1 << SRS_ID_GEQ)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GEQ, + (void *)&msm_srs_trumedia_params.srs_params.geq); + if (techs & (1 << SRS_ID_GLOBAL)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GLOBAL, + (void *)&msm_srs_trumedia_params.srs_params.global); +} + + +static int msm_dts_srs_trumedia_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int msm_dts_srs_trumedia_control_set_(int port_id, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + __u16 offset, value, max = sizeof(msm_srs_trumedia_params) >> 1; + + if (SRS_CMD_UPLOAD == + (ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) { + __u32 techs = ucontrol->value.integer.value[0] & 0xFF; + __s32 index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id 0x%x\n", + __func__, index, port_id); + return -EINVAL; + } + pr_debug("SRS %s: send params request, flag = %u\n", + __func__, techs); + if (srs_port_id[index] >= 0 && techs) + msm_dts_srs_tm_send_params(port_id, techs); + return 0; + } + offset = (__u16)((ucontrol->value.integer.value[0] & + SRS_PARAM_OFFSET_MASK) >> 16); + value = (__u16)(ucontrol->value.integer.value[0] & + SRS_PARAM_VALUE_MASK); + if (offset < max) { + msm_srs_trumedia_params.raw_params[offset] = value; + pr_debug("SRS %s: index set... (max %d, requested %d, value 0x%X)\n", + __func__, max, offset, value); + } else { + pr_err("SRS %s: index out of bounds! (max %d, requested %d)\n", + __func__, max, offset); + } + return 0; +} + +static int msm_dts_srs_trumedia_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control normal called\n"); + msm_pcm_routing_acquire_lock(); + port_id = SLIMBUS_0_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_pcm_routing_release_lock(); + return ret; +} + +static int msm_dts_srs_trumedia_control_i2s_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control I2S called\n"); + msm_pcm_routing_acquire_lock(); + port_id = PRIMARY_I2S_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_pcm_routing_release_lock(); + return ret; +} + +static int msm_dts_srs_trumedia_control_mi2s_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control MI2S called\n"); + msm_pcm_routing_acquire_lock(); + port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_pcm_routing_release_lock(); + return ret; +} + +static int msm_dts_srs_trumedia_control_hdmi_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control HDMI called\n"); + msm_pcm_routing_acquire_lock(); + port_id = HDMI_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_pcm_routing_release_lock(); + return ret; +} + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls[] = { + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + {.reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls_hdmi[] = { + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia HDMI", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_hdmi_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + {.reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls_i2s[] = { + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia I2S", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_i2s_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + {.reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls_mi2s[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia MI2S", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_mi2s_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + { + .reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls, + ARRAY_SIZE(lpa_srs_trumedia_controls)); + + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls_hdmi, + ARRAY_SIZE(lpa_srs_trumedia_controls_hdmi)); + + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls_i2s, + ARRAY_SIZE(lpa_srs_trumedia_controls_i2s)); + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls_mi2s, + ARRAY_SIZE(lpa_srs_trumedia_controls_mi2s)); +} + +static int reg_ion_mem(void) +{ + int rc; + + rc = msm_audio_ion_alloc("SRS_TRUMEDIA", &ion_client, &ion_handle, + ION_MEM_SIZE, &po.paddr, (size_t *)&po.size, + &po.kvaddr); + if (rc != 0) + pr_err("%s: failed to allocate memory.\n", __func__); + pr_debug("%s: exited ion_client = %pK, ion_handle = %pK, phys_addr = %lu, length = %d, vaddr = %pK, rc = 0x%x\n", + __func__, ion_client, ion_handle, (long)po.paddr, + (unsigned int)po.size, po.kvaddr, rc); + return rc; +} + +void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) +{ + if (po.kvaddr == NULL) { + pr_debug("%s: callingreg_ion_mem()\n", __func__); + reg_ion_mem(); + } + po_->size = ION_MEM_SIZE; + po_->kvaddr = po.kvaddr; + po_->paddr = po.paddr; +} + +static void unreg_ion_mem(void) +{ + msm_audio_ion_free(ion_client, ion_handle); + po.kvaddr = NULL; + po.paddr = 0; + po.size = 0; +} + +void msm_dts_srs_tm_deinit(int port_id) +{ + set_port_id(port_id, -1); + atomic_dec(&ref_cnt); + if (po.kvaddr != NULL) { + if (!atomic_read(&ref_cnt)) { + pr_debug("%s: calling unreg_ion_mem()\n", __func__); + unreg_ion_mem(); + } + } +} + +void msm_dts_srs_tm_init(int port_id, int copp_idx) +{ + int cur_ref_cnt = 0; + + if (set_port_id(port_id, copp_idx) < 0) { + pr_err("%s: Invalid port_id: %d\n", __func__, port_id); + return; + } + + cur_ref_cnt = atomic_read(&ref_cnt); + atomic_inc(&ref_cnt); + if (!cur_ref_cnt && po.kvaddr == NULL) { + pr_debug("%s: calling reg_ion_mem()\n", __func__); + if (reg_ion_mem() != 0) { + atomic_dec(&ref_cnt); + po.kvaddr = NULL; + return; + } + } + msm_dts_srs_tm_send_params(port_id, 1); +} diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h new file mode 100644 index 000000000000..1dd7aedd5f27 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_DTS_SRS_TM_CONFIG_H_ +#define _MSM_DTS_SRS_TM_CONFIG_H_ + +#include + +struct param_outband; + +#ifdef CONFIG_DTS_SRS_TM + +union srs_trumedia_params_u { + struct srs_trumedia_params srs_params; + __u16 raw_params[1]; +}; + +void msm_dts_srs_tm_ion_memmap(struct param_outband *po_); +void msm_dts_srs_tm_init(int port_id, int copp_idx); +void msm_dts_srs_tm_deinit(int port_id); +void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform); +#else +static inline void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) { } +static inline void msm_dts_srs_tm_init(int port_id, int copp_idx) { } +static inline void msm_dts_srs_tm_deinit(int port_id) { } +static inline void msm_dts_srs_tm_add_controls( + struct snd_soc_platform *platform) { } + +#endif + +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c new file mode 100644 index 000000000000..b3427a278875 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c @@ -0,0 +1,1989 @@ +/* + * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" + +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 4096 +#define CAPTURE_MIN_PERIOD_SIZE 320 +#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256 + +#define LAB_BUFFER_ALLOC 1 +#define LAB_BUFFER_DEALLOC 0 + +static struct snd_pcm_hardware msm_pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_16000, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * + CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 16000, +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +struct lsm_priv { + struct snd_pcm_substream *substream; + struct lsm_client *lsm_client; + struct snd_lsm_event_status *event_status; + spinlock_t event_lock; + wait_queue_head_t event_wait; + unsigned long event_avail; + atomic_t event_wait_stop; + atomic_t buf_count; + atomic_t read_abort; + wait_queue_head_t period_wait; + int appl_cnt; + int dma_write; +}; + +static int msm_lsm_queue_lab_buffer(struct lsm_priv *prtd, int i) +{ + int rc = 0; + struct lsm_cmd_read cmd_read; + struct snd_soc_pcm_runtime *rtd; + + if (!prtd || !prtd->lsm_client) { + pr_err("%s: Invalid params prtd %pK lsm client %pK\n", + __func__, prtd, ((!prtd) ? NULL : prtd->lsm_client)); + return -EINVAL; + } + if (!prtd->substream || !prtd->substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!prtd->substream) ? "substream" : "private_data"); + return -EINVAL; + } + rtd = prtd->substream->private_data; + + if (!prtd->lsm_client->lab_buffer || + i >= prtd->lsm_client->hw_params.period_count) { + dev_err(rtd->dev, + "%s: Lab buffer not setup %pK incorrect index %d period count %d\n", + __func__, prtd->lsm_client->lab_buffer, i, + prtd->lsm_client->hw_params.period_count); + return -EINVAL; + } + cmd_read.buf_addr_lsw = + lower_32_bits(prtd->lsm_client->lab_buffer[i].phys); + cmd_read.buf_addr_msw = + msm_audio_populate_upper_32_bits( + prtd->lsm_client->lab_buffer[i].phys); + cmd_read.buf_size = prtd->lsm_client->lab_buffer[i].size; + cmd_read.mem_map_handle = + prtd->lsm_client->lab_buffer[i].mem_map_handle; + rc = q6lsm_read(prtd->lsm_client, &cmd_read); + if (rc) + dev_err(rtd->dev, + "%s: error in queuing the lab buffer rc %d\n", + __func__, rc); + return rc; +} + +static int lsm_lab_buffer_sanity(struct lsm_priv *prtd, + struct lsm_cmd_read_done *read_done, int *index) +{ + int i = 0, rc = -EINVAL; + struct snd_soc_pcm_runtime *rtd; + + if (!prtd || !read_done || !index) { + pr_err("%s: Invalid params prtd %pK read_done %pK index %pK\n", + __func__, prtd, read_done, index); + return -EINVAL; + } + + if (!prtd->substream || !prtd->substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!prtd->substream) ? "substream" : "private_data"); + return -EINVAL; + } + rtd = prtd->substream->private_data; + + if (!prtd->lsm_client->lab_enable || !prtd->lsm_client->lab_buffer) { + dev_err(rtd->dev, + "%s: Lab not enabled %d invalid lab buffer %pK\n", + __func__, prtd->lsm_client->lab_enable, + prtd->lsm_client->lab_buffer); + return -EINVAL; + } + for (i = 0; i < prtd->lsm_client->hw_params.period_count; i++) { + if ((lower_32_bits(prtd->lsm_client->lab_buffer[i].phys) == + read_done->buf_addr_lsw) && + (msm_audio_populate_upper_32_bits + (prtd->lsm_client->lab_buffer[i].phys) == + read_done->buf_addr_msw) && + (prtd->lsm_client->lab_buffer[i].mem_map_handle == + read_done->mem_map_handle)) { + dev_dbg(rtd->dev, + "%s: Buffer found %pK memmap handle %d\n", + __func__, &prtd->lsm_client->lab_buffer[i].phys, + prtd->lsm_client->lab_buffer[i].mem_map_handle); + if (read_done->total_size > + prtd->lsm_client->lab_buffer[i].size) { + dev_err(rtd->dev, + "%s: Size mismatch call back size %d actual size %zd\n", + __func__, read_done->total_size, + prtd->lsm_client->lab_buffer[i].size); + rc = -EINVAL; + break; + } else { + *index = i; + rc = 0; + break; + } + } + } + return rc; +} + +static void lsm_event_handler(uint32_t opcode, uint32_t token, + void *payload, void *priv) +{ + unsigned long flags; + struct lsm_priv *prtd = priv; + struct snd_pcm_substream *substream = prtd->substream; + struct snd_soc_pcm_runtime *rtd; + struct snd_lsm_event_status *temp; + uint16_t status = 0; + uint16_t payload_size = 0; + uint16_t index = 0; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return; + } + rtd = substream->private_data; + + switch (opcode) { + case LSM_DATA_EVENT_READ_DONE: { + int rc; + struct lsm_cmd_read_done *read_done = payload; + int buf_index = 0; + + if (prtd->lsm_client->session != token || + !read_done) { + dev_err(rtd->dev, + "%s: EVENT_READ_DONE invalid callback, session %d callback %d payload %pK", + __func__, prtd->lsm_client->session, + token, read_done); + return; + } + if (atomic_read(&prtd->read_abort)) { + dev_dbg(rtd->dev, + "%s: read abort set skip data\n", __func__); + return; + } + if (!lsm_lab_buffer_sanity(prtd, read_done, &buf_index)) { + dev_dbg(rtd->dev, + "%s: process read done index %d\n", + __func__, buf_index); + if (buf_index >= + prtd->lsm_client->hw_params.period_count) { + dev_err(rtd->dev, + "%s: Invalid index %d buf_index max cnt %d\n", + __func__, buf_index, + prtd->lsm_client->hw_params.period_count); + return; + } + prtd->dma_write += read_done->total_size; + atomic_inc(&prtd->buf_count); + snd_pcm_period_elapsed(substream); + wake_up(&prtd->period_wait); + /* queue the next period buffer */ + buf_index = (buf_index + 1) % + prtd->lsm_client->hw_params.period_count; + rc = msm_lsm_queue_lab_buffer(prtd, buf_index); + if (rc) + dev_err(rtd->dev, + "%s: error in queuing the lab buffer rc %d\n", + __func__, rc); + } else + dev_err(rtd->dev, "%s: Invalid lab buffer returned by dsp\n", + __func__); + break; + } + + case LSM_SESSION_EVENT_DETECTION_STATUS: + status = (uint16_t)((uint8_t *)payload)[0]; + payload_size = (uint16_t)((uint8_t *)payload)[2]; + index = 4; + dev_dbg(rtd->dev, + "%s: event detect status = %d payload size = %d\n", + __func__, status, payload_size); + break; + + case LSM_SESSION_EVENT_DETECTION_STATUS_V2: + status = (uint16_t)((uint8_t *)payload)[0]; + payload_size = (uint16_t)((uint8_t *)payload)[1]; + index = 2; + dev_dbg(rtd->dev, + "%s: event detect status = %d payload size = %d\n", + __func__, status, payload_size); + break; + default: + break; + } + + if (opcode == LSM_SESSION_EVENT_DETECTION_STATUS || + opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + spin_lock_irqsave(&prtd->event_lock, flags); + temp = krealloc(prtd->event_status, + sizeof(struct snd_lsm_event_status) + + payload_size, GFP_ATOMIC); + if (!temp) { + dev_err(rtd->dev, "%s: no memory for event status\n", + __func__); + return; + } + + prtd->event_status = temp; + prtd->event_status->status = status; + prtd->event_status->payload_size = payload_size; + if (likely(prtd->event_status)) { + memcpy(prtd->event_status->payload, + &((uint8_t *)payload)[index], + payload_size); + prtd->event_avail = 1; + spin_unlock_irqrestore(&prtd->event_lock, flags); + wake_up(&prtd->event_wait); + } else { + spin_unlock_irqrestore(&prtd->event_lock, flags); + dev_err(rtd->dev, + "%s: Couldn't allocate %d bytes of memory\n", + __func__, payload_size); + } + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + } +} + +static int msm_lsm_lab_buffer_alloc(struct lsm_priv *lsm, int alloc) +{ + int ret = 0; + struct snd_dma_buffer *dma_buf = NULL; + + if (!lsm) { + pr_err("%s: Invalid param lsm %pK\n", __func__, lsm); + return -EINVAL; + } + if (alloc) { + if (!lsm->substream) { + pr_err("%s: substream is NULL\n", __func__); + return -EINVAL; + } + ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc); + if (ret) { + pr_err("%s: alloc lab buffer failed ret %d\n", + __func__, ret); + goto exit; + } + dma_buf = &lsm->substream->dma_buffer; + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = lsm->substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = lsm->lsm_client->lab_buffer[0].data; + dma_buf->addr = lsm->lsm_client->lab_buffer[0].phys; + dma_buf->bytes = lsm->lsm_client->hw_params.buf_sz * + lsm->lsm_client->hw_params.period_count; + snd_pcm_set_runtime_buffer(lsm->substream, dma_buf); + } else { + ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc); + if (ret) + pr_err("%s: free lab buffer failed ret %d\n", + __func__, ret); + kfree(lsm->lsm_client->lab_buffer); + lsm->lsm_client->lab_buffer = NULL; + } +exit: + return ret; +} + +static int msm_lsm_get_conf_levels(struct lsm_client *client, + u8 *conf_levels_ptr) +{ + int rc = 0; + + if (client->num_confidence_levels == 0) { + pr_debug("%s: no confidence levels provided\n", + __func__); + client->confidence_levels = NULL; + goto done; + } + + client->confidence_levels = + kzalloc((sizeof(uint8_t) * client->num_confidence_levels), + GFP_KERNEL); + if (!client->confidence_levels) { + pr_err("%s: No memory for confidence\n" + "levels num of level from user = %d\n", + __func__, client->num_confidence_levels); + rc = -ENOMEM; + goto done; + } + + if (copy_from_user(client->confidence_levels, + conf_levels_ptr, + client->num_confidence_levels)) { + pr_err("%s: copy from user failed, size = %d\n", + __func__, client->num_confidence_levels); + rc = -EFAULT; + goto copy_err; + } + + return rc; + +copy_err: + kfree(client->confidence_levels); + client->confidence_levels = NULL; +done: + return rc; + +} + +static int msm_lsm_set_epd(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + struct snd_lsm_ep_det_thres epd_th; + + if (p_info->param_size != sizeof(epd_th)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&epd_th, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &epd_th, LSM_ENDPOINT_DETECT_THRESHOLD); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set epd param, err = %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_lsm_set_mode(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_lsm_detect_mode mode; + int rc = 0; + + if (p_info->param_size != sizeof(mode)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&mode, p_info->param_data, + sizeof(mode))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(mode)); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &mode, LSM_OPERATION_MODE); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set det_mode param, err = %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_lsm_set_gain(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_lsm_gain gain; + int rc = 0; + + if (p_info->param_size != sizeof(gain)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&gain, p_info->param_data, + sizeof(gain))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(gain)); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &gain, LSM_GAIN); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set det_mode param, err = %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_lsm_set_conf(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (p_info->param_size > MAX_NUM_CONFIDENCE) { + dev_err(rtd->dev, + "%s: invalid confidence levels %d\n", + __func__, p_info->param_size); + return -EINVAL; + } + + prtd->lsm_client->num_confidence_levels = + p_info->param_size; + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + p_info->param_data); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + return rc; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + prtd->lsm_client->confidence_levels, + LSM_MIN_CONFIDENCE_LEVELS); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set min_conf_levels, err = %d\n", + __func__, rc); + + return rc; +} + +static int msm_lsm_reg_model(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + u8 *snd_model_ptr; + size_t offset; + + rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client, + p_info->param_size, + true); + if (rc) { + dev_err(rtd->dev, + "%s: snd_model buf alloc failed, size = %d\n", + __func__, p_info->param_size); + return rc; + } + + q6lsm_sm_set_param_data(prtd->lsm_client, p_info, &offset); + + /* + * For set_param, advance the sound model data with the + * number of bytes required by param_data. + */ + snd_model_ptr = ((u8 *) prtd->lsm_client->sound_model.data) + offset; + + if (copy_from_user(snd_model_ptr, + p_info->param_data, p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user for snd_model failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_copy; + } + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, NULL, + LSM_REG_SND_MODEL); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set sound_model, err = %d\n", + __func__, rc); + goto err_copy; + } + return rc; + +err_copy: + q6lsm_snd_model_buf_free(prtd->lsm_client); + return rc; +} + +static int msm_lsm_dereg_model(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + NULL, LSM_DEREG_SND_MODEL); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set det_mode param, err = %d\n", + __func__, rc); + + q6lsm_snd_model_buf_free(prtd->lsm_client); + + return rc; +} + +static int msm_lsm_set_custom(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u8 *data; + int rc = 0; + + data = kzalloc(p_info->param_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for custom params, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_ret; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + data, LSM_CUSTOM_PARAMS); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set custom param, err = %d\n", + __func__, rc); + +err_ret: + kfree(data); + return rc; +} + +static int msm_lsm_process_params(struct snd_pcm_substream *substream, + struct snd_lsm_module_params *p_data, + void *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct lsm_params_info *p_info; + int i; + int rc = 0; + + p_info = (struct lsm_params_info *) params; + + for (i = 0; i < p_data->num_params; i++) { + dev_dbg(rtd->dev, + "%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n", + __func__, i, p_info->module_id, + p_info->param_id, p_info->param_size, + p_info->param_type); + + switch (p_info->param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = msm_lsm_set_epd(substream, p_info); + break; + case LSM_OPERATION_MODE: + rc = msm_lsm_set_mode(substream, p_info); + break; + case LSM_GAIN: + rc = msm_lsm_set_gain(substream, p_info); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = msm_lsm_set_conf(substream, p_info); + break; + case LSM_REG_SND_MODEL: + rc = msm_lsm_reg_model(substream, p_info); + break; + case LSM_DEREG_SND_MODEL: + rc = msm_lsm_dereg_model(substream, p_info); + break; + case LSM_CUSTOM_PARAMS: + rc = msm_lsm_set_custom(substream, p_info); + break; + default: + dev_err(rtd->dev, + "%s: Invalid param_type %d\n", + __func__, p_info->param_type); + rc = -EINVAL; + break; + } + if (rc) { + pr_err("%s: set_param fail for param_type %d\n", + __func__, p_info->param_type); + return rc; + } + + p_info++; + } + + return rc; +} + +static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_soc_pcm_runtime *rtd; + unsigned long flags; + int ret; + struct snd_lsm_sound_model_v2 snd_model_v2; + struct snd_lsm_session_data session_data; + int rc = 0; + int xchg = 0; + u32 size = 0; + struct snd_pcm_runtime *runtime; + struct lsm_priv *prtd; + struct snd_lsm_event_status *user = arg; + struct snd_lsm_detection_params det_params; + uint8_t *confidence_level = NULL; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return -EINVAL; + } + + runtime = substream->runtime; + prtd = runtime->private_data; + rtd = substream->private_data; + + switch (cmd) { + case SNDRV_LSM_SET_SESSION_DATA: + dev_dbg(rtd->dev, "%s: set session data\n", __func__); + memcpy(&session_data, arg, + sizeof(struct snd_lsm_session_data)); + if (session_data.app_id != LSM_VOICE_WAKEUP_APP_ID_V2) { + dev_err(rtd->dev, + "%s:Invalid App id %d for Listen client\n", + __func__, session_data.app_id); + rc = -EINVAL; + break; + } + + prtd->lsm_client->app_id = session_data.app_id; + ret = q6lsm_open(prtd->lsm_client, + prtd->lsm_client->app_id); + if (ret < 0) { + dev_err(rtd->dev, + "%s: lsm open failed, %d\n", + __func__, ret); + return ret; + } + prtd->lsm_client->opened = true; + dev_dbg(rtd->dev, "%s: Session_ID = %d, APP ID = %d\n", + __func__, + prtd->lsm_client->session, + prtd->lsm_client->app_id); + break; + case SNDRV_LSM_REG_SND_MODEL_V2: + dev_dbg(rtd->dev, "%s: Registering sound model V2\n", + __func__); + memcpy(&snd_model_v2, arg, + sizeof(struct snd_lsm_sound_model_v2)); + if (snd_model_v2.num_confidence_levels > + MAX_NUM_CONFIDENCE) { + dev_err(rtd->dev, + "%s: Invalid conf_levels = %d, maximum allowed = %d\n", + __func__, snd_model_v2.num_confidence_levels, + MAX_NUM_CONFIDENCE); + rc = -EINVAL; + break; + } + rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client, + snd_model_v2.data_size, false); + if (rc) { + dev_err(rtd->dev, + "%s: q6lsm buffer alloc failed V2, size %d\n", + __func__, snd_model_v2.data_size); + break; + } + if (copy_from_user(prtd->lsm_client->sound_model.data, + snd_model_v2.data, snd_model_v2.data_size)) { + dev_err(rtd->dev, + "%s: copy from user data failed\n" + "data %pK size %d\n", __func__, + snd_model_v2.data, snd_model_v2.data_size); + q6lsm_snd_model_buf_free(prtd->lsm_client); + rc = -EFAULT; + break; + } + + dev_dbg(rtd->dev, "SND Model Magic no byte[0] %x,\n" + "byte[1] %x, byte[2] %x byte[3] %x\n", + snd_model_v2.data[0], snd_model_v2.data[1], + snd_model_v2.data[2], snd_model_v2.data[3]); + prtd->lsm_client->num_confidence_levels = + snd_model_v2.num_confidence_levels; + + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + snd_model_v2.confidence_level); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + break; + } + + rc = q6lsm_register_sound_model(prtd->lsm_client, + snd_model_v2.detection_mode, + snd_model_v2.detect_failure); + if (rc < 0) { + dev_err(rtd->dev, + "%s: Register snd Model v2 failed =%d\n", + __func__, rc); + kfree(confidence_level); + q6lsm_snd_model_buf_free(prtd->lsm_client); + } + + kfree(prtd->lsm_client->confidence_levels); + prtd->lsm_client->confidence_levels = NULL; + break; + + case SNDRV_LSM_SET_PARAMS: + if (!arg) { + dev_err(rtd->dev, + "%s: %s Invalid argument\n", + __func__, "SNDRV_LSM_SET_PARAMS"); + return -EINVAL; + } + + dev_dbg(rtd->dev, "%s: set_params\n", __func__); + memcpy(&det_params, arg, + sizeof(det_params)); + if (det_params.num_confidence_levels > + MAX_NUM_CONFIDENCE) { + rc = -EINVAL; + break; + } + + prtd->lsm_client->num_confidence_levels = + det_params.num_confidence_levels; + + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + det_params.conf_level); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to get conf_levels, err = %d\n", + __func__, rc); + break; + } + + rc = q6lsm_set_data(prtd->lsm_client, + det_params.detect_mode, + det_params.detect_failure); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set params, err = %d\n", + __func__, rc); + + kfree(prtd->lsm_client->confidence_levels); + prtd->lsm_client->confidence_levels = NULL; + + break; + + case SNDRV_LSM_DEREG_SND_MODEL: + dev_dbg(rtd->dev, "%s: Deregistering sound model\n", + __func__); + rc = q6lsm_deregister_sound_model(prtd->lsm_client); + if (rc) + dev_err(rtd->dev, + "%s: Sound model de-register failed, err = %d\n", + __func__, rc); + break; + + case SNDRV_LSM_EVENT_STATUS: + dev_dbg(rtd->dev, "%s: Get event status\n", __func__); + atomic_set(&prtd->event_wait_stop, 0); + rc = wait_event_freezable(prtd->event_wait, + (cmpxchg(&prtd->event_avail, 1, 0) || + (xchg = atomic_cmpxchg(&prtd->event_wait_stop, + 1, 0)))); + dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n", + __func__, rc, xchg); + if (!rc && !xchg) { + dev_dbg(rtd->dev, "%s: New event available %ld\n", + __func__, prtd->event_avail); + spin_lock_irqsave(&prtd->event_lock, flags); + if (prtd->event_status) { + size = sizeof(*(prtd->event_status)) + + prtd->event_status->payload_size; + spin_unlock_irqrestore(&prtd->event_lock, + flags); + } else { + spin_unlock_irqrestore(&prtd->event_lock, + flags); + rc = -EINVAL; + dev_err(rtd->dev, + "%s: prtd->event_status is NULL\n", + __func__); + break; + } + if (user->payload_size < + prtd->event_status->payload_size) { + dev_dbg(rtd->dev, + "%s: provided %d bytes isn't enough, needs %d bytes\n", + __func__, user->payload_size, + prtd->event_status->payload_size); + rc = -ENOMEM; + } else { + memcpy(user, prtd->event_status, size); + if (prtd->lsm_client->lab_enable + && !prtd->lsm_client->lab_started + && prtd->event_status->status == + LSM_VOICE_WAKEUP_STATUS_DETECTED) { + atomic_set(&prtd->read_abort, 0); + atomic_set(&prtd->buf_count, 0); + prtd->appl_cnt = 0; + prtd->dma_write = 0; + rc = msm_lsm_queue_lab_buffer(prtd, + 0); + if (rc) + dev_err(rtd->dev, + "%s: Queue buffer failed for lab rc = %d\n", + __func__, rc); + else + prtd->lsm_client->lab_started + = true; + } + } + } else if (xchg) { + dev_dbg(rtd->dev, "%s: Wait aborted\n", __func__); + rc = 0; + } + break; + + case SNDRV_LSM_ABORT_EVENT: + dev_dbg(rtd->dev, "%s: Aborting event status wait\n", + __func__); + atomic_set(&prtd->event_wait_stop, 1); + wake_up(&prtd->event_wait); + break; + + case SNDRV_LSM_START: + dev_dbg(rtd->dev, "%s: Starting LSM client session\n", + __func__); + if (!prtd->lsm_client->started) { + ret = q6lsm_start(prtd->lsm_client, true); + if (!ret) { + prtd->lsm_client->started = true; + dev_dbg(rtd->dev, "%s: LSM client session started\n", + __func__); + } + } + break; + + case SNDRV_LSM_STOP: { + dev_dbg(rtd->dev, + "%s: Stopping LSM client session\n", + __func__); + if (prtd->lsm_client->started) { + if (prtd->lsm_client->lab_enable) { + atomic_set(&prtd->read_abort, 1); + if (prtd->lsm_client->lab_started) { + ret = q6lsm_stop_lab(prtd->lsm_client); + if (ret) + dev_err(rtd->dev, + "%s: stop lab failed ret %d\n", + __func__, ret); + prtd->lsm_client->lab_started = false; + } + } + ret = q6lsm_stop(prtd->lsm_client, true); + if (!ret) + dev_dbg(rtd->dev, + "%s: LSM client session stopped %d\n", + __func__, ret); + prtd->lsm_client->started = false; + } + break; + } + case SNDRV_LSM_LAB_CONTROL: { + u32 *enable = NULL; + + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid param arg for ioctl %s session %d\n", + __func__, "SNDRV_LSM_LAB_CONTROL", + prtd->lsm_client->session); + rc = -EINVAL; + break; + } + enable = (int *)arg; + dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n", + __func__, "SNDRV_LSM_LAB_CONTROL", *enable); + if (!prtd->lsm_client->started) { + if (prtd->lsm_client->lab_enable == *enable) { + dev_dbg(rtd->dev, + "%s: Lab for session %d already %s\n", + __func__, prtd->lsm_client->session, + ((*enable) ? "enabled" : "disabled")); + rc = 0; + break; + } + rc = q6lsm_lab_control(prtd->lsm_client, *enable); + if (rc) { + dev_err(rtd->dev, + "%s: ioctl %s failed rc %d to %s lab for session %d\n", + __func__, "SNDRV_LAB_CONTROL", rc, + ((*enable) ? "enable" : "disable"), + prtd->lsm_client->session); + } else { + rc = msm_lsm_lab_buffer_alloc(prtd, + ((*enable) ? LAB_BUFFER_ALLOC + : LAB_BUFFER_DEALLOC)); + if (rc) + dev_err(rtd->dev, + "%s: msm_lsm_lab_buffer_alloc failed rc %d for %s", + __func__, rc, + ((*enable) ? "ALLOC" : "DEALLOC")); + if (!rc) + prtd->lsm_client->lab_enable = *enable; + } + } else { + dev_err(rtd->dev, "%s: ioctl %s issued after start", + __func__, "SNDRV_LSM_LAB_CONTROL"); + rc = -EINVAL; + } + break; + } + case SNDRV_LSM_STOP_LAB: + dev_dbg(rtd->dev, "%s: stopping LAB\n", __func__); + if (prtd->lsm_client->lab_enable && + prtd->lsm_client->lab_started) { + atomic_set(&prtd->read_abort, 1); + rc = q6lsm_stop_lab(prtd->lsm_client); + if (rc) + dev_err(rtd->dev, + "%s: Lab stop failed for session %d rc %d\n", + __func__, + prtd->lsm_client->session, rc); + prtd->lsm_client->lab_started = false; + } + break; + default: + dev_dbg(rtd->dev, + "%s: Falling into default snd_lib_ioctl cmd 0x%x\n", + __func__, cmd); + rc = snd_pcm_lib_ioctl(substream, cmd, arg); + break; + } + + if (!rc) + dev_dbg(rtd->dev, "%s: leave (%d)\n", + __func__, rc); + else + dev_err(rtd->dev, "%s: cmd 0x%x failed %d\n", + __func__, cmd, rc); + + return rc; +} +#ifdef CONFIG_COMPAT +struct snd_lsm_event_status32 { + u16 status; + u16 payload_size; + u8 payload[0]; +}; + +struct snd_lsm_sound_model_v2_32 { + compat_uptr_t data; + compat_uptr_t confidence_level; + u32 data_size; + enum lsm_detection_mode detection_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct snd_lsm_detection_params_32 { + compat_uptr_t conf_level; + enum lsm_detection_mode detect_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct lsm_params_info_32 { + u32 module_id; + u32 param_id; + u32 param_size; + compat_uptr_t param_data; + enum LSM_PARAM_TYPE param_type; +}; + +struct snd_lsm_module_params_32 { + compat_uptr_t params; + u32 num_params; + u32 data_size; +}; + +enum { + SNDRV_LSM_EVENT_STATUS32 = + _IOW('U', 0x02, struct snd_lsm_event_status32), + SNDRV_LSM_REG_SND_MODEL_V2_32 = + _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32), + SNDRV_LSM_SET_PARAMS_32 = + _IOW('U', 0x0A, struct snd_lsm_detection_params_32), + SNDRV_LSM_SET_MODULE_PARAMS_32 = + _IOW('U', 0x0B, struct snd_lsm_module_params_32), +}; + +static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, + unsigned int cmd, void __user *arg) +{ + struct snd_pcm_runtime *runtime; + struct lsm_priv *prtd; + struct snd_soc_pcm_runtime *rtd; + int err = 0; + u32 size = 0; + + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return -EINVAL; + } + runtime = substream->runtime; + rtd = substream->private_data; + prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_LSM_EVENT_STATUS32: { + struct snd_lsm_event_status32 userarg32, *user32 = NULL; + struct snd_lsm_event_status *user = NULL; + + if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { + dev_err(rtd->dev, "%s: err copyuser ioctl %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS32"); + return -EFAULT; + } + + if (userarg32.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + pr_err("%s: payload_size %d is invalid, max allowed = %d\n", + __func__, userarg32.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + return -EINVAL; + } + + size = sizeof(*user) + userarg32.payload_size; + user = kmalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + return -EFAULT; + } else { + cmd = SNDRV_LSM_EVENT_STATUS; + user->payload_size = userarg32.payload_size; + err = msm_lsm_ioctl_shared(substream, cmd, user); + } + + /* Update size with actual payload size */ + size = sizeof(userarg32) + user->payload_size; + if (!err && !access_ok(VERIFY_WRITE, arg, size)) { + dev_err(rtd->dev, + "%s: write verify failed size %d\n", + __func__, size); + err = -EFAULT; + } + if (!err) { + user32 = kmalloc(size, GFP_KERNEL); + if (!user32) { + dev_err(rtd->dev, + "%s: Allocation event user status size %d\n", + __func__, size); + err = -EFAULT; + } else { + user32->status = user->status; + user32->payload_size = user->payload_size; + memcpy(user32->payload, + user->payload, user32->payload_size); + } + } + if (!err && (copy_to_user(arg, user32, size))) { + dev_err(rtd->dev, "%s: failed to copy payload %d", + __func__, size); + err = -EFAULT; + } + kfree(user); + kfree(user32); + if (err) + dev_err(rtd->dev, "%s: lsmevent failed %d", + __func__, err); + break; + } + + case SNDRV_LSM_REG_SND_MODEL_V2_32: { + struct snd_lsm_sound_model_v2_32 snd_modelv232; + struct snd_lsm_sound_model_v2 snd_modelv2; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "REG_SND_MODEL_V2"); + return -EINVAL; + } + + if (copy_from_user(&snd_modelv232, arg, + sizeof(snd_modelv232))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: copy user failed, size %zd %s\n", + __func__, + sizeof(struct snd_lsm_sound_model_v2_32), + "SNDRV_LSM_REG_SND_MODEL_V2_32"); + } else { + snd_modelv2.confidence_level = + compat_ptr(snd_modelv232.confidence_level); + snd_modelv2.data = compat_ptr(snd_modelv232.data); + snd_modelv2.data_size = snd_modelv232.data_size; + snd_modelv2.detect_failure = + snd_modelv232.detect_failure; + snd_modelv2.detection_mode = + snd_modelv232.detection_mode; + snd_modelv2.num_confidence_levels = + snd_modelv232.num_confidence_levels; + cmd = SNDRV_LSM_REG_SND_MODEL_V2; + err = msm_lsm_ioctl_shared(substream, cmd, + &snd_modelv2); + if (err) + dev_err(rtd->dev, + "%s: ioctl %s failed\n", __func__, + "SNDDRV_LSM_REG_SND_MODEL_V2_32"); + } + break; + } + + case SNDRV_LSM_SET_PARAMS_32:{ + struct snd_lsm_detection_params_32 det_params32; + struct snd_lsm_detection_params det_params; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SET_PARAMS_32"); + return -EINVAL; + } + + if (copy_from_user(&det_params32, arg, + sizeof(det_params32))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS_32", + sizeof(det_params32)); + } else { + det_params.conf_level = + compat_ptr(det_params32.conf_level); + det_params.detect_mode = + det_params32.detect_mode; + det_params.num_confidence_levels = + det_params32.num_confidence_levels; + det_params.detect_failure = + det_params32.detect_failure; + cmd = SNDRV_LSM_SET_PARAMS; + err = msm_lsm_ioctl_shared(substream, cmd, + &det_params); + if (err) + dev_err(rtd->dev, + "%s: ioctl %s failed\n", __func__, + "SNDRV_LSM_SET_PARAMS"); + } + break; + } + + case SNDRV_LSM_SET_MODULE_PARAMS_32: { + struct snd_lsm_module_params_32 p_data_32; + struct snd_lsm_module_params p_data; + u8 *params, *params32; + size_t p_size; + struct lsm_params_info_32 *p_info_32; + struct lsm_params_info *p_info; + int i; + + if (!prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS_32"); + return -EINVAL; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: %s: No Param data to set\n", + __func__, "SET_MODULE_PARAMS_32"); + return -EINVAL; + } + + if (copy_from_user(&p_data_32, arg, + sizeof(p_data_32))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SET_MODULE_PARAMS_32", + sizeof(p_data_32)); + return -EFAULT; + } + + p_data.params = compat_ptr(p_data_32.params); + p_data.num_params = p_data_32.num_params; + p_data.data_size = p_data_32.data_size; + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS_32", + p_data.num_params); + return -EINVAL; + } + + if (p_data.data_size != + (p_data.num_params * sizeof(struct lsm_params_info_32))) { + dev_err(rtd->dev, + "%s: %s: Invalid size %d\n", + __func__, "SET_MODULE_PARAMS_32", + p_data.data_size); + return -EINVAL; + } + + p_size = sizeof(struct lsm_params_info_32) * + p_data.num_params; + + params32 = kzalloc(p_size, GFP_KERNEL); + if (!params32) + return -ENOMEM; + + p_size = sizeof(struct lsm_params_info) * p_data.num_params; + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + dev_err(rtd->dev, + "%s: no memory for params, size = %zd\n", + __func__, p_size); + kfree(params32); + return -ENOMEM; + } + + if (copy_from_user(params32, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params32", p_data.data_size); + kfree(params32); + kfree(params); + return -EFAULT; + } + + p_info_32 = (struct lsm_params_info_32 *) params32; + p_info = (struct lsm_params_info *) params; + for (i = 0; i < p_data.num_params; i++) { + p_info->module_id = p_info_32->module_id; + p_info->param_id = p_info_32->param_id; + p_info->param_size = p_info_32->param_size; + p_info->param_data = compat_ptr(p_info_32->param_data); + p_info->param_type = p_info_32->param_type; + + p_info_32++; + p_info++; + } + + err = msm_lsm_process_params(substream, + &p_data, params); + if (err) + dev_err(rtd->dev, + "%s: Failed to process params, err = %d\n", + __func__, err); + kfree(params); + kfree(params32); + break; + } + default: + err = msm_lsm_ioctl_shared(substream, cmd, arg); + break; + } + return err; +} +#else +#define msm_lsm_ioctl_compat NULL +#endif + +static int msm_lsm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + u32 size = 0; + struct snd_lsm_session_data session_data; + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; + struct lsm_priv *prtd; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return -EINVAL; + } + runtime = substream->runtime; + prtd = runtime->private_data; + rtd = substream->private_data; + + switch (cmd) { + case SNDRV_LSM_SET_SESSION_DATA: + dev_dbg(rtd->dev, + "%s: SNDRV_LSM_SET_SESSION_DATA\n", + __func__); + if (copy_from_user(&session_data, (void *)arg, + sizeof(struct snd_lsm_session_data))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, sizeof(struct snd_lsm_session_data)); + break; + } + if (!err) + err = msm_lsm_ioctl_shared(substream, + cmd, &session_data); + if (err) + dev_err(rtd->dev, + "%s REG_SND_MODEL failed err %d\n", + __func__, err); + break; + case SNDRV_LSM_REG_SND_MODEL_V2: { + struct snd_lsm_sound_model_v2 snd_model_v2; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "REG_SND_MODEL_V2"); + return -EINVAL; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid params snd_model\n", __func__); + return -EINVAL; + } + if (copy_from_user(&snd_model_v2, arg, sizeof(snd_model_v2))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_sound_model_v2)); + } + if (!err) + err = msm_lsm_ioctl_shared(substream, cmd, + &snd_model_v2); + if (err) + dev_err(rtd->dev, + "%s REG_SND_MODEL failed err %d\n", + __func__, err); + return err; + } + break; + case SNDRV_LSM_SET_PARAMS: { + struct snd_lsm_detection_params det_params; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SET_PARAMS"); + return -EINVAL; + } + + pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__); + if (!arg) { + dev_err(rtd->dev, + "%s: %s, Invalid params\n", + __func__, "SNDRV_LSM_SET_PARAMS"); + return -EINVAL; + } + + if (copy_from_user(&det_params, arg, + sizeof(det_params))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS", + sizeof(det_params)); + err = -EFAULT; + } + + if (!err) + err = msm_lsm_ioctl_shared(substream, cmd, + &det_params); + else + dev_err(rtd->dev, + "%s: LSM_SET_PARAMS failed, err %d\n", + __func__, err); + return err; + } + + case SNDRV_LSM_SET_MODULE_PARAMS: { + struct snd_lsm_module_params p_data; + size_t p_size; + u8 *params; + + if (!prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS"); + return -EINVAL; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: %s: No Param data to set\n", + __func__, "SET_MODULE_PARAMS"); + return -EINVAL; + } + + if (copy_from_user(&p_data, arg, + sizeof(p_data))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "p_data", sizeof(p_data)); + return -EFAULT; + } + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS", + p_data.num_params); + return -EINVAL; + } + + p_size = p_data.num_params * + sizeof(struct lsm_params_info); + + if (p_data.data_size != p_size) { + dev_err(rtd->dev, + "%s: %s: Invalid size %zd\n", + __func__, "SET_MODULE_PARAMS", p_size); + + return -EFAULT; + } + + params = kzalloc(p_size, GFP_KERNEL); + if (!params) + return -ENOMEM; + + if (copy_from_user(params, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params", p_data.data_size); + kfree(params); + return -EFAULT; + } + + err = msm_lsm_process_params(substream, &p_data, params); + if (err) + dev_err(rtd->dev, + "%s: %s: Failed to set params, err = %d\n", + __func__, "SET_MODULE_PARAMS", err); + kfree(params); + break; + } + + case SNDRV_LSM_EVENT_STATUS: { + struct snd_lsm_event_status *user = NULL, userarg; + + dev_dbg(rtd->dev, + "%s: SNDRV_LSM_EVENT_STATUS\n", __func__); + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid params event status\n", + __func__); + return -EINVAL; + } + if (copy_from_user(&userarg, arg, sizeof(userarg))) { + dev_err(rtd->dev, + "%s: err copyuser event_status\n", + __func__); + return -EFAULT; + } + + if (userarg.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + pr_err("%s: payload_size %d is invalid, max allowed = %d\n", + __func__, userarg.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + return -EINVAL; + } + + size = sizeof(struct snd_lsm_event_status) + + userarg.payload_size; + user = kmalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + return -EFAULT; + } + user->payload_size = userarg.payload_size; + err = msm_lsm_ioctl_shared(substream, cmd, user); + + /* Update size with actual payload size */ + size = sizeof(*user) + user->payload_size; + if (!err && !access_ok(VERIFY_WRITE, arg, size)) { + dev_err(rtd->dev, + "%s: write verify failed size %d\n", + __func__, size); + err = -EFAULT; + } + if (!err && (copy_to_user(arg, user, size))) { + dev_err(rtd->dev, + "%s: failed to copy payload %d", + __func__, size); + err = -EFAULT; + } + kfree(user); + if (err) + dev_err(rtd->dev, + "%s: lsmevent failed %d", __func__, err); + return err; + } + default: + err = msm_lsm_ioctl_shared(substream, cmd, arg); + break; + } + return err; +} + +static int msm_lsm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd; + int ret = 0; + + pr_debug("%s\n", __func__); + prtd = kzalloc(sizeof(struct lsm_priv), GFP_KERNEL); + if (!prtd) { + pr_err("%s: Failed to allocate memory for lsm_priv\n", + __func__); + return -ENOMEM; + } + spin_lock_init(&prtd->event_lock); + init_waitqueue_head(&prtd->event_wait); + init_waitqueue_head(&prtd->period_wait); + prtd->substream = substream; + runtime->private_data = prtd; + runtime->hw = msm_pcm_hardware_capture; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("%s: snd_pcm_hw_constraint_list failed ret %d\n", + __func__, ret); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("%s: snd_pcm_hw_constraint_integer failed ret %d\n", + __func__, ret); + + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE, + CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE); + if (ret < 0) + pr_info("%s: constraint for buffer bytes min max ret = %d\n", + __func__, ret); + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret < 0) { + pr_info("%s: constraint for period bytes step ret = %d\n", + __func__, ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret < 0) + pr_info("%s: constraint for buffer bytes step ret = %d\n", + __func__, ret); + prtd->lsm_client = q6lsm_client_alloc( + (lsm_app_cb)lsm_event_handler, prtd); + if (!prtd->lsm_client) { + pr_err("%s: Could not allocate memory\n", __func__); + kfree(prtd); + runtime->private_data = NULL; + return -ENOMEM; + } + prtd->lsm_client->opened = false; + return 0; +} + +static int msm_lsm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + + rtd = prtd->substream->private_data; + + if (!prtd->lsm_client) { + dev_err(rtd->dev, + "%s: LSM client data ptr is NULL\n", __func__); + return -EINVAL; + } + prtd->lsm_client->started = false; + runtime->private_data = prtd; + return 0; +} + +static int msm_lsm_close(struct snd_pcm_substream *substream) +{ + unsigned long flags; + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + if (!prtd || !prtd->lsm_client) { + pr_err("%s: No LSM session active\n", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + dev_dbg(rtd->dev, "%s\n", __func__); + if (prtd->lsm_client->started) { + ret = q6lsm_stop(prtd->lsm_client, true); + if (ret) + dev_err(rtd->dev, + "%s: session stop failed, err = %d\n", + __func__, ret); + else + dev_dbg(rtd->dev, + "%s: LSM client session stopped %d\n", + __func__, ret); + + /* + * Go Ahead and try de-register sound model, + * even if stop failed + */ + prtd->lsm_client->started = false; + + ret = q6lsm_deregister_sound_model(prtd->lsm_client); + if (ret) + dev_err(rtd->dev, + "%s: dereg_snd_model failed, err = %d\n", + __func__, ret); + else + dev_dbg(rtd->dev, "%s: dereg_snd_model successful\n", + __func__); + } + + if (prtd->lsm_client->opened) { + q6lsm_close(prtd->lsm_client); + prtd->lsm_client->opened = false; + } + q6lsm_client_free(prtd->lsm_client); + + spin_lock_irqsave(&prtd->event_lock, flags); + kfree(prtd->event_status); + prtd->event_status = NULL; + spin_unlock_irqrestore(&prtd->event_lock, flags); + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_lsm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct lsm_lab_hw_params *hw_params = NULL; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + if (!prtd || !params) { + dev_err(rtd->dev, + "%s: invalid params prtd %pK params %pK", + __func__, prtd, params); + return -EINVAL; + } + hw_params = &prtd->lsm_client->hw_params; + hw_params->sample_rate = params_rate(params); + hw_params->sample_size = + (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) ? 16 : 0; + hw_params->period_count = params_periods(params); + if (hw_params->sample_rate != 16000 || hw_params->sample_size != 16 || + hw_params->period_count == 0) { + dev_err(rtd->dev, + "%s: Invalid params sample rate %d sample size %d period count %d", + __func__, hw_params->sample_rate, + hw_params->sample_size, + hw_params->period_count); + return -EINVAL; + } + hw_params->buf_sz = params_buffer_bytes(params) / + hw_params->period_count; + dev_dbg(rtd->dev, + "%s: sample rate %d sample size %d buffer size %d period count %d\n", + __func__, hw_params->sample_rate, hw_params->sample_size, + hw_params->buf_sz, hw_params->period_count); + return 0; +} + +static snd_pcm_uframes_t msm_lsm_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + if (!prtd) { + dev_err(rtd->dev, + "%s: Invalid param %pK\n", __func__, prtd); + return 0; + } + + if (prtd->dma_write >= snd_pcm_lib_buffer_bytes(substream)) + prtd->dma_write = 0; + dev_dbg(rtd->dev, + "%s: dma post = %d\n", __func__, prtd->dma_write); + return bytes_to_frames(runtime, prtd->dma_write); +} + +static int msm_lsm_pcm_copy(struct snd_pcm_substream *substream, int ch, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + char *pcm_buf = NULL; + int fbytes = 0, rc = 0; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + if (!prtd) { + dev_err(rtd->dev, + "%s: Invalid param %pK\n", __func__, prtd); + return -EINVAL; + } + + fbytes = frames_to_bytes(runtime, frames); + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + dev_err(rtd->dev, + "%s: runtime state incorrect %d", __func__, + runtime->status->state); + return 0; + } + rc = wait_event_timeout(prtd->period_wait, + (atomic_read(&prtd->buf_count) | + atomic_read(&prtd->read_abort)), (2 * HZ)); + if (!rc) { + dev_err(rtd->dev, + "%s: timeout for read retry\n", __func__); + return -EAGAIN; + } + if (atomic_read(&prtd->read_abort)) { + dev_err(rtd->dev, + "%s: Read abort received\n", __func__); + return -EIO; + } + prtd->appl_cnt = prtd->appl_cnt % + prtd->lsm_client->hw_params.period_count; + pcm_buf = prtd->lsm_client->lab_buffer[prtd->appl_cnt].data; + dev_dbg(rtd->dev, + "%s: copy the pcm data size %d\n", + __func__, fbytes); + if (pcm_buf) { + if (copy_to_user(buf, pcm_buf, fbytes)) { + dev_err(rtd->dev, + "%s: failed to copy bytes %d\n", + __func__, fbytes); + return -EINVAL; + } + } else { + dev_err(rtd->dev, + "%s: Invalid pcm buffer\n", __func__); + return -EINVAL; + } + prtd->appl_cnt = (prtd->appl_cnt + 1) % + prtd->lsm_client->hw_params.period_count; + atomic_dec(&prtd->buf_count); + return 0; +} + +static struct snd_pcm_ops msm_lsm_ops = { + .open = msm_lsm_open, + .close = msm_lsm_close, + .ioctl = msm_lsm_ioctl, + .prepare = msm_lsm_prepare, + .compat_ioctl = msm_lsm_ioctl_compat, + .hw_params = msm_lsm_hw_params, + .copy = msm_lsm_pcm_copy, + .pointer = msm_lsm_pcm_pointer, +}; + +static int msm_asoc_lsm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + return 0; +} + +static int msm_asoc_lsm_probe(struct snd_soc_platform *platform) +{ + pr_debug("enter %s\n", __func__); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_lsm_ops, + .pcm_new = msm_asoc_lsm_new, + .probe = msm_asoc_lsm_probe, +}; + +static int msm_lsm_probe(struct platform_device *pdev) +{ + + return snd_soc_register_platform(&pdev->dev, &msm_soc_platform); +} + +static int msm_lsm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + + return 0; +} + +static const struct of_device_id msm_lsm_client_dt_match[] = { + {.compatible = "qcom,msm-lsm-client" }, + { } +}; + +static struct platform_driver msm_lsm_driver = { + .driver = { + .name = "msm-lsm-client", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(msm_lsm_client_dt_match), + }, + .probe = msm_lsm_probe, + .remove = msm_lsm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + return platform_driver_register(&msm_lsm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_lsm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("LSM client platform driver"); +MODULE_DEVICE_TABLE(of, msm_lsm_client_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c new file mode 100644 index 000000000000..4ea8abb82590 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c @@ -0,0 +1,921 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-afe-v2.h" + +#define MIN_PLAYBACK_PERIOD_SIZE (128 * 2) +#define MAX_PLAYBACK_PERIOD_SIZE (128 * 2 * 2 * 6) +#define MIN_PLAYBACK_NUM_PERIODS (4) +#define MAX_PLAYBACK_NUM_PERIODS (384) + +#define MIN_CAPTURE_PERIOD_SIZE (128 * 2) +#define MAX_CAPTURE_PERIOD_SIZE (192 * 2 * 2 * 8 * 4) +#define MIN_CAPTURE_NUM_PERIODS (4) +#define MAX_CAPTURE_NUM_PERIODS (384) + +static struct snd_pcm_hardware msm_afe_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE| + SNDRV_PCM_FMTBIT_S24_LE, + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 6, + .buffer_bytes_max = MAX_PLAYBACK_PERIOD_SIZE * + MAX_PLAYBACK_NUM_PERIODS, + .period_bytes_min = MIN_PLAYBACK_PERIOD_SIZE, + .period_bytes_max = MAX_PLAYBACK_PERIOD_SIZE, + .periods_min = MIN_PLAYBACK_NUM_PERIODS, + .periods_max = MAX_PLAYBACK_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_afe_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE| + SNDRV_PCM_FMTBIT_S24_LE, + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 6, + .buffer_bytes_max = MAX_CAPTURE_PERIOD_SIZE * + MAX_CAPTURE_NUM_PERIODS, + .period_bytes_min = MIN_CAPTURE_PERIOD_SIZE, + .period_bytes_max = MAX_CAPTURE_PERIOD_SIZE, + .periods_min = MIN_CAPTURE_NUM_PERIODS, + .periods_max = MAX_CAPTURE_NUM_PERIODS, + .fifo_size = 0, +}; + + +static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt); +static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt); + +static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt) +{ + struct pcm_afe_info *prtd = + container_of(hrt, struct pcm_afe_info, hrt); + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + u32 mem_map_handle = 0; + + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) + pr_err("%s: mem_map_handle is NULL\n", __func__); + + if (prtd->start) { + pr_debug("sending frame to DSP: poll_time: %d\n", + prtd->poll_time); + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle); + afe_rt_proxy_port_write( + (prtd->dma_addr + + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + prtd->dsp_cnt++; + hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time + * 1000)); + + return HRTIMER_RESTART; + } else + return HRTIMER_NORESTART; +} +static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt) +{ + struct pcm_afe_info *prtd = + container_of(hrt, struct pcm_afe_info, hrt); + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + u32 mem_map_handle = 0; + int ret; + + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) + pr_err("%s: mem_map_handle is NULL\n", __func__); + + if (prtd->start) { + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle); + ret = afe_rt_proxy_port_read( + (prtd->dma_addr + (prtd->dsp_cnt + * snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + if (ret < 0) { + pr_err("%s: AFE port read fails: %d\n", __func__, ret); + prtd->start = 0; + return HRTIMER_NORESTART; + } + prtd->dsp_cnt++; + pr_debug("sending frame rec to DSP: poll_time: %d\n", + prtd->poll_time); + hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time + * 1000)); + + return HRTIMER_RESTART; + } else + return HRTIMER_NORESTART; +} +static void pcm_afe_process_tx_pkt(uint32_t opcode, + uint32_t token, uint32_t *payload, + void *priv) +{ + struct pcm_afe_info *prtd = priv; + unsigned long dsp_flags; + struct snd_pcm_substream *substream = NULL; + struct snd_pcm_runtime *runtime = NULL; + uint16_t event; + uint64_t period_bytes; + uint64_t bytes_one_sec; + + if (prtd == NULL) + return; + substream = prtd->substream; + runtime = substream->runtime; + pr_debug("%s\n", __func__); + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + switch (opcode) { + case AFE_EVENT_RT_PROXY_PORT_STATUS: { + event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10); + switch (event) { + case AFE_EVENT_RTPORT_START: { + prtd->dsp_cnt = 0; + /* Calculate poll time. + * Split steps to avoid overflow. + * Poll time-time corresponding to one period + * in bytes. + * (Samplerate * channelcount * format) = + * bytes in 1 sec. + * Poll time = + * (period bytes / bytes in one sec) * + * 1000000 micro seconds. + * Multiplication by 1000000 is done in two + * steps to keep the accuracy of poll time. + */ + if (prtd->mmap_flag) { + period_bytes = ((uint64_t)( + (snd_pcm_lib_period_bytes( + prtd->substream)) * + 1000)); + bytes_one_sec = (runtime->rate + * runtime->channels * 2); + bytes_one_sec = + div_u64(bytes_one_sec, 1000); + prtd->poll_time = + div_u64(period_bytes, + bytes_one_sec); + pr_debug("prtd->poll_time: %d", + prtd->poll_time); + } + break; + } + case AFE_EVENT_RTPORT_STOP: + pr_debug("%s: event!=0\n", __func__); + prtd->start = 0; + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case AFE_EVENT_RTPORT_LOW_WM: + pr_debug("%s: Underrun\n", __func__); + break; + case AFE_EVENT_RTPORT_HI_WM: + pr_debug("%s: Overrun\n", __func__); + break; + default: + break; + } + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2: + pr_debug("write done\n"); + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + break; + } + case RESET_EVENTS: + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + prtd->reset_event = true; + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); +} + +static void pcm_afe_process_rx_pkt(uint32_t opcode, + uint32_t token, uint32_t *payload, + void *priv) +{ + struct pcm_afe_info *prtd = priv; + unsigned long dsp_flags; + struct snd_pcm_substream *substream = NULL; + struct snd_pcm_runtime *runtime = NULL; + uint16_t event; + uint64_t period_bytes; + uint64_t bytes_one_sec; + uint32_t mem_map_handle = 0; + + if (prtd == NULL) + return; + substream = prtd->substream; + runtime = substream->runtime; + pr_debug("%s\n", __func__); + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + switch (opcode) { + case AFE_EVENT_RT_PROXY_PORT_STATUS: { + event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10); + switch (event) { + case AFE_EVENT_RTPORT_START: { + prtd->dsp_cnt = 0; + /* Calculate poll time. Split steps to avoid overflow. + * Poll time-time corresponding to one period in bytes. + * (Samplerate * channelcount * format)=bytes in 1 sec. + * Poll time = (period bytes / bytes in one sec) * + * 1000000 micro seconds. + * Multiplication by 1000000 is done in two steps to + * keep the accuracy of poll time. + */ + if (prtd->mmap_flag) { + period_bytes = ((uint64_t)( + (snd_pcm_lib_period_bytes( + prtd->substream)) * 1000)); + bytes_one_sec = (runtime->rate * + runtime->channels * 2); + bytes_one_sec = div_u64(bytes_one_sec, 1000); + prtd->poll_time = + div_u64(period_bytes, bytes_one_sec); + pr_debug("prtd->poll_time : %d\n", + prtd->poll_time); + } else { + mem_map_handle = + afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) + pr_err("%s:mem_map_handle is NULL\n", + __func__); + /* Do initial read to start transfer */ + afe_rt_proxy_port_read((prtd->dma_addr + + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes( + prtd->substream))), + mem_map_handle, + snd_pcm_lib_period_bytes( + prtd->substream)); + prtd->dsp_cnt++; + } + break; + } + case AFE_EVENT_RTPORT_STOP: + pr_debug("%s: event!=0\n", __func__); + prtd->start = 0; + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case AFE_EVENT_RTPORT_LOW_WM: + pr_debug("%s: Underrun\n", __func__); + break; + case AFE_EVENT_RTPORT_HI_WM: + pr_debug("%s: Overrun\n", __func__); + break; + default: + break; + } + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2: + pr_debug("%s :Read done\n", __func__); + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + if (!prtd->mmap_flag) { + atomic_set(&prtd->rec_bytes_avail, 1); + wake_up(&prtd->read_wait); + } + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + break; + } + case RESET_EVENTS: + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + prtd->reset_event = true; + if (!prtd->mmap_flag) { + atomic_set(&prtd->rec_bytes_avail, 1); + wake_up(&prtd->read_wait); + } + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); +} + +static int msm_afe_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + int ret = 0; + + pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate); + + pr_debug("%s: dai->id =%x\n", __func__, dai->id); + ret = afe_register_get_events(dai->id, + pcm_afe_process_tx_pkt, prtd); + if (ret < 0) { + pr_err("afe-pcm:register for events failed\n"); + return ret; + } + pr_debug("%s:success\n", __func__); + prtd->prepared++; + return ret; +} + +static int msm_afe_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + int ret = 0; + + pr_debug("%s\n", __func__); + + pr_debug("%s: dai->id =%x\n", __func__, dai->id); + ret = afe_register_get_events(dai->id, + pcm_afe_process_rx_pkt, prtd); + if (ret < 0) { + pr_err("afe-pcm:register for events failed\n"); + return ret; + } + pr_debug("%s:success\n", __func__); + prtd->prepared++; + return 0; +} + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 16000, 48000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int msm_afe_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = NULL; + int ret = 0; + + prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + pr_debug("prtd %pK\n", prtd); + + mutex_init(&prtd->lock); + spin_lock_init(&prtd->dsp_lock); + prtd->dsp_cnt = 0; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = msm_afe_hardware_playback; + else + runtime->hw = msm_afe_hardware_capture; + + prtd->substream = substream; + runtime->private_data = prtd; + prtd->audio_client = q6afe_audio_client_alloc(prtd); + if (!prtd->audio_client) { + pr_debug("%s: Could not allocate memory\n", __func__); + mutex_unlock(&prtd->lock); + kfree(prtd); + return -ENOMEM; + } + + atomic_set(&prtd->rec_bytes_avail, 0); + init_waitqueue_head(&prtd->read_wait); + + hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->hrt.function = afe_hrtimer_callback; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->hrt.function = afe_hrtimer_rec_callback; + + mutex_unlock(&prtd->lock); + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_err("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_err("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + MIN_CAPTURE_NUM_PERIODS * MIN_CAPTURE_PERIOD_SIZE, + MAX_CAPTURE_NUM_PERIODS * MAX_CAPTURE_PERIOD_SIZE); + + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + + prtd->reset_event = false; + return 0; +} + +static int msm_afe_playback_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t hwoff, + void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + u32 mem_map_handle = 0; + + pr_debug("%s : appl_ptr 0x%lx hw_ptr 0x%lx dest_to_copy 0x%pK\n", + __func__, + runtime->control->appl_ptr, runtime->status->hw_ptr, hwbuf); + + if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) { + pr_err("%s :Failed to copy audio from user buffer\n", + __func__); + + ret = -EFAULT; + goto fail; + } + + if (!prtd->mmap_flag) { + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) { + pr_err("%s: mem_map_handle is NULL\n", __func__); + ret = -EFAULT; + goto fail; + } + + pr_debug("%s : prtd-> dma_addr 0x%lx dsp_cnt %d\n", __func__, + prtd->dma_addr, prtd->dsp_cnt); + + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + + ret = afe_rt_proxy_port_write( + (prtd->dma_addr + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes(prtd->substream))), + mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + + if (ret) { + pr_err("%s: AFE proxy port write failed %d\n", + __func__, ret); + goto fail; + } + prtd->dsp_cnt++; + } +fail: + return ret; +} + +static int msm_afe_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t hwoff, + void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + u32 mem_map_handle = 0; + + if (!prtd->mmap_flag) { + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + + if (!mem_map_handle) { + pr_err("%s: mem_map_handle is NULL\n", __func__); + ret = -EFAULT; + goto fail; + } + + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + + ret = afe_rt_proxy_port_read((prtd->dma_addr + + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes(prtd->substream))), + mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + + if (ret) { + pr_err("%s: AFE proxy port read failed %d\n", + __func__, ret); + goto fail; + } + + prtd->dsp_cnt++; + ret = wait_event_timeout(prtd->read_wait, + atomic_read(&prtd->rec_bytes_avail), 5 * HZ); + if (ret < 0) { + pr_err("%s: wait_event_timeout failed\n", __func__); + + ret = -ETIMEDOUT; + goto fail; + } + atomic_set(&prtd->rec_bytes_avail, 0); + } + pr_debug("%s:appl_ptr 0x%lx hw_ptr 0x%lx src_to_copy 0x%pK\n", + __func__, runtime->control->appl_ptr, + runtime->status->hw_ptr, hwbuf); + + if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) { + pr_err("%s: copy to user failed\n", __func__); + + goto fail; + ret = -EFAULT; + } + +fail: + return ret; +} + +static int msm_afe_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t hwoff, void __user *buf, + snd_pcm_uframes_t frames) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + int ret = 0; + + if (prtd->reset_event) { + pr_debug("%s: reset events received from ADSP, return error\n", + __func__); + return -ENETRESET; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_afe_playback_copy(substream, channel, hwoff, + buf, frames); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_afe_capture_copy(substream, channel, hwoff, + buf, frames); + return ret; +} + +static int msm_afe_close(struct snd_pcm_substream *substream) +{ + int rc = 0; + struct snd_dma_buffer *dma_buf; + struct snd_pcm_runtime *runtime; + struct pcm_afe_info *prtd; + struct snd_soc_pcm_runtime *rtd = NULL; + struct snd_soc_dai *dai = NULL; + int dir = IN; + int ret = 0; + + pr_debug("%s\n", __func__); + if (substream == NULL) { + pr_err("substream is NULL\n"); + return -EINVAL; + } + rtd = substream->private_data; + dai = rtd->cpu_dai; + runtime = substream->runtime; + prtd = runtime->private_data; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dir = IN; + ret = afe_unregister_get_events(dai->id); + if (ret < 0) + pr_err("AFE unregister for events failed\n"); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dir = OUT; + ret = afe_unregister_get_events(dai->id); + if (ret < 0) + pr_err("AFE unregister for events failed\n"); + } + if (prtd->mmap_flag) + hrtimer_cancel(&prtd->hrt); + + rc = afe_cmd_memory_unmap(afe_req_mmap_handle(prtd->audio_client)); + if (rc < 0) + pr_err("AFE memory unmap failed\n"); + + pr_debug("release all buffer\n"); + dma_buf = &substream->dma_buffer; + if (dma_buf == NULL) { + pr_debug("dma_buf is NULL\n"); + goto done; + } + + if (dma_buf->area) + dma_buf->area = NULL; + q6afe_audio_client_buf_free_contiguous(dir, prtd->audio_client); +done: + pr_debug("%s: dai->id =%x\n", __func__, dai->id); + q6afe_audio_client_free(prtd->audio_client); + mutex_unlock(&prtd->lock); + prtd->prepared--; + kfree(prtd); + return 0; +} +static int msm_afe_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + prtd->pcm_irq_pos = 0; + if (prtd->prepared) + return 0; + mutex_lock(&prtd->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_afe_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_afe_capture_prepare(substream); + mutex_unlock(&prtd->lock); + return ret; +} +static int msm_afe_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + struct afe_audio_client *ac = prtd->audio_client; + struct afe_audio_port_data *apd = ac->port; + struct afe_audio_buffer *ab; + int dir = -1; + + pr_debug("%s\n", __func__); + prtd->mmap_flag = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ab = &(apd[dir].buf[0]); + + return msm_audio_ion_mmap((struct audio_buffer *)ab, vma); +} +static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__); + prtd->start = 1; + if (prtd->mmap_flag) + hrtimer_start(&prtd->hrt, ns_to_ktime(0), + HRTIMER_MODE_REL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); + prtd->start = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +static int msm_afe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct pcm_afe_info *prtd = runtime->private_data; + struct afe_audio_buffer *buf; + int dir, rc; + + pr_debug("%s:\n", __func__); + + mutex_lock(&prtd->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + + rc = q6afe_audio_client_buf_alloc_contiguous(dir, + prtd->audio_client, + (params_buffer_bytes(params) / params_periods(params)), + params_periods(params)); + pr_debug("params_buffer_bytes(params) = %d\n", + (params_buffer_bytes(params))); + pr_debug("params_periods(params) = %d\n", + (params_periods(params))); + pr_debug("params_periodsize(params) = %d\n", + (params_buffer_bytes(params) / params_periods(params))); + + if (rc < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", rc); + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + buf = prtd->audio_client->port[dir].buf; + + if (buf == NULL || buf[0].data == NULL) { + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + + pr_debug("%s:buf = %pK\n", __func__, buf); + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf[0].data; + dma_buf->addr = buf[0].phys; + + dma_buf->bytes = params_buffer_bytes(params); + + if (!dma_buf->area) { + pr_err("%s:MSM AFE physical memory allocation failed\n", + __func__); + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + + memset(dma_buf->area, 0, params_buffer_bytes(params)); + + prtd->dma_addr = (phys_addr_t) dma_buf->addr; + + mutex_unlock(&prtd->lock); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + rc = afe_memory_map(dma_buf->addr, dma_buf->bytes, prtd->audio_client); + if (rc < 0) + pr_err("fail to map memory to DSP\n"); + + return rc; +} +static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream)) + prtd->pcm_irq_pos = 0; + + if (prtd->reset_event) { + pr_debug("%s: reset events received from ADSP, return XRUN\n", + __func__); + return SNDRV_PCM_POS_XRUN; + } + + pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos); + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static struct snd_pcm_ops msm_afe_ops = { + .open = msm_afe_open, + .copy = msm_afe_copy, + .hw_params = msm_afe_hw_params, + .trigger = msm_afe_trigger, + .close = msm_afe_close, + .prepare = msm_afe_prepare, + .mmap = msm_afe_mmap, + .pointer = msm_afe_pointer, +}; + + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + pr_debug("%s\n", __func__); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static int msm_afe_afe_probe(struct snd_soc_platform *platform) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_afe_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_afe_afe_probe, +}; + +static int msm_afe_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_afe_remove(struct platform_device *pdev) +{ + pr_debug("%s\n", __func__); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} +static const struct of_device_id msm_pcm_afe_dt_match[] = { + {.compatible = "qcom,msm-pcm-afe"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_afe_dt_match); + +static struct platform_driver msm_afe_driver = { + .driver = { + .name = "msm-pcm-afe", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_afe_dt_match, + }, + .probe = msm_afe_probe, + .remove = msm_afe_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&msm_afe_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&msm_afe_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("AFE PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h new file mode 100644 index 000000000000..84a4c3c6c275 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2012,2015-2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MSM_PCM_AFE_H +#define _MSM_PCM_AFE_H +#include +#include + + +struct pcm_afe_info { + unsigned long dma_addr; + struct snd_pcm_substream *substream; + unsigned int pcm_irq_pos; /* IRQ position */ + struct mutex lock; + spinlock_t dsp_lock; + uint32_t samp_rate; + uint32_t channel_mode; + uint8_t start; + uint32_t dsp_cnt; + uint32_t buf_phys; + int32_t mmap_flag; + int prepared; + struct hrtimer hrt; + int poll_time; + struct afe_audio_client *audio_client; + wait_queue_head_t read_wait; + atomic_t rec_bytes_avail; + bool reset_event; +}; + + +#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \ + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, \ + .info = fp_info,\ + .get = fp_get, .put = fp_put, \ + .private_value = addr, \ + } + +#endif /*_MSM_PCM_AFE_H*/ diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c new file mode 100644 index 000000000000..000ad0ee1c9d --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c @@ -0,0 +1,596 @@ +/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" +#include "q6voice.h" + +enum { + DTMF_IN_RX, + DTMF_IN_TX, +}; + +enum format { + FORMAT_S16_LE = 2 +}; + +struct dtmf_det_info { + char session[MAX_SESSION_NAME_LEN]; + uint8_t dir; + uint16_t high_freq; + uint16_t low_freq; +}; + +struct dtmf_buf_node { + struct list_head list; + struct dtmf_det_info dtmf_det_pkt; +}; + +enum dtmf_state { + DTMF_GEN_RX_STOPPED, + DTMF_GEN_RX_STARTED, +}; + +#define DTMF_MAX_Q_LEN 10 +#define DTMF_PKT_SIZE sizeof(struct dtmf_det_info) + +struct dtmf_drv_info { + enum dtmf_state state; + struct snd_pcm_substream *capture_substream; + + struct list_head out_queue; + struct list_head free_out_queue; + + wait_queue_head_t out_wait; + + struct mutex lock; + spinlock_t dsp_lock; + + uint8_t capture_start; + uint8_t capture_instance; + + unsigned int pcm_capture_size; + unsigned int pcm_capture_count; + unsigned int pcm_capture_irq_pos; + unsigned int pcm_capture_buf_pos; +}; + +static struct snd_pcm_hardware msm_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN), + .period_bytes_min = DTMF_PKT_SIZE, + .period_bytes_max = DTMF_PKT_SIZE, + .periods_min = DTMF_MAX_Q_LEN, + .periods_max = DTMF_MAX_Q_LEN, + .fifo_size = 0, +}; + +static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint16_t low_freq = ucontrol->value.integer.value[0]; + uint16_t high_freq = ucontrol->value.integer.value[1]; + int64_t duration = ucontrol->value.integer.value[2]; + uint16_t gain = ucontrol->value.integer.value[3]; + + pr_debug("%s: low_freq=%d high_freq=%d duration=%d gain=%d\n", + __func__, low_freq, high_freq, (int)duration, gain); + afe_dtmf_generate_rx(duration, high_freq, low_freq, gain); + return 0; +} + +static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s:\n", __func__); + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: enable=%d\n", __func__, enable); + voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME), + enable); + + return 0; +} + +static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: enable=%d\n", __func__, enable); + voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME), + enable); + + return 0; +} + +static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static struct snd_kcontrol_new msm_dtmf_controls[] = { + SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain", + SND_SOC_NOPM, 0, 5000, 0, 4, + msm_dtmf_rx_generate_get, + msm_dtmf_rx_generate_put), + SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0, + msm_dtmf_detect_voice_rx_get, + msm_dtmf_detect_voice_rx_put), + SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0, + msm_dtmf_detect_volte_rx_get, + msm_dtmf_detect_volte_rx_put), +}; + +static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_dtmf_controls, + ARRAY_SIZE(msm_dtmf_controls)); + return 0; +} + +static void dtmf_rx_detected_cb(uint8_t *pkt, + char *session, + void *private_data) +{ + struct dtmf_buf_node *buf_node = NULL; + struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt = + (struct vss_istream_evt_rx_dtmf_detected *)pkt; + struct dtmf_drv_info *prtd = private_data; + unsigned long dsp_flags; + + pr_debug("%s\n", __func__); + if (prtd->capture_substream == NULL) + return; + + /* Copy dtmf detected info into out_queue. */ + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + /* discarding dtmf detection info till start is received */ + if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) { + buf_node = list_first_entry(&prtd->free_out_queue, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq; + buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq; + if (session != NULL) + strlcpy(buf_node->dtmf_det_pkt.session, + session, MAX_SESSION_NAME_LEN); + + buf_node->dtmf_det_pkt.dir = DTMF_IN_RX; + pr_debug("high =%d, low=%d session=%s\n", + buf_node->dtmf_det_pkt.high_freq, + buf_node->dtmf_det_pkt.low_freq, + buf_node->dtmf_det_pkt.session); + list_add_tail(&buf_node->list, &prtd->out_queue); + prtd->pcm_capture_irq_pos += prtd->pcm_capture_count; + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(prtd->capture_substream); + } else { + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + pr_err("DTMF detection pkt in Rx dropped, no free node available\n"); + } + + wake_up(&prtd->out_wait); +} + +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t hwoff, + void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + int count = 0; + struct dtmf_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + + count = frames_to_bytes(runtime, frames); + + ret = wait_event_interruptible_timeout(prtd->out_wait, + (!list_empty(&prtd->out_queue)), + 1 * HZ); + + if (ret > 0) { + if (count <= DTMF_PKT_SIZE) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + buf_node = list_first_entry(&prtd->out_queue, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + ret = copy_to_user(buf, + &buf_node->dtmf_det_pkt, + count); + if (ret) { + pr_err("%s: Copy to user returned %d\n", + __func__, ret); + ret = -EFAULT; + } + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, + &prtd->free_out_queue); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + + } else { + pr_err("%s: Read count %d > DTMF_PKT_SIZE\n", + __func__, count); + ret = -ENOMEM; + } + } else if (ret == 0) { + pr_err("%s: No UL data available\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + ret = -ERESTARTSYS; + } + return ret; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + + pr_debug("%s() DTMF\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames); + + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = NULL; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL); + + if (prtd == NULL) { + ret = -ENOMEM; + goto done; + } + + mutex_init(&prtd->lock); + spin_lock_init(&prtd->dsp_lock); + init_waitqueue_head(&prtd->out_wait); + INIT_LIST_HEAD(&prtd->out_queue); + INIT_LIST_HEAD(&prtd->free_out_queue); + + runtime->hw = msm_pcm_hardware; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + prtd->capture_substream = substream; + prtd->capture_instance++; + runtime->private_data = prtd; + } + +done: + return ret; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct dtmf_buf_node *buf_node = NULL; + struct snd_dma_buffer *c_dma_buf; + struct snd_pcm_substream *c_substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + + pr_debug("%s() DTMF\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mutex_lock(&prtd->lock); + wake_up(&prtd->out_wait); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_instance--; + + if (!prtd->capture_instance) { + if (prtd->state == DTMF_GEN_RX_STARTED) { + prtd->state = DTMF_GEN_RX_STOPPED; + voc_disable_dtmf_det_on_active_sessions(); + voc_register_dtmf_rx_detection_cb(NULL, NULL); + } + /* release all buffer */ + /* release out_queue and free_out_queue */ + pr_debug("release all buffer\n"); + c_substream = prtd->capture_substream; + if (c_substream == NULL) { + pr_debug("c_substream is NULL\n"); + mutex_unlock(&prtd->lock); + return -EINVAL; + } + + c_dma_buf = &c_substream->dma_buffer; + if (c_dma_buf == NULL) { + pr_debug("c_dma_buf is NULL.\n"); + mutex_unlock(&prtd->lock); + return -EINVAL; + } + + if (c_dma_buf->area != NULL) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_for_each_safe(ptr, next, + &prtd->out_queue) { + buf_node = list_entry(ptr, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + } + + list_for_each_safe(ptr, next, + &prtd->free_out_queue) { + buf_node = list_entry(ptr, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + } + + spin_unlock_irqrestore(&prtd->dsp_lock, + dsp_flags); + dma_free_coherent(c_substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + c_dma_buf->area, + c_dma_buf->addr); + c_dma_buf->area = NULL; + } + } + prtd->capture_substream = NULL; + mutex_unlock(&prtd->lock); + } + + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct dtmf_buf_node *buf_node = NULL; + int i = 0, offset = 0; + int ret = 0; + + pr_debug("%s: DTMF\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mutex_lock(&prtd->lock); + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + + dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + &dma_buf->addr, GFP_KERNEL); + if (!dma_buf->area) { + pr_err("%s:MSM DTMF dma_alloc failed\n", __func__); + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + + dma_buf->bytes = runtime->hw.buffer_bytes_max; + memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max); + + for (i = 0; i < DTMF_MAX_Q_LEN; i++) { + pr_debug("node =%d\n", i); + buf_node = (void *) dma_buf->area + offset; + list_add_tail(&buf_node->list, + &prtd->free_out_queue); + offset = offset + sizeof(struct dtmf_buf_node); + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + mutex_unlock(&prtd->lock); + } + + return ret; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + pr_debug("%s: DTMF\n", __func__); + prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_capture_irq_pos = 0; + prtd->pcm_capture_buf_pos = 0; + return 0; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + pr_debug("%s: DTMF\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mutex_lock(&prtd->lock); + + msm_pcm_capture_prepare(substream); + + if (runtime->format != FORMAT_S16_LE) { + pr_err("format:%u doesn't match %d\n", + (uint32_t)runtime->format, FORMAT_S16_LE); + mutex_unlock(&prtd->lock); + return -EINVAL; + } + + if (prtd->capture_instance && + (prtd->state != DTMF_GEN_RX_STARTED)) { + voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb, + prtd); + prtd->state = DTMF_GEN_RX_STARTED; + } + mutex_unlock(&prtd->lock); + } + + return 0; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + pr_debug("%s: Trigger start\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_start = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_start = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size) + prtd->pcm_capture_irq_pos = 0; + ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos)); + } + + return ret; +} + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .copy = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_dtmf_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_dtmf_dt_match[] = { + {.compatible = "qcom,msm-pcm-dtmf"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_pcm_dtmf_dt_match); + + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-dtmf", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_dtmf_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + return platform_driver_register(&msm_pcm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("DTMF platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c new file mode 100644 index 000000000000..720c46a03bd5 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c @@ -0,0 +1,1553 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6voice.h" + +#define HPCM_MAX_Q_LEN 2 +#define HPCM_MIN_VOC_PKT_SIZE 320 +#define HPCM_MAX_VOC_PKT_SIZE 640 +#define VHPCM_BLOCK_SIZE 4096 +#define CACHE_ALIGNMENT_SIZE 128 +#define CACHE_ALIGNMENT_MASK 0xFFFFFF80 + +#define VOICE_TX_CAPTURE_DAI_ID "CS-VOICE HOST TX CAPTURE" +#define VOICE_TX_PLAYBACK_DAI_ID "CS-VOICE HOST TX PLAYBACK" +#define VOICE_RX_CAPTURE_DAI_ID "CS-VOICE HOST RX CAPTURE" +#define VOICE_RX_PLAYBACK_DAI_ID "CS-VOICE HOST RX PLAYBACK" + +#define VOLTE_TX_CAPTURE_DAI_ID "VOLTE HOST TX CAPTURE" +#define VOLTE_TX_PLAYBACK_DAI_ID "VOLTE HOST TX PLAYBACK" +#define VOLTE_RX_CAPTURE_DAI_ID "VOLTE HOST RX CAPTURE" +#define VOLTE_RX_PLAYBACK_DAI_ID "VOLTE HOST RX PLAYBACK" + + +#define VoMMode1_TX_CAPTURE_DAI_ID "VoiceMMode1 HOST TX CAPTURE" +#define VoMMode1_TX_PLAYBACK_DAI_ID "VoiceMMode1 HOST TX PLAYBACK" +#define VoMMode1_RX_CAPTURE_DAI_ID "VoiceMMode1 HOST RX CAPTURE" +#define VoMMode1_RX_PLAYBACK_DAI_ID "VoiceMMode1 HOST RX PLAYBACK" + +#define VoMMode2_TX_CAPTURE_DAI_ID "VoiceMMode2 HOST TX CAPTURE" +#define VoMMode2_TX_PLAYBACK_DAI_ID "VoiceMMode2 HOST TX PLAYBACK" +#define VoMMode2_RX_CAPTURE_DAI_ID "VoiceMMode2 HOST RX CAPTURE" +#define VoMMode2_RX_PLAYBACK_DAI_ID "VoiceMMode2 HOST RX PLAYBACK" + +enum { + RX = 1, + TX, +}; + +enum { + VOICE_INDEX = 0, + VOLTE_INDEX, + VOMMODE1_INDEX, + VOMMODE2_INDEX, + MAX_SESSION +}; + +enum hpcm_state { + HPCM_STOPPED = 1, + HPCM_CLOSED, + HPCM_PREPARED, + HPCM_STARTED, +}; + +struct hpcm_frame { + uint32_t len; + uint8_t voc_pkt[HPCM_MAX_VOC_PKT_SIZE]; +}; + +struct hpcm_buf_node { + struct list_head list; + struct hpcm_frame frame; +}; + +struct vocpcm_ion_buffer { + /* Physical address */ + phys_addr_t paddr; + /* Kernel virtual address */ + void *kvaddr; +}; + +struct dai_data { + enum hpcm_state state; + struct snd_pcm_substream *substream; + struct list_head filled_queue; + struct list_head free_queue; + wait_queue_head_t queue_wait; + spinlock_t dsp_lock; + uint32_t pcm_size; + uint32_t pcm_count; + /* IRQ position */ + uint32_t pcm_irq_pos; + /* Position in buffer */ + uint32_t pcm_buf_pos; + struct vocpcm_ion_buffer vocpcm_ion_buffer; +}; + +struct tap_point { + struct dai_data playback_dai_data; + struct dai_data capture_dai_data; +}; + +struct session { + struct tap_point tx_tap_point; + struct tap_point rx_tap_point; + phys_addr_t sess_paddr; + void *sess_kvaddr; + struct ion_handle *ion_handle; + struct mem_map_table tp_mem_table; +}; + +struct tappnt_mxr_data { + bool enable; + uint16_t direction; + uint16_t sample_rate; +}; + +/* Values from mixer ctl are cached in this structure */ +struct mixer_conf { + int8_t sess_indx; + struct tappnt_mxr_data rx; + struct tappnt_mxr_data tx; +}; + +struct start_cmd { + struct vss_ivpcm_tap_point tap_pnt[2]; + uint32_t no_of_tapoints; +}; + +struct hpcm_drv { + struct mutex lock; + struct session session[MAX_SESSION]; + struct mixer_conf mixer_conf; + struct ion_client *ion_client; + struct start_cmd start_cmd; +}; + +static struct hpcm_drv hpcm_drv; + +static struct snd_pcm_hardware msm_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = sizeof(struct hpcm_buf_node) * HPCM_MAX_Q_LEN, + .period_bytes_min = HPCM_MIN_VOC_PKT_SIZE, + .period_bytes_max = HPCM_MAX_VOC_PKT_SIZE, + .periods_min = HPCM_MAX_Q_LEN, + .periods_max = HPCM_MAX_Q_LEN, + .fifo_size = 0, +}; + +static char *hpcm_get_sess_name(int sess_indx) +{ + char *sess_name = NULL; + + if (sess_indx == VOICE_INDEX) + sess_name = VOICE_SESSION_NAME; + else if (sess_indx == VOLTE_INDEX) + sess_name = VOLTE_SESSION_NAME; + else if (sess_indx == VOMMODE1_INDEX) + sess_name = VOICEMMODE1_NAME; + else if (sess_indx == VOMMODE2_INDEX) + sess_name = VOICEMMODE2_NAME; + else + pr_err("%s:, Invalid sess_index\n", __func__); + + return sess_name; +} + +static void hpcm_reset_mixer_config(struct hpcm_drv *prtd) +{ + prtd->mixer_conf.sess_indx = -1; + prtd->mixer_conf.rx.enable = false; + prtd->mixer_conf.rx.direction = -1; + prtd->mixer_conf.rx.sample_rate = 0; + + prtd->mixer_conf.tx.enable = false; + prtd->mixer_conf.tx.direction = -1; + prtd->mixer_conf.tx.sample_rate = 0; +} + +/* Check for valid mixer control values */ +static bool hpcm_is_valid_config(int sess_indx, int tap_point, + uint16_t direction, uint16_t samplerate) +{ + if (sess_indx < VOICE_INDEX || sess_indx > VOMMODE2_INDEX) { + pr_err("%s: invalid sess_indx :%d\n", __func__, sess_indx); + goto error; + } + + if (samplerate != VSS_IVPCM_SAMPLING_RATE_8K && + samplerate != VSS_IVPCM_SAMPLING_RATE_16K) { + pr_err("%s: invalid sample rate :%d\n", __func__, samplerate); + goto error; + } + + if ((tap_point != RX) && (tap_point != TX)) { + pr_err("%s: invalid tappoint :%d\n", __func__, tap_point); + goto error; + } + + if ((direction != VSS_IVPCM_TAP_POINT_DIR_IN) && + (direction != VSS_IVPCM_TAP_POINT_DIR_OUT) && + (direction != VSS_IVPCM_TAP_POINT_DIR_OUT_IN)) { + pr_err("%s: invalid direction :%d\n", __func__, direction); + goto error; + } + + return true; + +error: + return false; +} + + +static struct dai_data *hpcm_get_dai_data(char *pcm_id, struct hpcm_drv *prtd) +{ + struct dai_data *dai_data = NULL; + size_t size = 0; + + if (pcm_id) { + size = strlen(pcm_id); + /* Check for Voice DAI */ + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].rx_tap_point.playback_dai_data; + /* Check for VoLTE DAI */ + } else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].rx_tap_point.playback_dai_data; + /* check for VoiceMMode1 DAI */ + } else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].rx_tap_point.playback_dai_data; + /* check for VOiceMMode2 DAI */ + } else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].rx_tap_point.playback_dai_data; + + } else { + pr_err("%s: Wrong dai id\n", __func__); + } + } + + return dai_data; +} + +static struct tap_point *hpcm_get_tappoint_data(char *pcm_id, + struct hpcm_drv *prtd) +{ + struct tap_point *tp = NULL; + size_t size = 0; + + if (pcm_id) { + size = strlen(pcm_id); + /* Check for Voice DAI */ + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].rx_tap_point; + /* Check for VoLTE DAI */ + } else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].rx_tap_point; + /* check for VoiceMMode1 */ + } else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point; + /* check for VoiceMMode2 */ + } else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point; + } else { + pr_err("%s: wrong dai id\n", __func__); + } + } + + return tp; +} + +static struct tappnt_mxr_data *hpcm_get_tappnt_mixer_data(char *pcm_id, + struct hpcm_drv *prtd) +{ + + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) { + return &prtd->mixer_conf.tx; + } else { + return &prtd->mixer_conf.rx; + } +} + +static int get_tappnt_value(char *pcm_id) +{ + + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) { + return TX; + } else { + return RX; + } +} + +static bool hpcm_all_dais_are_ready(uint16_t direction, struct tap_point *tp, + enum hpcm_state state) +{ + bool dais_started = false; + + /* + * Based on the direction set per tap point in the mixer control, + * all the dais per tap point should meet the required state for the + * commands such as vpcm_map_memory/vpcm_start to be executed. + */ + switch (direction) { + case VSS_IVPCM_TAP_POINT_DIR_OUT_IN: + if ((tp->playback_dai_data.state >= state) && + (tp->capture_dai_data.state >= state)) { + dais_started = true; + } + break; + + case VSS_IVPCM_TAP_POINT_DIR_IN: + if (tp->playback_dai_data.state >= state) + dais_started = true; + break; + + case VSS_IVPCM_TAP_POINT_DIR_OUT: + if (tp->capture_dai_data.state >= state) + dais_started = true; + break; + + default: + pr_err("invalid direction\n"); + } + + return dais_started; +} + +static void hpcm_create_free_queue(struct snd_dma_buffer *dma_buf, + struct dai_data *dai_data) +{ + struct hpcm_buf_node *buf_node = NULL; + int i = 0, offset = 0; + + for (i = 0; i < HPCM_MAX_Q_LEN; i++) { + buf_node = (void *)dma_buf->area + offset; + list_add_tail(&buf_node->list, + &dai_data->free_queue); + offset = offset + sizeof(struct hpcm_buf_node); + } +} + +static void hpcm_free_allocated_mem(struct hpcm_drv *prtd) +{ + phys_addr_t paddr = 0; + struct tap_point *txtp = NULL; + struct tap_point *rxtp = NULL; + struct session *sess = NULL; + + sess = &prtd->session[prtd->mixer_conf.sess_indx]; + txtp = &sess->tx_tap_point; + rxtp = &sess->rx_tap_point; + paddr = sess->sess_paddr; + + if (paddr) { + msm_audio_ion_free(prtd->ion_client, sess->ion_handle); + prtd->ion_client = NULL; + sess->ion_handle = NULL; + msm_audio_ion_free(sess->tp_mem_table.client, + sess->tp_mem_table.handle); + sess->tp_mem_table.client = NULL; + sess->tp_mem_table.handle = NULL; + sess->sess_paddr = 0; + sess->sess_kvaddr = 0; + sess->ion_handle = 0; + prtd->ion_client = 0; + sess->tp_mem_table.client = 0; + sess->tp_mem_table.handle = 0; + + txtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0; + txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0; + + txtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0; + txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0; + + rxtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0; + rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0; + + rxtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0; + rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0; + } else { + pr_debug("%s, paddr = 0, nothing to free\n", __func__); + } +} + +static void hpcm_unmap_and_free_shared_memory(struct hpcm_drv *prtd) + +{ + phys_addr_t paddr = 0; + char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + + if (prtd->mixer_conf.sess_indx >= 0) + paddr = prtd->session[prtd->mixer_conf.sess_indx].sess_paddr; + else + paddr = 0; + + if (paddr) { + voc_send_cvp_unmap_vocpcm_memory(voc_get_session_id(sess_name)); + hpcm_free_allocated_mem(prtd); + } else { + pr_debug("%s, paddr = 0, nothing to unmap/free\n", __func__); + } +} + +static int hpcm_map_vocpcm_memory(struct hpcm_drv *prtd) +{ + int ret = 0; + char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + struct session *sess = NULL; + + sess = &prtd->session[prtd->mixer_conf.sess_indx]; + + ret = voc_send_cvp_map_vocpcm_memory(voc_get_session_id(sess_name), + &sess->tp_mem_table, + sess->sess_paddr, + VHPCM_BLOCK_SIZE); + + return ret; +} + +static int hpcm_allocate_shared_memory(struct hpcm_drv *prtd) +{ + int result; + int ret = 0; + size_t mem_len; + size_t len; + struct tap_point *txtp = NULL; + struct tap_point *rxtp = NULL; + struct session *sess = NULL; + + sess = &prtd->session[prtd->mixer_conf.sess_indx]; + txtp = &sess->tx_tap_point; + rxtp = &sess->rx_tap_point; + + result = msm_audio_ion_alloc("host_pcm_buffer", + &prtd->ion_client, + &sess->ion_handle, + VHPCM_BLOCK_SIZE, + &sess->sess_paddr, + &mem_len, + &sess->sess_kvaddr); + if (result) { + pr_err("%s: msm_audio_ion_alloc error, rc = %d\n", + __func__, result); + sess->sess_paddr = 0; + sess->sess_kvaddr = 0; + ret = -ENOMEM; + goto done; + } + pr_debug("%s: Host PCM memory block allocated\n", __func__); + + /* Allocate mem_map_table for tap point */ + result = msm_audio_ion_alloc("host_pcm_table", + &sess->tp_mem_table.client, + &sess->tp_mem_table.handle, + sizeof(struct vss_imemory_table_t), + &sess->tp_mem_table.phys, + &len, + &sess->tp_mem_table.data); + + if (result) { + pr_err("%s: msm_audio_ion_alloc error, rc = %d\n", + __func__, result); + msm_audio_ion_free(prtd->ion_client, sess->ion_handle); + prtd->ion_client = NULL; + sess->ion_handle = NULL; + sess->sess_paddr = 0; + sess->sess_kvaddr = 0; + ret = -ENOMEM; + goto done; + } + pr_debug("%s: Host PCM memory table allocated\n", __func__); + + memset(sess->tp_mem_table.data, 0, + sizeof(struct vss_imemory_table_t)); + + sess->tp_mem_table.size = sizeof(struct vss_imemory_table_t); + + pr_debug("%s: data %pK phys %pK\n", __func__, + sess->tp_mem_table.data, &sess->tp_mem_table.phys); + + /* Split 4096 block into four 1024 byte blocks for each dai */ + txtp->capture_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr; + txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr; + + txtp->playback_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr + VHPCM_BLOCK_SIZE/4; + txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr + VHPCM_BLOCK_SIZE/4; + + rxtp->capture_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 2; + rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 2; + + rxtp->playback_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 3; + rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 3; + +done: + return ret; +} + +static int hpcm_start_vocpcm(char *pcm_id, struct hpcm_drv *prtd, + struct tap_point *tp) +{ + int indx = prtd->mixer_conf.sess_indx; + uint32_t *no_of_tp = &prtd->start_cmd.no_of_tapoints; + struct vss_ivpcm_tap_point *tap_pnt = &prtd->start_cmd.tap_pnt[0]; + uint32_t no_of_tp_req = 0; + char *sess_name = hpcm_get_sess_name(indx); + + if (prtd->mixer_conf.rx.enable) + no_of_tp_req++; + if (prtd->mixer_conf.tx.enable) + no_of_tp_req++; + + if (prtd->mixer_conf.rx.enable && (get_tappnt_value(pcm_id) == RX)) { + if (hpcm_all_dais_are_ready(prtd->mixer_conf.rx.direction, + tp, HPCM_PREPARED)) { + pr_debug("%s: RX conditions met\n", __func__); + tap_pnt[*no_of_tp].tap_point = + VSS_IVPCM_TAP_POINT_RX_DEFAULT; + tap_pnt[*no_of_tp].direction = + prtd->mixer_conf.rx.direction; + tap_pnt[*no_of_tp].sampling_rate = + prtd->mixer_conf.rx.sample_rate; + (*no_of_tp)++; + } + } + + if (prtd->mixer_conf.tx.enable && (get_tappnt_value(pcm_id) == TX)) { + if (hpcm_all_dais_are_ready(prtd->mixer_conf.tx.direction, + tp, HPCM_PREPARED)) { + pr_debug("%s: TX conditions met\n", __func__); + tap_pnt[*no_of_tp].tap_point = + VSS_IVPCM_TAP_POINT_TX_DEFAULT; + tap_pnt[*no_of_tp].direction = + prtd->mixer_conf.tx.direction; + tap_pnt[*no_of_tp].sampling_rate = + prtd->mixer_conf.tx.sample_rate; + (*no_of_tp)++; + } + } + + if ((prtd->mixer_conf.tx.enable || prtd->mixer_conf.rx.enable) && + *no_of_tp == no_of_tp_req) { + voc_send_cvp_start_vocpcm(voc_get_session_id(sess_name), + tap_pnt, *no_of_tp); + /* Reset the start command so that it is not called twice */ + memset(&prtd->start_cmd, 0, sizeof(struct start_cmd)); + } else { + pr_debug("%s: required pcm handles not opened yet\n", __func__); + } + + return 0; +} + +/* Playback path*/ +static void hpcm_copy_playback_data_from_queue(struct dai_data *dai_data, + uint32_t *len) +{ + struct hpcm_buf_node *buf_node = NULL; + unsigned long dsp_flags; + + if (dai_data->substream == NULL) + return; + + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + + if (!list_empty(&dai_data->filled_queue)) { + buf_node = list_first_entry(&dai_data->filled_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + *len = buf_node->frame.len; + memcpy((u8 *)dai_data->vocpcm_ion_buffer.kvaddr, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &dai_data->free_queue); + dai_data->pcm_irq_pos += dai_data->pcm_count; + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(dai_data->substream); + } else { + *len = 0; + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + pr_err("IN data not available\n"); + } + + wake_up(&dai_data->queue_wait); +} + +/* Capture path*/ +static void hpcm_copy_capture_data_to_queue(struct dai_data *dai_data, + uint32_t len) +{ + struct hpcm_buf_node *buf_node = NULL; + unsigned long dsp_flags; + + if (dai_data->substream == NULL) + return; + + /* Copy out buffer packet into free_queue */ + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + + if (!list_empty(&dai_data->free_queue)) { + buf_node = list_first_entry(&dai_data->free_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + buf_node->frame.len = len; + memcpy(&buf_node->frame.voc_pkt[0], + (uint8_t *)dai_data->vocpcm_ion_buffer.kvaddr, + buf_node->frame.len); + list_add_tail(&buf_node->list, &dai_data->filled_queue); + dai_data->pcm_irq_pos += dai_data->pcm_count; + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(dai_data->substream); + } else { + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + pr_err("OUTPUT data dropped\n"); + } + + wake_up(&dai_data->queue_wait); +} + +void hpcm_notify_evt_processing(uint8_t *data, char *session, + void *private_data) +{ + struct hpcm_drv *prtd = (struct hpcm_drv *)private_data; + struct vss_ivpcm_evt_notify_v2_t *notify_evt = + (struct vss_ivpcm_evt_notify_v2_t *)data; + struct vss_ivpcm_evt_push_buffer_v2_t push_buff_event; + struct tap_point *tp = NULL; + int in_buf_len = 0; + struct tappnt_mxr_data *tmd = NULL; + char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + + /* If it's not a timetick, it's a error notification, drop the event */ + if ((notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_TIMETICK) == 0) { + pr_err("%s: Error notification. mask=%d\n", __func__, + notify_evt->notify_mask); + return; + } + + if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) { + tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point; + tmd = &prtd->mixer_conf.tx; + } else if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_RX_DEFAULT) { + tp = &prtd->session[prtd->mixer_conf.sess_indx].rx_tap_point; + tmd = &prtd->mixer_conf.rx; + } + + if (tp == NULL || tmd == NULL) { + pr_err("%s: tp = %pK or tmd = %pK is null\n", __func__, + tp, tmd); + + return; + } + + if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER) { + hpcm_copy_capture_data_to_queue(&tp->capture_dai_data, + notify_evt->filled_out_size); + } + + if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER) { + hpcm_copy_playback_data_from_queue(&tp->playback_dai_data, + &in_buf_len); + } + + switch (tmd->direction) { + /* + * When the dir is OUT_IN, for the first notify mask, pushbuf mask + * should be set to VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER since we + * atleast need one buffer's worth data before we can send IN buffer. + * For the consecutive notify evts, the push buf mask will set for both + * VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER and + * VSS_IVPCM_PUSH_BUFFER_MASK_IN_BUFFER. + */ + case VSS_IVPCM_TAP_POINT_DIR_OUT_IN: + if (notify_evt->notify_mask == + VSS_IVPCM_NOTIFY_MASK_TIMETICK) { + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER; + } else { + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER | + VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER; + } + break; + + case VSS_IVPCM_TAP_POINT_DIR_IN: + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER; + break; + + case VSS_IVPCM_TAP_POINT_DIR_OUT: + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER; + break; + } + + push_buff_event.tap_point = notify_evt->tap_point; + push_buff_event.out_buf_mem_address = + tp->capture_dai_data.vocpcm_ion_buffer.paddr; + push_buff_event.in_buf_mem_address = + tp->playback_dai_data.vocpcm_ion_buffer.paddr; + push_buff_event.sampling_rate = notify_evt->sampling_rate; + push_buff_event.num_in_channels = 1; + + /* + * ADSP must read and write from a cache aligned (128 byte) location, + * and in blocks of the cache alignment size. The 128 byte cache + * alignment requirement is guaranteed due to 4096 byte memory + * alignment requirement during memory allocation/mapping. The output + * buffer (ADSP write) size mask ensures that a 128 byte multiple + * worth of will be written. Internally, the input buffer (ADSP read) + * size will also be a multiple of 128 bytes. However it is the + * application's responsibility to ensure no other data is written in + * the specified length of memory. + */ + push_buff_event.out_buf_mem_size = ((notify_evt->request_buf_size) + + CACHE_ALIGNMENT_SIZE) & CACHE_ALIGNMENT_MASK; + push_buff_event.in_buf_mem_size = in_buf_len; + + voc_send_cvp_vocpcm_push_buf_evt(voc_get_session_id(sess_name), + &push_buff_event); +} + +static int msm_hpcm_configure_voice_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOICE_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control voice values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOICE_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; +} + +static int msm_hpcm_configure_vmmode1_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOMMODE1_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control voice values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOMMODE1_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; +} + +static int msm_hpcm_configure_vmmode2_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOMMODE2_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control voice values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOMMODE2_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; +} + +static int msm_hpcm_configure_volte_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point=%d direction=%d sample_rate=%d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOLTE_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control volte values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOLTE_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; + +} + +static struct snd_kcontrol_new msm_hpcm_controls[] = { + SOC_SINGLE_MULTI_EXT("HPCM_Voice tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_voice_put), + SOC_SINGLE_MULTI_EXT("HPCM_VoLTE tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_volte_put), + SOC_SINGLE_MULTI_EXT("HPCM_VMMode1 tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_vmmode1_put), + SOC_SINGLE_MULTI_EXT("HPCM_VMMode2 tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_vmmode2_put), +}; + +/* Sample rates supported */ +static unsigned int supported_sample_rates[] = {8000, 16000}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct hpcm_buf_node *buf_node = NULL; + struct snd_dma_buffer *dma_buf; + struct snd_pcm_runtime *runtime; + struct hpcm_drv *prtd; + unsigned long dsp_flags; + struct dai_data *dai_data = NULL; + struct tap_point *tp = NULL; + struct tappnt_mxr_data *tmd = NULL; + char *sess_name = NULL; + + if (substream == NULL) { + pr_err("substream is NULL\n"); + return -EINVAL; + } + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + runtime = substream->runtime; + prtd = runtime->private_data; + sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + wake_up(&dai_data->queue_wait); + mutex_lock(&prtd->lock); + + tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd); + + tp = hpcm_get_tappoint_data(substream->pcm->id, prtd); + /* Send stop command */ + voc_send_cvp_stop_vocpcm(voc_get_session_id(sess_name)); + /* Memory unmap/free takes place only when called the first time */ + hpcm_unmap_and_free_shared_memory(prtd); + /* Unregister host PCM event callback function */ + voc_deregister_hpcm_evt_cb(); + /* Reset the cached start cmd */ + memset(&prtd->start_cmd, 0, sizeof(struct start_cmd)); + /* Release all buffer */ + pr_debug("%s: Release all buffer\n", __func__); + substream = dai_data->substream; + if (substream == NULL) { + pr_debug("%s: substream is NULL\n", __func__); + goto done; + } + dma_buf = &substream->dma_buffer; + if (dma_buf == NULL) { + pr_debug("%s: dma_buf is NULL\n", __func__); + goto done; + } + if (dma_buf->area != NULL) { + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + list_for_each_safe(ptr, next, &dai_data->filled_queue) { + buf_node = list_entry(ptr, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + } + list_for_each_safe(ptr, next, &dai_data->free_queue) { + buf_node = list_entry(ptr, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + } + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + dma_free_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, dma_buf->area, + dma_buf->addr); + dma_buf->area = NULL; + } + dai_data->substream = NULL; + dai_data->pcm_buf_pos = 0; + dai_data->pcm_count = 0; + dai_data->pcm_irq_pos = 0; + dai_data->pcm_size = 0; + dai_data->state = HPCM_CLOSED; + hpcm_reset_mixer_config(prtd); + +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, + snd_pcm_uframes_t frames) +{ + int ret = 0; + struct hpcm_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + unsigned long dsp_flags; + + int count = frames_to_bytes(runtime, frames); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_interruptible_timeout(dai_data->queue_wait, + (!list_empty(&dai_data->free_queue) || + dai_data->state == HPCM_STOPPED), + 1 * HZ); + if (ret > 0) { + if (count <= HPCM_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + buf_node = + list_first_entry(&dai_data->free_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + ret = copy_from_user(&buf_node->frame.voc_pkt, buf, + count); + buf_node->frame.len = count; + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, &dai_data->filled_queue); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + } else { + pr_err("%s: Write cnt %d is > HPCM_MAX_VOC_PKT_SIZE\n", + __func__, count); + ret = -ENOMEM; + } + } else if (ret == 0) { + pr_err("%s: No free Playback buffer\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: playback copy was interrupted\n", __func__); + } + +done: + return ret; +} + +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t hwoff, + void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + int count = 0; + struct hpcm_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + unsigned long dsp_flags; + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + count = frames_to_bytes(runtime, frames); + + ret = wait_event_interruptible_timeout(dai_data->queue_wait, + (!list_empty(&dai_data->filled_queue) || + dai_data->state == HPCM_STOPPED), + 1 * HZ); + + if (ret > 0) { + if (count <= HPCM_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + buf_node = list_first_entry(&dai_data->filled_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + ret = copy_to_user(buf, &buf_node->frame.voc_pkt, + buf_node->frame.len); + if (ret) { + pr_err("%s: Copy to user returned %d\n", + __func__, ret); + ret = -EFAULT; + } + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, &dai_data->free_queue); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + + } else { + pr_err("%s: Read count %d > HPCM_MAX_VOC_PKT_SIZE\n", + __func__, count); + ret = -ENOMEM; + } + + } else if (ret == 0) { + pr_err("%s: No Caputre data available\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + ret = -ERESTARTSYS; + } + +done: + return ret; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t hwoff, void __user *buf, + snd_pcm_uframes_t frames) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_copy(substream, channel, + hwoff, buf, frames); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, channel, + hwoff, buf, frames); + + return ret; +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct dai_data *dai_data = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + snd_pcm_uframes_t ret; + + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = 0; + goto done; + } + + if (dai_data->pcm_irq_pos >= dai_data->pcm_size) + dai_data->pcm_irq_pos = 0; + + ret = bytes_to_frames(runtime, (dai_data->pcm_irq_pos)); + +done: + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = + hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("SNDRV_PCM_TRIGGER_START\n"); + dai_data->state = HPCM_STARTED; + break; + + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + dai_data->state = HPCM_STOPPED; + break; + + default: + ret = -EINVAL; + break; + } + +done: + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = NULL; + struct tap_point *tp = NULL; + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + mutex_lock(&prtd->lock); + + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + dai_data->pcm_size = snd_pcm_lib_buffer_bytes(substream); + dai_data->pcm_count = snd_pcm_lib_period_bytes(substream); + dai_data->pcm_irq_pos = 0; + dai_data->pcm_buf_pos = 0; + dai_data->state = HPCM_PREPARED; + + /* Register event notify processing callback in prepare instead of + * init() as q6voice module's init() can be called at a later point + */ + voc_register_hpcm_evt_cb(hpcm_notify_evt_processing, &hpcm_drv); + + tp = hpcm_get_tappoint_data(substream->pcm->id, prtd); + if (tp != NULL) { + ret = hpcm_start_vocpcm(substream->pcm->id, prtd, tp); + if (ret) { + pr_err("error sending start cmd err=%d\n", ret); + goto done; + } + } else { + pr_err("%s tp is NULL\n", __func__); + } +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct hpcm_drv *prtd = (struct hpcm_drv *)runtime->private_data; + int ret = 0; + + pr_debug("%s: %s\n", __func__, substream->pcm->id); + mutex_lock(&prtd->lock); + + /* Allocate and map voice host PCM ion buffer */ + if (prtd->session[prtd->mixer_conf.sess_indx].sess_paddr == 0) { + ret = hpcm_allocate_shared_memory(prtd); + if (ret) { + pr_err("error creating shared memory err=%d\n", ret); + goto done; + } + + ret = hpcm_map_vocpcm_memory(prtd); + if (ret) { + pr_err("error mapping shared memory err=%d\n", ret); + hpcm_free_allocated_mem(prtd); + goto done; + } + } else { + pr_debug("%s, VHPCM memory allocation/mapping not performed\n" + , __func__); + } + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + + dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + &dma_buf->addr, GFP_KERNEL); + + if (!dma_buf->area) { + pr_err("%s:MSM dma_alloc failed\n", __func__); + ret = -ENOMEM; + goto done; + } + + dma_buf->bytes = runtime->hw.buffer_bytes_max; + memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max); + + hpcm_create_free_queue(dma_buf, + hpcm_get_dai_data(substream->pcm->id, prtd)); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = &hpcm_drv; + struct tappnt_mxr_data *tmd = NULL; + struct dai_data *dai_data = NULL; + int ret = 0; + int tp_val = 0; + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + mutex_lock(&prtd->lock); + + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + runtime->hw = msm_pcm_hardware; + + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_debug("snd_pcm_hw_constraint_list failed\n"); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + pr_debug("snd_pcm_hw_constraint_integer failed\n"); + goto done; + } + + tp_val = get_tappnt_value(substream->pcm->id); + tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd); + + /* Check wheather the kcontrol values set are valid */ + if (!tmd || + !(tmd->enable) || + !hpcm_is_valid_config(prtd->mixer_conf.sess_indx, + tp_val, tmd->direction, + tmd->sample_rate)) { + ret = -EINVAL; + goto done; + } + + dai_data->substream = substream; + runtime->private_data = prtd; + +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .hw_params = msm_pcm_hw_params, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .copy = msm_pcm_copy, + .close = msm_pcm_close, +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + + pr_debug("%s:\n", __func__); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + return 0; +} + +static int msm_pcm_hpcm_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_hpcm_controls, + ARRAY_SIZE(msm_hpcm_controls)); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_hpcm_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + + pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_voice_host_pcm_dt_match[] = { + {.compatible = "qcom,msm-voice-host-pcm"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_voice_host_pcm_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-voice-host-pcm", + .owner = THIS_MODULE, + .of_match_table = msm_voice_host_pcm_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + int i = 0; + struct session *s = NULL; + + memset(&hpcm_drv, 0, sizeof(hpcm_drv)); + mutex_init(&hpcm_drv.lock); + + for (i = 0; i < MAX_SESSION; i++) { + s = &hpcm_drv.session[i]; + spin_lock_init(&s->rx_tap_point.capture_dai_data.dsp_lock); + spin_lock_init(&s->rx_tap_point.playback_dai_data.dsp_lock); + spin_lock_init(&s->tx_tap_point.capture_dai_data.dsp_lock); + spin_lock_init(&s->tx_tap_point.playback_dai_data.dsp_lock); + + init_waitqueue_head( + &s->rx_tap_point.capture_dai_data.queue_wait); + init_waitqueue_head( + &s->rx_tap_point.playback_dai_data.queue_wait); + init_waitqueue_head( + &s->tx_tap_point.capture_dai_data.queue_wait); + init_waitqueue_head( + &s->tx_tap_point.playback_dai_data.queue_wait); + + INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.filled_queue); + INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.free_queue); + INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.filled_queue); + INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.free_queue); + + INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.filled_queue); + INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.free_queue); + INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.filled_queue); + INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.free_queue); + } + + return platform_driver_register(&msm_pcm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c new file mode 100644 index 000000000000..5072ecd12b5f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c @@ -0,0 +1,790 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-v2.h" + +#define LOOPBACK_VOL_MAX_STEPS 0x2000 +#define LOOPBACK_SESSION_MAX 4 + +static DEFINE_MUTEX(loopback_session_lock); +static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0, + LOOPBACK_VOL_MAX_STEPS); + +struct msm_pcm_loopback { + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + int instance; + + struct mutex lock; + + uint32_t samp_rate; + uint32_t channel_mode; + + int playback_start; + int capture_start; + int session_id; + struct audio_client *audio_client; + uint32_t volume; +}; + +struct fe_dai_session_map { + char stream_name[32]; + struct msm_pcm_loopback *loopback_priv; +}; + +static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = { + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, +}; + +static u32 hfp_tx_mute; + +static void stop_pcm(struct msm_pcm_loopback *pcm); +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm); + +static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, + void *priv_data) +{ + struct msm_pcm_loopback *pcm = priv_data; + + WARN_ON(!pcm); + + pr_debug("%s: event 0x%x\n", __func__, event); + + switch (event) { + case MSM_PCM_RT_EVT_DEVSWITCH: + q6asm_cmd(pcm->audio_client, CMD_PAUSE); + q6asm_cmd(pcm->audio_client, CMD_FLUSH); + q6asm_run(pcm->audio_client, 0, 0, 0); + /* fallthrough */ + default: + pr_err("%s: default event 0x%x\n", __func__, event); + break; + } +} + +static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + pr_debug("%s:\n", __func__); + switch (opcode) { + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + break; + default: + break; + } + } + break; + default: + pr_err("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); + break; + } +} + +static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hfp_tx_mute; + return 0; +} + +static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0, n = 0; + int mute = ucontrol->value.integer.value[0]; + struct msm_pcm_loopback *pcm = NULL; + + if ((mute < 0) || (mute > 1)) { + pr_err(" %s Invalid arguments", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d\n", __func__, mute); + hfp_tx_mute = mute; + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(session_map[n].stream_name, "MultiMedia6")) + pcm = session_map[n].loopback_priv; + } + if (pcm && pcm->audio_client) { + ret = q6asm_set_mute(pcm->audio_client, mute); + if (ret < 0) + pr_err("%s: Send mute command failed rc=%d\n", + __func__, ret); + } +done: + return ret; +} + +static struct snd_kcontrol_new msm_loopback_controls[] = { + SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0, + msm_loopback_session_mute_get, + msm_loopback_session_mute_put), +}; + +static int msm_pcm_loopback_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_loopback_controls, + ARRAY_SIZE(msm_loopback_controls)); + + return 0; +} +static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, + uint32_t volume) +{ + int rc = -EINVAL; + + pr_debug("%s: Setting volume 0x%x\n", __func__, volume); + + if (prtd && prtd->audio_client) { + rc = q6asm_set_volume(prtd->audio_client, volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc = %d\n", + __func__, rc); + return rc; + } + prtd->volume = volume; + } + return rc; +} + +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm) +{ + int ret = 0; + int n, index = -1; + + dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__, + rtd->dai_link->stream_name); + + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + *pcm = session_map[n].loopback_priv; + goto exit; + } + /* + * Store the min index value for allocating a new session. + * Here, if session stream name is not found in the + * existing entries after the loop iteration, then this + * index will be used to allocate the new session. + * This index variable is expected to point to the topmost + * available free session. + */ + if (!(session_map[n].stream_name[0]) && (index < 0)) + index = n; + } + + if (index < 0) { + dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n", + __func__); + ret = -EAGAIN; + goto exit; + } + + session_map[index].loopback_priv = kzalloc( + sizeof(struct msm_pcm_loopback), GFP_KERNEL); + if (!session_map[index].loopback_priv) { + ret = -ENOMEM; + goto exit; + } + + strlcpy(session_map[index].stream_name, + rtd->dai_link->stream_name, + sizeof(session_map[index].stream_name)); + dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n", + __func__, session_map[index].stream_name, index); + + mutex_init(&session_map[index].loopback_priv->lock); + *pcm = session_map[index].loopback_priv; +exit: + mutex_unlock(&loopback_session_lock); + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct msm_pcm_loopback *pcm = NULL; + int ret = 0; + uint16_t bits_per_sample = 16; + struct msm_pcm_routing_evt event; + struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; + uint32_t param_id; + + ret = msm_pcm_loopback_get_session(rtd, &pcm); + if (ret) + return ret; + + mutex_lock(&pcm->lock); + + pcm->volume = 0x2000; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pcm->playback_substream = substream; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm->capture_substream = substream; + + pcm->instance++; + dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__, + pcm->instance, substream->stream); + if (pcm->instance == 2) { + struct snd_soc_pcm_runtime *soc_pcm_rx = + pcm->playback_substream->private_data; + struct snd_soc_pcm_runtime *soc_pcm_tx = + pcm->capture_substream->private_data; + if (pcm->audio_client != NULL) + stop_pcm(pcm); + + pcm->audio_client = q6asm_audio_client_alloc( + (app_cb)msm_pcm_loopback_event_handler, pcm); + if (!pcm->audio_client) { + dev_err(rtd->platform->dev, + "%s: Could not allocate memory\n", __func__); + mutex_unlock(&pcm->lock); + return -ENOMEM; + } + pcm->session_id = pcm->audio_client->session; + pcm->audio_client->perf_mode = false; + ret = q6asm_open_loopback_v2(pcm->audio_client, + bits_per_sample); + if (ret < 0) { + dev_err(rtd->platform->dev, + "%s: pcm out open failed\n", __func__); + q6asm_audio_client_free(pcm->audio_client); + mutex_unlock(&pcm->lock); + return -ENOMEM; + } + event.event_func = msm_pcm_route_event_handler; + event.priv_data = (void *) pcm; + msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->be_id, + pcm->audio_client->perf_mode, + pcm->session_id, pcm->capture_substream->stream); + msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->be_id, + pcm->audio_client->perf_mode, + pcm->session_id, pcm->playback_substream->stream, + event); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pcm->playback_substream = substream; + ret = pcm_loopback_set_volume(pcm, pcm->volume); + if (ret < 0) + dev_err(rtd->platform->dev, + "Error %d setting volume", ret); + } + /* Set to largest negative value */ + asm_mtmx_strtr_window.window_lsw = 0x00000000; + asm_mtmx_strtr_window.window_msw = 0x80000000; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2; + q6asm_send_mtmx_strtr_window(pcm->audio_client, + &asm_mtmx_strtr_window, + param_id); + /* Set to largest positive value */ + asm_mtmx_strtr_window.window_lsw = 0xffffffff; + asm_mtmx_strtr_window.window_msw = 0x7fffffff; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2; + q6asm_send_mtmx_strtr_window(pcm->audio_client, + &asm_mtmx_strtr_window, + param_id); + } + dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n", + __func__, pcm->instance, substream->pcm->id); + runtime->private_data = pcm; + + mutex_unlock(&pcm->lock); + + return 0; +} + +static void stop_pcm(struct msm_pcm_loopback *pcm) +{ + struct snd_soc_pcm_runtime *soc_pcm_rx; + struct snd_soc_pcm_runtime *soc_pcm_tx; + + if (pcm->audio_client == NULL) + return; + q6asm_cmd(pcm->audio_client, CMD_CLOSE); + + if (pcm->playback_substream != NULL) { + soc_pcm_rx = pcm->playback_substream->private_data; + msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id, + SNDRV_PCM_STREAM_PLAYBACK); + } + if (pcm->capture_substream != NULL) { + soc_pcm_tx = pcm->capture_substream->private_data; + msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id, + SNDRV_PCM_STREAM_CAPTURE); + } + q6asm_audio_client_free(pcm->audio_client); + pcm->audio_client = NULL; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_pcm_loopback *pcm = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + int ret = 0, n; + bool found = false; + + mutex_lock(&pcm->lock); + + dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n", + __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pcm->playback_start = 0; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm->capture_start = 0; + + pcm->instance--; + if (!pcm->playback_start || !pcm->capture_start) { + dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__); + stop_pcm(pcm); + } + + if (!pcm->instance) { + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + found = true; + break; + } + } + if (found) { + memset(session_map[n].stream_name, 0, + sizeof(session_map[n].stream_name)); + mutex_unlock(&pcm->lock); + mutex_destroy(&session_map[n].loopback_priv->lock); + session_map[n].loopback_priv = NULL; + kfree(pcm); + dev_dbg(rtd->platform->dev, "%s: stream freed %s\n", + __func__, rtd->dai_link->stream_name); + mutex_unlock(&loopback_session_lock); + return 0; + } + mutex_unlock(&loopback_session_lock); + } + mutex_unlock(&pcm->lock); + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_pcm_loopback *pcm = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + + mutex_lock(&pcm->lock); + + dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n", + __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!pcm->playback_start) + pcm->playback_start = 1; + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (!pcm->capture_start) + pcm->capture_start = 1; + } + mutex_unlock(&pcm->lock); + + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_pcm_loopback *pcm = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev_dbg(rtd->platform->dev, + "%s: playback_start:%d,capture_start:%d\n", __func__, + pcm->playback_start, pcm->capture_start); + if (pcm->playback_start && pcm->capture_start) + q6asm_run_nowait(pcm->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + dev_dbg(rtd->platform->dev, + "%s:Pause/Stop - playback_start:%d,capture_start:%d\n", + __func__, pcm->playback_start, pcm->capture_start); + if (pcm->playback_start && pcm->capture_start) + q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE); + break; + default: + pr_err("%s: default cmd %d\n", __func__, cmd); + break; + } + + return 0; +} + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, +}; + +static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = kcontrol->private_data; + struct snd_pcm_substream *substream = vol->pcm->streams[0].substream; + struct msm_pcm_loopback *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if ((!substream) || (!substream->runtime)) { + pr_err("%s substream or runtime not found\n", __func__); + rc = -ENODEV; + goto exit; + } + prtd = substream->runtime->private_data; + if (!prtd) { + rc = -ENODEV; + goto exit; + } + rc = pcm_loopback_set_volume(prtd, volume); + +exit: + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_pcm_loopback *prtd; + + pr_debug("%s\n", __func__); + if ((!substream) || (!substream->runtime)) { + pr_err("%s substream or runtime not found\n", __func__); + rc = -ENODEV; + goto exit; + } + prtd = substream->runtime->private_data; + if (!prtd) { + rc = -ENODEV; + goto exit; + } + ucontrol->value.integer.value[0] = prtd->volume; + +exit: + return rc; +} + +static int msm_pcm_add_volume_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm->streams[0].pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + int ret = 0; + + dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, + rtd->dai_link->be_id, + &volume_info); + if (ret < 0) + return ret; + kctl = volume_info->kctl; + kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; + kctl->tlv.p = loopback_rx_vol_gain; + return 0; +} + +static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int app_type; + int acdb_dev_id; + int sample_rate = 48000; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + return -EINVAL; + } + + app_type = ucontrol->value.integer.value[0]; + acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n", + __func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_RX); + msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, + acdb_dev_id, sample_rate, SESSION_TYPE_RX); + + return 0; +} + +static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int ret = 0; + int app_type; + int acdb_dev_id; + int sample_rate; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_RX, + &app_type, &acdb_dev_id, &sample_rate); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = app_type; + ucontrol->value.integer.value[1] = acdb_dev_id; + ucontrol->value.integer.value[2] = sample_rate; + pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, SESSION_TYPE_RX, + app_type, acdb_dev_id, sample_rate); +done: + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int app_type; + int acdb_dev_id; + int sample_rate = 48000; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + return -EINVAL; + } + + app_type = ucontrol->value.integer.value[0]; + acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n", + __func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_TX); + msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, + acdb_dev_id, sample_rate, SESSION_TYPE_TX); + + return 0; +} + +static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int ret = 0; + int app_type; + int acdb_dev_id; + int sample_rate; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_TX, + &app_type, &acdb_dev_id, &sample_rate); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = app_type; + ucontrol->value.integer.value[1] = acdb_dev_id; + ucontrol->value.integer.value[2] = sample_rate; + pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, SESSION_TYPE_TX, + app_type, acdb_dev_id, sample_rate); +done: + return ret; +} + +static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm->streams[0].pcm; + struct snd_pcm_usr *app_type_info; + struct snd_kcontrol *kctl; + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ctl_len = strlen(playback_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Playback app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, ctl_len, rtd->dai_link->be_id, + &app_type_info); + if (ret < 0) + return ret; + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_playback_app_type_cfg_ctl_put; + kctl->get = msm_pcm_playback_app_type_cfg_ctl_get; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ctl_len = strlen(capture_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Capture app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->be_id, + &app_type_info); + if (ret < 0) + return ret; + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_capture_app_type_cfg_ctl_put; + kctl->get = msm_pcm_capture_app_type_cfg_ctl_get; + } + + return 0; +} + +static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + ret = msm_pcm_add_volume_controls(rtd); + if (ret) + pr_err("%s: pcm add volume controls failed:%d\n", + __func__, ret); + ret = msm_pcm_add_app_type_controls(rtd); + if (ret) + pr_err("%s: pcm add app type controls failed:%d\n", + __func__, ret); + return ret; +} + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_pcm_add_controls(rtd); + if (ret) + dev_err(rtd->dev, "%s, kctl add failed\n", __func__); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_loopback_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_loopback_dt_match[] = { + {.compatible = "qcom,msm-pcm-loopback"}, + {} +}; + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-loopback", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_loopback_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + return platform_driver_register(&msm_pcm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("PCM loopback platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c new file mode 100644 index 000000000000..1fdb878a1a1f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c @@ -0,0 +1,845 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" + +#define PCM_MASTER_VOL_MAX_STEPS 0x2000 +static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0, + PCM_MASTER_VOL_MAX_STEPS); + +struct snd_msm { + struct snd_card *card; + struct snd_pcm *pcm; +}; + +#define CMD_EOS_MIN_TIMEOUT_LENGTH 50 +#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50) + +#define ATRACE_END() \ + trace_printk("tracing_mark_write: E\n") +#define ATRACE_BEGIN(name) \ + trace_printk("tracing_mark_write: B|%d|%s\n", current->tgid, name) +#define ATRACE_FUNC() ATRACE_BEGIN(__func__) +#define ATRACE_INT(name, value) \ + trace_printk("tracing_mark_write: C|%d|%s|%d\n", \ + current->tgid, name, (int)(value)) + +#define SIO_PLAYBACK_MAX_PERIOD_SIZE PLAYBACK_MAX_PERIOD_SIZE +#define SIO_PLAYBACK_MIN_PERIOD_SIZE 48 +#define SIO_PLAYBACK_MAX_NUM_PERIODS 512 +#define SIO_PLAYBACK_MIN_NUM_PERIODS PLAYBACK_MIN_NUM_PERIODS +#define SIO_PLAYBACK_MIN_BYTES (SIO_PLAYBACK_MIN_NUM_PERIODS * \ + SIO_PLAYBACK_MIN_PERIOD_SIZE) + +#define SIO_PLAYBACK_MAX_BYTES ((SIO_PLAYBACK_MAX_NUM_PERIODS) * \ + (SIO_PLAYBACK_MAX_PERIOD_SIZE)) + +#define SIO_CAPTURE_MAX_PERIOD_SIZE CAPTURE_MAX_PERIOD_SIZE +#define SIO_CAPTURE_MIN_PERIOD_SIZE 48 +#define SIO_CAPTURE_MAX_NUM_PERIODS 512 +#define SIO_CAPTURE_MIN_NUM_PERIODS CAPTURE_MIN_NUM_PERIODS + +#define SIO_CAPTURE_MIN_BYTES (SIO_CAPTURE_MIN_NUM_PERIODS * \ + SIO_CAPTURE_MIN_PERIOD_SIZE) + +#define SIO_CAPTURE_MAX_BYTES (SIO_CAPTURE_MAX_NUM_PERIODS * \ + SIO_CAPTURE_MAX_PERIOD_SIZE) + +static struct snd_pcm_hardware msm_pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = SIO_PLAYBACK_MAX_NUM_PERIODS * + SIO_PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = SIO_PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = SIO_PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = SIO_PLAYBACK_MIN_NUM_PERIODS, + .periods_max = SIO_PLAYBACK_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = SIO_CAPTURE_MAX_NUM_PERIODS * + SIO_CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = SIO_CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = SIO_CAPTURE_MAX_PERIOD_SIZE, + .periods_min = SIO_CAPTURE_MIN_NUM_PERIODS, + .periods_max = SIO_CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static void event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + uint32_t *ptrmem = (uint32_t *)payload; + + switch (opcode) { + case ASM_DATA_EVENT_WATERMARK: + pr_debug("%s: Watermark level = 0x%08x\n", __func__, *ptrmem); + break; + case APR_BASIC_RSP_RESULT: + pr_debug("%s: Payload = [0x%x]stat[0x%x]\n", + __func__, payload[0], payload[1]); + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + case ASM_SESSION_CMD_PAUSE: + case ASM_STREAM_CMD_FLUSH: + break; + default: + break; + } + break; + default: + pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + break; + } +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd; + int ret = 0; + + prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL); + + if (prtd == NULL) + return -ENOMEM; + + prtd->substream = substream; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = msm_pcm_hardware_playback; + else + runtime->hw = msm_pcm_hardware_capture; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret) + pr_info("snd_pcm_hw_constraint_list failed\n"); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + SIO_PLAYBACK_MIN_BYTES, + SIO_PLAYBACK_MAX_BYTES); + if (ret) { + pr_info("%s: P buffer bytes minmax constraint ret %d\n", + __func__, ret); + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + SIO_CAPTURE_MIN_BYTES, + SIO_CAPTURE_MAX_BYTES); + if (ret) { + pr_info("%s: C buffer bytes minmax constraint ret %d\n", + __func__, ret); + } + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret) { + pr_err("%s: Constraint for period bytes step ret = %d\n", + __func__, ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret) { + pr_err("%s: Constraint for buffer bytes step ret = %d\n", + __func__, ret); + } + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)event_handler, prtd); + if (!prtd->audio_client) { + pr_err("%s: client alloc failed\n", __func__); + ret = -ENOMEM; + goto fail_cmd; + } + prtd->dsp_cnt = 0; + prtd->set_channel_map = false; + runtime->private_data = prtd; + return 0; + +fail_cmd: + kfree(prtd); + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct msm_plat_data *pdata; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct audio_buffer *buf; + struct shared_io_config config; + uint16_t sample_word_size; + uint16_t bits_per_sample; + int ret; + int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? IN : OUT; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data not populated ret: %d\n", __func__, + ret); + return ret; + } + + /* need to set LOW_LATENCY_PCM_MODE for capture since + * push mode does not support ULL + */ + prtd->audio_client->perf_mode = (dir == IN) ? + pdata->perf_mode : + LOW_LATENCY_PCM_MODE; + + /* rate and channels are sent to audio driver */ + prtd->samp_rate = params_rate(params); + prtd->channel_mode = params_channels(params); + if (prtd->enabled) + return 0; + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + + config.format = FORMAT_LINEAR_PCM; + config.bits_per_sample = bits_per_sample; + config.rate = params_rate(params); + config.channels = params_channels(params); + config.sample_word_size = sample_word_size; + config.bufsz = params_buffer_bytes(params) / params_periods(params); + config.bufcnt = params_periods(params); + + ret = q6asm_open_shared_io(prtd->audio_client, &config, dir); + if (ret) { + pr_err("%s: q6asm_open_write_shared_io failed ret: %d\n", + __func__, ret); + return ret; + } + + prtd->pcm_size = params_buffer_bytes(params); + prtd->pcm_count = params_buffer_bytes(params); + prtd->pcm_irq_pos = 0; + + buf = prtd->audio_client->port[dir].buf; + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf->data; + dma_buf->addr = buf->phys; + dma_buf->bytes = prtd->pcm_size; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + pr_debug("%s: session ID %d, perf %d\n", __func__, + prtd->audio_client->session, + prtd->audio_client->perf_mode); + prtd->session_id = prtd->audio_client->session; + + pr_debug("msm_pcm_routing_reg_phy_stream w/ id %d\n", + soc_prtd->dai_link->be_id); + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream); + + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + + atomic_set(&prtd->out_count, runtime->periods); + prtd->enabled = 1; + prtd->cmd_pending = 0; + prtd->cmd_interrupt = 0; + + return 0; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; + struct audio_buffer *buf; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: %s Trigger start\n", __func__, + dir == 0 ? "P" : "C"); + ret = q6asm_run(prtd->audio_client, 0, 0, 0); + if (ret) + break; + atomic_set(&prtd->start, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); + atomic_set(&prtd->start, 0); + q6asm_cmd(prtd->audio_client, CMD_PAUSE); + q6asm_cmd(prtd->audio_client, CMD_FLUSH); + buf = q6asm_shared_io_buf(prtd->audio_client, dir); + if (buf == NULL) { + pr_err("%s: shared IO buffer is null\n", __func__); + ret = -EINVAL; + break; + } + memset(buf->data, 0, buf->actual_size); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("%s: SNDRV_PCM_TRIGGER_PAUSE\n", __func__); + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->start, 0); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int msm_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; + struct audio_buffer *buf; + + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + pr_debug("%s: %s SNDRV_PCM_IOCTL1_RESET\n", __func__, + dir == 0 ? "P" : "C"); + buf = q6asm_shared_io_buf(prtd->audio_client, dir); + + if (buf && buf->data) + memset(buf->data, 0, buf->actual_size); + break; + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + uint32_t read_index, wall_clk_msw, wall_clk_lsw; + /*these are offsets, unlike ASoC's full values*/ + snd_pcm_sframes_t hw_ptr; + snd_pcm_sframes_t period_size; + int ret; + int retries = 10; + struct msm_audio *prtd = runtime->private_data; + + period_size = runtime->period_size; + + do { + ret = q6asm_get_shared_pos(prtd->audio_client, + &read_index, &wall_clk_msw, + &wall_clk_lsw); + } while (ret == -EAGAIN && --retries); + + if (ret || !period_size) { + pr_err("get_shared_pos error or zero period size\n"); + return 0; + } + + hw_ptr = bytes_to_frames(substream->runtime, + read_index); + + if (runtime->control->appl_ptr == 0) { + pr_debug("ptr(%s): appl(0), hw = %lu read_index = %u\n", + prtd->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "P" : "C", + hw_ptr, read_index); + } + return (hw_ptr/period_size) * period_size; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + return -EINVAL; +} + +static int msm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + struct audio_port_data *apd = ac->port; + struct audio_buffer *ab; + int dir = -1; + int ret; + + pr_debug("%s: mmap begin\n", __func__); + prtd->mmap_flag = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + + ab = &(apd[dir].buf[0]); + + ret = msm_audio_ion_mmap(ab, vma); + + if (ret) + prtd->mmap_flag = 0; + + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + if (!prtd || !prtd->mmap_flag) + return -EIO; + + return 0; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + uint32_t timeout; + int dir = 0; + int ret = 0; + + if (ac) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = OUT; + + /* determine timeout length */ + if (runtime->frame_bits == 0 || runtime->rate == 0) { + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } else { + timeout = (runtime->period_size * + CMD_EOS_TIMEOUT_MULTIPLIER) / + ((runtime->frame_bits / 8) * + runtime->rate); + if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH) + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } + + q6asm_cmd(ac, CMD_CLOSE); + + ret = q6asm_shared_io_free(ac, dir); + + if (ret) { + pr_err("%s: Failed to close pull mode, ret %d\n", + __func__, ret); + } + q6asm_audio_client_free(ac); + } + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, + dir == IN ? + SNDRV_PCM_STREAM_PLAYBACK : + SNDRV_PCM_STREAM_CAPTURE); + kfree(prtd); + return 0; +} + +static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume) +{ + int rc = 0; + + if (prtd && prtd->audio_client) { + pr_debug("%s: channels %d volume 0x%x\n", __func__, + prtd->channel_mode, volume); + rc = q6asm_set_volume(prtd->audio_client, volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + } + } + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + + pr_debug("%s\n", __func__); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) + ucontrol->value.integer.value[0] = prtd->volume; + return 0; +} + +static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) { + rc = msm_pcm_set_volume(prtd, volume); + prtd->volume = volume; + } + return rc; +} + +static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + + dev_dbg(rtd->dev, "%s, Volume control add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, rtd->dai_link->be_id, + &volume_info); + if (ret < 0) { + pr_err("%s volume control failed ret %d\n", __func__, ret); + return ret; + } + kctl = volume_info->kctl; + kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; + kctl->tlv.p = msm_pcm_vol_gain; + return 0; +} + +static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + pr_debug("%s", __func__); + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + if (!substream->runtime) + return 0; + + prtd = substream->runtime->private_data; + if (prtd) { + prtd->set_channel_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + prtd->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } + return 0; +} + +static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + pr_debug("%s", __func__); + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + memset(ucontrol->value.integer.value, 0, + sizeof(ucontrol->value.integer.value)); + if (!substream->runtime) + return 0; /* no channels set */ + + prtd = substream->runtime->private_data; + + if (prtd && prtd->set_channel_map == true) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = + (int)prtd->channel_map[i]; + } else { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = 0; + } + + return 0; +} + +static int msm_pcm_add_chmap_control(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_chmap *chmap_info; + struct snd_kcontrol *kctl; + char device_num[12]; + int i, ret; + + pr_debug("%s, Channel map cntrl add\n", __func__); + ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, + PCM_FORMAT_MAX_NUM_CHANNEL, 0, + &chmap_info); + if (ret) + return ret; + + kctl = chmap_info->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + snprintf(device_num, sizeof(device_num), "%d", pcm->device); + strlcat(kctl->id.name, device_num, sizeof(kctl->id.name)); + pr_debug("%s, Overwriting channel map control name to: %s", + __func__, kctl->id.name); + kctl->put = msm_pcm_chmap_ctl_put; + kctl->get = msm_pcm_chmap_ctl_get; + return 0; +} + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + pr_debug("%s , register new control\n", __func__); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_pcm_add_chmap_control(rtd); + if (ret) { + pr_err("%s failed to add chmap cntls\n", __func__); + goto exit; + } + ret = msm_pcm_add_volume_control(rtd); + if (ret) { + pr_err("%s: Could not add pcm Volume Control %d\n", + __func__, ret); + } + pcm->nonatomic = true; +exit: + return ret; +} + + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .prepare = msm_pcm_prepare, + .copy = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .ioctl = msm_pcm_ioctl, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .mmap = msm_pcm_mmap, + .close = msm_pcm_close, +}; + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + struct msm_plat_data *pdata; + const char *latency_level; + int perf_mode = LOW_LATENCY_PCM_MODE; + + dev_dbg(&pdev->dev, "Pull mode driver probe\n"); + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,msm-pcm-low-latency")) { + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,latency-level", &latency_level); + if (!rc && !strcmp(latency_level, "ultra")) + perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->perf_mode = perf_mode; + + dev_set_drvdata(&pdev->dev, pdata); + + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + dev_dbg(&pdev->dev, "Pull mode driver register\n"); + rc = snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); + + if (rc) + dev_err(&pdev->dev, "Failed to register pull mode driver\n"); + + return rc; +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + struct msm_plat_data *pdata; + + dev_dbg(&pdev->dev, "Pull mode remove\n"); + pdata = dev_get_drvdata(&pdev->dev); + devm_kfree(&pdev->dev, pdata); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} +static const struct of_device_id msm_pcm_dt_match[] = { + {.compatible = "qcom,msm-pcm-dsp-noirq"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_dt_match); + +static struct platform_driver msm_pcm_driver_noirq = { + .driver = { + .name = "msm-pcm-dsp-noirq", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + return platform_driver_register(&msm_pcm_driver_noirq); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver_noirq); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("PCM NOIRQ module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c new file mode 100644 index 000000000000..8ec46522c22d --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -0,0 +1,1515 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" + +enum stream_state { + IDLE = 0, + STOPPED, + RUNNING, +}; + +static struct audio_locks the_locks; + +#define PCM_MASTER_VOL_MAX_STEPS 0x2000 +static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0, + PCM_MASTER_VOL_MAX_STEPS); + +struct snd_msm { + struct snd_card *card; + struct snd_pcm *pcm; +}; + +#define CMD_EOS_MIN_TIMEOUT_LENGTH 50 +#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50) +#define MAX_PB_COPY_RETRIES 3 + +static struct snd_pcm_hardware msm_pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * + CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * + PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000, 384000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, + void *priv_data) +{ + struct msm_audio *prtd = priv_data; + + WARN_ON(!prtd); + + pr_debug("%s: event %x\n", __func__, event); + + switch (event) { + case MSM_PCM_RT_EVT_BUF_RECFG: + q6asm_cmd(prtd->audio_client, CMD_PAUSE); + q6asm_cmd(prtd->audio_client, CMD_FLUSH); + q6asm_run(prtd->audio_client, 0, 0, 0); + /* fallthrough */ + default: + break; + } +} + +static void event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct msm_audio *prtd = priv; + struct snd_pcm_substream *substream = prtd->substream; + uint32_t *ptrmem = (uint32_t *)payload; + uint32_t idx = 0; + uint32_t size = 0; + uint8_t buf_index; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: { + pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n"); + pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem); + prtd->pcm_irq_pos += prtd->pcm_count; + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + atomic_inc(&prtd->out_count); + wake_up(&the_locks.write_wait); + if (!atomic_read(&prtd->start)) + break; + if (!prtd->mmap_flag || prtd->reset_event) + break; + if (q6asm_is_cpu_buf_avail_nolock(IN, + prtd->audio_client, + &size, &idx)) { + pr_debug("%s:writing %d bytes of buffer to dsp 2\n", + __func__, prtd->pcm_count); + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + } + break; + } + case ASM_DATA_EVENT_RENDERED_EOS: + pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n"); + clear_bit(CMD_EOS, &prtd->cmd_pending); + wake_up(&the_locks.eos_wait); + break; + case ASM_DATA_EVENT_READ_DONE_V2: { + pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n"); + buf_index = q6asm_get_buf_index_from_token(token); + pr_debug("%s: token=0x%08x buf_index=0x%08x\n", + __func__, token, buf_index); + prtd->in_frame_info[buf_index].size = payload[4]; + prtd->in_frame_info[buf_index].offset = payload[5]; + /* assume data size = 0 during flushing */ + if (prtd->in_frame_info[buf_index].size) { + prtd->pcm_irq_pos += + prtd->in_frame_info[buf_index].size; + pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos); + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + if (atomic_read(&prtd->in_count) <= prtd->periods) + atomic_inc(&prtd->in_count); + wake_up(&the_locks.read_wait); + if (prtd->mmap_flag && + q6asm_is_cpu_buf_avail_nolock(OUT, + prtd->audio_client, + &size, &idx) && + (substream->runtime->status->state == + SNDRV_PCM_STATE_RUNNING)) + q6asm_read_nolock(prtd->audio_client); + } else { + pr_debug("%s: reclaim flushed buf in_count %x\n", + __func__, atomic_read(&prtd->in_count)); + prtd->pcm_irq_pos += prtd->pcm_count; + if (prtd->mmap_flag) { + if (q6asm_is_cpu_buf_avail_nolock(OUT, + prtd->audio_client, + &size, &idx) && + (substream->runtime->status->state == + SNDRV_PCM_STATE_RUNNING)) + q6asm_read_nolock(prtd->audio_client); + } else { + atomic_inc(&prtd->in_count); + } + if (atomic_read(&prtd->in_count) == prtd->periods) { + pr_info("%s: reclaimed all bufs\n", __func__); + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + wake_up(&the_locks.read_wait); + } + } + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + if (substream->stream + != SNDRV_PCM_STREAM_PLAYBACK) { + atomic_set(&prtd->start, 1); + break; + } + if (prtd->mmap_flag) { + pr_debug("%s:writing %d bytes of buffer to dsp\n", + __func__, + prtd->pcm_count); + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, + 0, 0, NO_TIMESTAMP); + } else { + while (atomic_read(&prtd->out_needed)) { + pr_debug("%s:writing %d bytes of buffer to dsp\n", + __func__, + prtd->pcm_count); + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, + 0, 0, NO_TIMESTAMP); + atomic_dec(&prtd->out_needed); + wake_up(&the_locks.write_wait); + }; + } + atomic_set(&prtd->start, 1); + break; + default: + pr_debug("%s:Payload = [0x%x]stat[0x%x]\n", + __func__, payload[0], payload[1]); + break; + } + } + break; + case RESET_EVENTS: + pr_debug("%s RESET_EVENTS\n", __func__); + prtd->pcm_irq_pos += prtd->pcm_count; + atomic_inc(&prtd->out_count); + atomic_inc(&prtd->in_count); + prtd->reset_event = true; + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + wake_up(&the_locks.eos_wait); + wake_up(&the_locks.write_wait); + wake_up(&the_locks.read_wait); + break; + default: + pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + break; + } +} + +static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct msm_plat_data *pdata; + struct snd_pcm_hw_params *params; + int ret; + uint16_t bits_per_sample; + uint16_t sample_word_size; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data not populated\n", __func__); + return -EINVAL; + } + if (!prtd || !prtd->audio_client) { + pr_err("%s: private data null or audio client freed\n", + __func__); + return -EINVAL; + } + params = &soc_prtd->dpcm[substream->stream].hw_params; + + pr_debug("%s\n", __func__); + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + prtd->samp_rate = runtime->rate; + prtd->channel_mode = runtime->channels; + if (prtd->enabled) + return 0; + + prtd->audio_client->perf_mode = pdata->perf_mode; + pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + + ret = q6asm_open_write_v4(prtd->audio_client, + FORMAT_LINEAR_PCM, bits_per_sample); + + if (ret < 0) { + pr_err("%s: q6asm_open_write_v2 failed\n", __func__); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + + ret = q6asm_send_cal(prtd->audio_client); + if (ret < 0) + pr_debug("%s : Send cal failed : %d", __func__, ret); + + pr_debug("%s: session ID %d\n", __func__, + prtd->audio_client->session); + prtd->session_id = prtd->audio_client->session; + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream); + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + + ret = q6asm_media_format_block_multi_ch_pcm_v4( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, bits_per_sample, + sample_word_size, ASM_LITTLE_ENDIAN, + DEFAULT_QF); + if (ret < 0) + pr_info("%s: CMD Format block failed\n", __func__); + + atomic_set(&prtd->out_count, runtime->periods); + + prtd->enabled = 1; + prtd->cmd_pending = 0; + prtd->cmd_interrupt = 0; + + return 0; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_plat_data *pdata; + struct snd_pcm_hw_params *params; + struct msm_pcm_routing_evt event; + int ret = 0; + int i = 0; + uint16_t bits_per_sample = 16; + uint16_t sample_word_size; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data not populated\n", __func__); + return -EINVAL; + } + if (!prtd || !prtd->audio_client) { + pr_err("%s: private data null or audio client freed\n", + __func__); + return -EINVAL; + } + + if (prtd->enabled == IDLE) { + pr_debug("%s:perf_mode=%d periods=%d\n", __func__, + pdata->perf_mode, runtime->periods); + params = &soc_prtd->dpcm[substream->stream].hw_params; + if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) || + (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE)) + bits_per_sample = 24; + else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + /* ULL mode is not supported in capture path */ + if (pdata->perf_mode == LEGACY_PCM_MODE) + prtd->audio_client->perf_mode = LEGACY_PCM_MODE; + else + prtd->audio_client->perf_mode = LOW_LATENCY_PCM_MODE; + + pr_debug("%s Opening %d-ch PCM read stream, perf_mode %d\n", + __func__, params_channels(params), + prtd->audio_client->perf_mode); + + ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM, + bits_per_sample); + if (ret < 0) { + pr_err("%s: q6asm_open_read failed\n", __func__); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + + ret = q6asm_send_cal(prtd->audio_client); + if (ret < 0) + pr_debug("%s : Send cal failed : %d", __func__, ret); + + pr_debug("%s: session ID %d\n", + __func__, prtd->audio_client->session); + prtd->session_id = prtd->audio_client->session; + event.event_func = msm_pcm_route_event_handler; + event.priv_data = (void *) prtd; + ret = msm_pcm_routing_reg_phy_stream_v2( + soc_prtd->dai_link->be_id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream, + event); + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + } + + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + prtd->samp_rate = runtime->rate; + prtd->channel_mode = runtime->channels; + + if (prtd->enabled == IDLE || prtd->enabled == STOPPED) { + for (i = 0; i < runtime->periods; i++) + q6asm_read(prtd->audio_client); + prtd->periods = runtime->periods; + } + + if (prtd->enabled != IDLE) + return 0; + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + + pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n", + __func__, prtd->samp_rate, prtd->channel_mode, + bits_per_sample, sample_word_size); + ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client, + prtd->samp_rate, + prtd->channel_mode, + bits_per_sample, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); + if (ret < 0) + pr_debug("%s: cmd cfg pcm was block failed", __func__); + + prtd->enabled = RUNNING; + + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: Trigger start\n", __func__); + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + atomic_set(&prtd->start, 0); + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + prtd->enabled = STOPPED; + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + break; + } + /* pending CMD_EOS isn't expected */ + WARN_ON_ONCE(test_bit(CMD_EOS, &prtd->cmd_pending)); + set_bit(CMD_EOS, &prtd->cmd_pending); + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + if (ret) + clear_bit(CMD_EOS, &prtd->cmd_pending); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n"); + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->start, 0); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd; + int ret = 0; + + prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + prtd->substream = substream; + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)event_handler, prtd); + if (!prtd->audio_client) { + pr_info("%s: Could not allocate memory\n", __func__); + kfree(prtd); + return -ENOMEM; + } + + prtd->audio_client->dev = soc_prtd->platform->dev; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = msm_pcm_hardware_playback; + + /* Capture path */ + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + runtime->hw = msm_pcm_hardware_capture; + else { + pr_err("Invalid Stream type %d\n", substream->stream); + return -EINVAL; + } + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, + PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE, + CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE); + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret < 0) { + pr_err("constraint for period bytes step ret = %d\n", + ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret < 0) { + pr_err("constraint for buffer bytes step ret = %d\n", + ret); + } + + prtd->enabled = IDLE; + prtd->dsp_cnt = 0; + prtd->set_channel_map = false; + prtd->reset_event = false; + runtime->private_data = prtd; + + return 0; +} + +static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + int fbytes = 0; + int xfer = 0; + char *bufptr = NULL; + void *data = NULL; + uint32_t idx = 0; + uint32_t size = 0; + uint32_t retries = 0; + + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + fbytes = frames_to_bytes(runtime, frames); + pr_debug("%s: prtd->out_count = %d\n", + __func__, atomic_read(&prtd->out_count)); + + while ((fbytes > 0) && (retries < MAX_PB_COPY_RETRIES)) { + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET before wait\n", + __func__); + return -ENETRESET; + } + + ret = wait_event_timeout(the_locks.write_wait, + (atomic_read(&prtd->out_count)), 5 * HZ); + if (!ret) { + pr_err("%s: wait_event_timeout failed\n", __func__); + ret = -ETIMEDOUT; + goto fail; + } + ret = 0; + + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET after wait\n", + __func__); + return -ENETRESET; + } + + if (!atomic_read(&prtd->out_count)) { + pr_err("%s: pcm stopped out_count 0\n", __func__); + return 0; + } + + data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, + &idx); + if (data == NULL) { + retries++; + continue; + } else { + retries = 0; + } + + if (fbytes > size) + xfer = size; + else + xfer = fbytes; + + bufptr = data; + if (bufptr) { + pr_debug("%s:fbytes =%d: xfer=%d size=%d\n", + __func__, fbytes, xfer, size); + if (copy_from_user(bufptr, buf, xfer)) { + ret = -EFAULT; + pr_err("%s: copy_from_user failed\n", + __func__); + q6asm_cpu_buf_release(IN, prtd->audio_client); + goto fail; + } + buf += xfer; + fbytes -= xfer; + pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, + xfer); + if (atomic_read(&prtd->start)) { + pr_debug("%s:writing %d bytes of buffer to dsp\n", + __func__, xfer); + ret = q6asm_write(prtd->audio_client, xfer, + 0, 0, NO_TIMESTAMP); + if (ret < 0) { + ret = -EFAULT; + q6asm_cpu_buf_release(IN, + prtd->audio_client); + goto fail; + } + } else + atomic_inc(&prtd->out_needed); + atomic_dec(&prtd->out_count); + } + } +fail: + if (retries >= MAX_PB_COPY_RETRIES) + ret = -ENOMEM; + + return ret; +} + +static int msm_pcm_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + uint32_t timeout; + int dir = 0; + int ret = 0; + + pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending); + + if (prtd->audio_client) { + dir = IN; + + /* determine timeout length */ + if (runtime->frame_bits == 0 || runtime->rate == 0) { + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } else { + timeout = (runtime->period_size * + CMD_EOS_TIMEOUT_MULTIPLIER) / + ((runtime->frame_bits / 8) * + runtime->rate); + if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH) + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } + pr_debug("%s: CMD_EOS timeout is %d\n", __func__, timeout); + + ret = wait_event_timeout(the_locks.eos_wait, + !test_bit(CMD_EOS, &prtd->cmd_pending), + timeout); + if (!ret) + pr_err("%s: CMD_EOS failed, cmd_pending 0x%lx\n", + __func__, prtd->cmd_pending); + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_audio_client_buf_free_contiguous(dir, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + } + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, + SNDRV_PCM_STREAM_PLAYBACK); + kfree(prtd); + return 0; +} + +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t hwoff, void __user *buf, + snd_pcm_uframes_t frames) +{ + int ret = 0; + int fbytes = 0; + int xfer; + char *bufptr; + void *data = NULL; + static uint32_t idx; + static uint32_t size; + uint32_t offset = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = substream->runtime->private_data; + + + pr_debug("%s\n", __func__); + fbytes = frames_to_bytes(runtime, frames); + + pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr); + pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr); + pr_debug("avail_min %d\n", (int)runtime->control->avail_min); + + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET before wait\n", __func__); + return -ENETRESET; + } + ret = wait_event_timeout(the_locks.read_wait, + (atomic_read(&prtd->in_count)), 5 * HZ); + if (!ret) { + pr_debug("%s: wait_event_timeout failed\n", __func__); + goto fail; + } + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET after wait\n", __func__); + return -ENETRESET; + } + if (!atomic_read(&prtd->in_count)) { + pr_debug("%s: pcm stopped in_count 0\n", __func__); + return 0; + } + pr_debug("Checking if valid buffer is available...%pK\n", + data); + data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx); + bufptr = data; + pr_debug("Size = %d\n", size); + pr_debug("fbytes = %d\n", fbytes); + pr_debug("idx = %d\n", idx); + if (bufptr) { + xfer = fbytes; + if (xfer > size) + xfer = size; + offset = prtd->in_frame_info[idx].offset; + pr_debug("Offset value = %d\n", offset); + if (copy_to_user(buf, bufptr+offset, xfer)) { + pr_err("Failed to copy buf to user\n"); + ret = -EFAULT; + q6asm_cpu_buf_release(OUT, prtd->audio_client); + goto fail; + } + fbytes -= xfer; + size -= xfer; + prtd->in_frame_info[idx].offset += xfer; + pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n", + __func__, fbytes, size, xfer); + pr_debug(" Sending next buffer to dsp\n"); + memset(&prtd->in_frame_info[idx], 0, + sizeof(struct msm_audio_in_frame_info)); + atomic_dec(&prtd->in_count); + ret = q6asm_read(prtd->audio_client); + if (ret < 0) { + pr_err("q6asm read failed\n"); + ret = -EFAULT; + q6asm_cpu_buf_release(OUT, prtd->audio_client); + goto fail; + } + } else + pr_err("No valid buffer\n"); + + pr_debug("Returning from capture_copy... %d\n", ret); +fail: + return ret; +} + +static int msm_pcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + int dir = OUT; + + pr_debug("%s\n", __func__); + if (prtd->audio_client) { + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_audio_client_buf_free_contiguous(dir, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + } + + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, + SNDRV_PCM_STREAM_CAPTURE); + kfree(prtd); + + return 0; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames); + return ret; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_close(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_close(substream); + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + return ret; +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + if (prtd->pcm_irq_pos >= prtd->pcm_size) + prtd->pcm_irq_pos = 0; + + pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos); + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static int msm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + struct audio_port_data *apd = ac->port; + struct audio_buffer *ab; + int dir = -1; + + prtd->mmap_flag = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ab = &(apd[dir].buf[0]); + + return msm_audio_ion_mmap(ab, vma); +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct audio_buffer *buf; + int dir, ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ret = q6asm_audio_client_buf_alloc_contiguous(dir, + prtd->audio_client, + (params_buffer_bytes(params) / params_periods(params)), + params_periods(params)); + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", + ret); + return -ENOMEM; + } + buf = prtd->audio_client->port[dir].buf; + if (buf == NULL || buf[0].data == NULL) + return -ENOMEM; + + pr_debug("%s:buf = %pK\n", __func__, buf); + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf[0].data; + dma_buf->addr = buf[0].phys; + dma_buf->bytes = params_buffer_bytes(params); + if (!dma_buf->area) + return -ENOMEM; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .copy = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .mmap = msm_pcm_mmap, +}; + +static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume) +{ + int rc = 0; + + if (prtd && prtd->audio_client) { + pr_debug("%s: channels %d volume 0x%x\n", __func__, + prtd->channel_mode, volume); + rc = q6asm_set_volume(prtd->audio_client, volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + } + } + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + + pr_debug("%s\n", __func__); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) + ucontrol->value.integer.value[0] = prtd->volume; + return 0; +} + +static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) { + rc = msm_pcm_set_volume(prtd, volume); + prtd->volume = volume; + } + return rc; +} + +static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + + dev_dbg(rtd->dev, "%s, Volume control add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, rtd->dai_link->be_id, + &volume_info); + if (ret < 0) { + pr_err("%s volume control failed ret %d\n", __func__, ret); + return ret; + } + kctl = volume_info->kctl; + kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; + kctl->tlv.p = msm_pcm_vol_gain; + return 0; +} + +static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + pr_debug("%s", __func__); + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + if (!substream->runtime) + return 0; + + prtd = substream->runtime->private_data; + if (prtd) { + prtd->set_channel_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + prtd->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } + return 0; +} + +static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + pr_debug("%s", __func__); + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + memset(ucontrol->value.integer.value, 0, + sizeof(ucontrol->value.integer.value)); + if (!substream->runtime) + return 0; /* no channels set */ + + prtd = substream->runtime->private_data; + + if (prtd && prtd->set_channel_map == true) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = + (int)prtd->channel_map[i]; + } else { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = 0; + } + + return 0; +} + +static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_chmap *chmap_info; + struct snd_kcontrol *kctl; + char device_num[12]; + int i, ret = 0; + + pr_debug("%s, Channel map cntrl add\n", __func__); + ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, + PCM_FORMAT_MAX_NUM_CHANNEL, 0, + &chmap_info); + if (ret < 0) { + pr_err("%s, channel map cntrl add failed\n", __func__); + return ret; + } + kctl = chmap_info->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + snprintf(device_num, sizeof(device_num), "%d", pcm->device); + strlcat(kctl->id.name, device_num, sizeof(kctl->id.name)); + pr_debug("%s, Overwriting channel map control name to: %s\n", + __func__, kctl->id.name); + kctl->put = msm_pcm_chmap_ctl_put; + kctl->get = msm_pcm_chmap_ctl_get; + return 0; +} + +static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int app_type; + int acdb_dev_id; + int sample_rate = 48000; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + return -EINVAL; + } + + app_type = ucontrol->value.integer.value[0]; + acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n", + __func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_RX); + msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, + acdb_dev_id, sample_rate, SESSION_TYPE_RX); + + return 0; +} + +static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int ret = 0; + int app_type; + int acdb_dev_id; + int sample_rate; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_RX, + &app_type, &acdb_dev_id, &sample_rate); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = app_type; + ucontrol->value.integer.value[1] = acdb_dev_id; + ucontrol->value.integer.value[2] = sample_rate; + pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, SESSION_TYPE_RX, + app_type, acdb_dev_id, sample_rate); +done: + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int app_type; + int acdb_dev_id; + int sample_rate = 48000; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + return -EINVAL; + } + + app_type = ucontrol->value.integer.value[0]; + acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n", + __func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_TX); + msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, + acdb_dev_id, sample_rate, SESSION_TYPE_TX); + + return 0; +} + +static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int ret = 0; + int app_type; + int acdb_dev_id; + int sample_rate; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_TX, + &app_type, &acdb_dev_id, &sample_rate); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = app_type; + ucontrol->value.integer.value[1] = acdb_dev_id; + ucontrol->value.integer.value[2] = sample_rate; + pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, SESSION_TYPE_TX, + app_type, acdb_dev_id, sample_rate); +done: + return ret; +} + +static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_usr *app_type_info; + struct snd_kcontrol *kctl; + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ctl_len = strlen(playback_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Playback app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, ctl_len, rtd->dai_link->be_id, + &app_type_info); + if (ret < 0) { + pr_err("%s: playback app type cntrl add failed: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_playback_app_type_cfg_ctl_put; + kctl->get = msm_pcm_playback_app_type_cfg_ctl_get; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ctl_len = strlen(capture_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Capture app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->be_id, + &app_type_info); + if (ret < 0) { + pr_err("%s: capture app type cntrl add failed: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_capture_app_type_cfg_ctl_put; + kctl->get = msm_pcm_capture_app_type_cfg_ctl_get; + } + + return 0; +} + +static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + ret = msm_pcm_add_chmap_controls(rtd); + if (ret) + pr_err("%s: pcm add controls failed:%d\n", __func__, ret); + ret = msm_pcm_add_app_type_controls(rtd); + if (ret) + pr_err("%s: pcm add app type controls failed:%d\n", + __func__, ret); + return ret; +} + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_pcm_add_controls(rtd); + if (ret) { + pr_err("%s, kctl add failed:%d\n", __func__, ret); + return ret; + } + + ret = msm_pcm_add_volume_control(rtd); + if (ret) + pr_err("%s: Could not add pcm Volume Control %d\n", + __func__, ret); + + return ret; +} + +static snd_pcm_sframes_t msm_pcm_delay_blk(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + snd_pcm_sframes_t frames; + int ret; + + ret = q6asm_get_path_delay(prtd->audio_client); + if (ret) { + pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret); + return 0; + } + + /* convert microseconds to frames */ + frames = ac->path_delay / 1000 * runtime->rate / 1000; + + /* also convert the remainder from the initial division */ + frames += ac->path_delay % 1000 * runtime->rate / 1000000; + + /* overcompensate for the loss of precision (empirical) */ + frames += 2; + + return frames; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .delay_blk = msm_pcm_delay_blk, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + int id; + struct msm_plat_data *pdata; + const char *latency_level; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-pcm-dsp-id", &id); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-pcm-dsp-id missing in DT node\n", + __func__); + return rc; + } + + pdata = kzalloc(sizeof(struct msm_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,msm-pcm-low-latency")) { + + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + rc = of_property_read_string(pdev->dev.of_node, + "qcom,latency-level", &latency_level); + if (!rc) { + if (!strcmp(latency_level, "ultra")) + pdata->perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + else if (!strcmp(latency_level, "ull-pp")) + pdata->perf_mode = + ULL_POST_PROCESSING_PCM_MODE; + } + } else { + pdata->perf_mode = LEGACY_PCM_MODE; + } + + dev_set_drvdata(&pdev->dev, pdata); + + + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + struct msm_plat_data *pdata; + + pdata = dev_get_drvdata(&pdev->dev); + kfree(pdata); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} +static const struct of_device_id msm_pcm_dt_match[] = { + {.compatible = "qcom,msm-pcm-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + init_waitqueue_head(&the_locks.enable_wait); + init_waitqueue_head(&the_locks.eos_wait); + init_waitqueue_head(&the_locks.write_wait); + init_waitqueue_head(&the_locks.read_wait); + + return platform_driver_register(&msm_pcm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h new file mode 100644 index 000000000000..5290d34a365a --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org. + */ + +#ifndef _MSM_PCM_H +#define _MSM_PCM_H +#include +#include + + + +/* Support unconventional sample rates 12000, 24000 as well */ +#define USE_RATE \ + (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) + +extern int copy_count; + +struct buffer { + void *data; + unsigned int size; + unsigned int used; + unsigned int addr; +}; + +struct buffer_rec { + void *data; + unsigned int size; + unsigned int read; + unsigned int addr; +}; + +struct audio_locks { + spinlock_t event_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t eos_wait; + wait_queue_head_t enable_wait; + wait_queue_head_t flush_wait; +}; + +struct msm_audio_in_frame_info { + uint32_t size; + uint32_t offset; +}; + +#define PLAYBACK_MIN_NUM_PERIODS 2 +#define PLAYBACK_MAX_NUM_PERIODS 8 +#define PLAYBACK_MAX_PERIOD_SIZE 122880 +#define PLAYBACK_MIN_PERIOD_SIZE 128 +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 122880 +#define CAPTURE_MIN_PERIOD_SIZE 320 + +struct msm_audio { + struct snd_pcm_substream *substream; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_irq_pos; /* IRQ position */ + uint16_t source; /* Encoding source bit mask */ + + struct audio_client *audio_client; + + uint16_t session_id; + + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t dsp_cnt; + + int abort; /* set when error, like sample rate mismatch */ + + bool reset_event; + int enabled; + int close_ack; + int cmd_ack; + /* + * cmd_ack doesn't tell if paticular command has been sent so can't + * determine if it needs to wait for completion. + * Use cmd_pending instead when checking whether a command is been + * sent or not. + */ + unsigned long cmd_pending; + atomic_t start; + atomic_t stop; + atomic_t out_count; + atomic_t in_count; + atomic_t out_needed; + atomic_t eos; + int out_head; + int periods; + int mmap_flag; + atomic_t pending_buffer; + bool set_channel_map; + char channel_map[8]; + int cmd_interrupt; + bool meta_data_mode; + uint32_t volume; + /* array of frame info */ + struct msm_audio_in_frame_info in_frame_info[CAPTURE_MAX_NUM_PERIODS]; +}; + +struct output_meta_data_st { + uint32_t meta_data_length; + uint32_t frame_size; + uint32_t timestamp_lsw; + uint32_t timestamp_msw; + uint32_t reserved[12]; +}; + +struct msm_plat_data { + int perf_mode; +}; + +#endif /*_MSM_PCM_H*/ diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c new file mode 100644 index 000000000000..dd3d73d1f8ab --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-devdep.h" +#include "msm-ds2-dap-config.h" + +#ifdef CONFIG_SND_HWDEP +static int msm_pcm_routing_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ + pr_debug("%s\n", __func__); + msm_ds2_dap_update_port_parameters(hw, file, true); + return 0; +} + +static int msm_pcm_routing_hwdep_release(struct snd_hwdep *hw, + struct file *file) +{ + pr_debug("%s\n", __func__); + msm_ds2_dap_update_port_parameters(hw, file, false); + return 0; +} + +static int msm_pcm_routing_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + pr_debug("%s:cmd %x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: + msm_pcm_routing_acquire_lock(); + ret = msm_ds2_dap_ioctl(hw, file, cmd, argp); + msm_pcm_routing_release_lock(); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: + ret = msm_ds2_dap_ioctl(hw, file, cmd, argp); + break; + case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: + case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: + case DTS_EAGLE_IOCTL_GET_PARAM: + case DTS_EAGLE_IOCTL_SET_PARAM: + case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: + case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: + case DTS_EAGLE_IOCTL_GET_LICENSE: + case DTS_EAGLE_IOCTL_SET_LICENSE: + case DTS_EAGLE_IOCTL_SEND_LICENSE: + case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: + ret = msm_dts_eagle_ioctl(cmd, arg); + if (ret == -EPERM) { + pr_err("%s called with invalid control 0x%X\n", + __func__, cmd); + ret = -EINVAL; + } + break; + default: + pr_err("%s called with invalid control 0x%X\n", __func__, cmd); + ret = -EINVAL; + break; + } + return ret; +} + +void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm) +{ + pr_debug("%s\n", __func__); + msm_dts_eagle_pcm_free(pcm); +} + +#ifdef CONFIG_COMPAT +static int msm_pcm_routing_hwdep_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + pr_debug("%s:cmd %x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32: + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32: + msm_pcm_routing_acquire_lock(); + ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp); + msm_pcm_routing_release_lock(); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32: + ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp); + break; + case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32: + case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32: + case DTS_EAGLE_IOCTL_GET_PARAM32: + case DTS_EAGLE_IOCTL_SET_PARAM32: + case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32: + case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32: + case DTS_EAGLE_IOCTL_GET_LICENSE32: + case DTS_EAGLE_IOCTL_SET_LICENSE32: + case DTS_EAGLE_IOCTL_SEND_LICENSE32: + case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32: + ret = msm_dts_eagle_compat_ioctl(cmd, arg); + if (ret == -EPERM) { + pr_err("%s called with invalid control 0x%X\n", + __func__, cmd); + ret = -EINVAL; + } + break; + default: + pr_err("%s called with invalid control 0x%X\n", __func__, cmd); + ret = -EINVAL; + break; + } + return ret; +} +#endif + +int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, + struct msm_pcm_routing_bdai_data *msm_bedais) +{ + struct snd_hwdep *hwdep; + struct snd_soc_dai_link *dai_link = runtime->dai_link; + int rc; + + if (dai_link->be_id < 0 || + dai_link->be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s:be_id %d invalid index\n", + __func__, dai_link->be_id); + return -EINVAL; + } + pr_debug("%s be_id %d\n", __func__, dai_link->be_id); + rc = snd_hwdep_new(runtime->card->snd_card, + msm_bedais[dai_link->be_id].name, + dai_link->be_id, &hwdep); + if (hwdep == NULL) { + pr_err("%s: hwdep intf failed to create %s- hwdep NULL\n", + __func__, msm_bedais[dai_link->be_id].name); + return rc; + } + if (IS_ERR_VALUE(rc)) { + pr_err("%s: hwdep intf failed to create %s rc %d\n", __func__, + msm_bedais[dai_link->be_id].name, rc); + return rc; + } + + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; + hwdep->private_data = &msm_bedais[dai_link->be_id]; + hwdep->ops.open = msm_pcm_routing_hwdep_open; + hwdep->ops.ioctl = msm_pcm_routing_hwdep_ioctl; + hwdep->ops.release = msm_pcm_routing_hwdep_release; +#ifdef CONFIG_COMPAT + hwdep->ops.ioctl_compat = msm_pcm_routing_hwdep_compat_ioctl; +#endif + return msm_dts_eagle_pcm_new(runtime); +} +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h new file mode 100644 index 000000000000..d93d04835d2e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2014-2015, 2017 The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_PCM_ROUTING_DEVDEP_H_ +#define _MSM_PCM_ROUTING_DEVDEP_H_ + +#include +#include "msm-pcm-routing-v2.h" + +#ifdef CONFIG_SND_HWDEP +int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, + struct msm_pcm_routing_bdai_data *msm_bedais); +void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm); +#else +static inline int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, + struct msm_pcm_routing_bdai_data *msm_bedais) +{ + return 0; +} + +static inline void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm) +{ +} +#endif +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c new file mode 100644 index 000000000000..a8cbdbee525d --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -0,0 +1,11931 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-v2.h" +#include "msm-pcm-routing-devdep.h" +#include "msm-qti-pp-config.h" +#include "msm-dts-srs-tm-config.h" +#include "msm-dolby-dap-config.h" +#include "msm-ds2-dap-config.h" +#include "q6voice.h" +#include "sound/q6lsm.h" + +static int get_cal_path(int path_type); + +static struct mutex routing_lock; + +static struct cal_type_data *cal_data; + +static int fm_switch_enable; +static int hfp_switch_enable; +static int pri_mi2s_switch_enable; +static int sec_mi2s_switch_enable; +static int tert_mi2s_switch_enable; +static int quat_mi2s_switch_enable; +static int fm_pcmrx_switch_enable; +static int lsm_mux_slim_port; +static int slim0_rx_aanc_fb_port; +static int msm_route_ec_ref_rx; +static uint32_t voc_session_id = ALL_SESSION_VSID; +static int msm_route_ext_ec_ref; +static bool is_custom_stereo_on; +static bool is_ds2_on; + +enum { + MADNONE, + MADAUDIO, + MADBEACON, + MADULTRASOUND, + MADSWAUDIO, +}; + +#define SLIMBUS_0_TX_TEXT "SLIMBUS_0_TX" +#define SLIMBUS_1_TX_TEXT "SLIMBUS_1_TX" +#define SLIMBUS_2_TX_TEXT "SLIMBUS_2_TX" +#define SLIMBUS_3_TX_TEXT "SLIMBUS_3_TX" +#define SLIMBUS_4_TX_TEXT "SLIMBUS_4_TX" +#define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX" +#define TERT_MI2S_TX_TEXT "TERT_MI2S_TX" +#define LSM_FUNCTION_TEXT "LSM Function" +static const char * const mad_audio_mux_text[] = { + "None", + SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT, + SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT, + TERT_MI2S_TX_TEXT +}; + +struct msm_pcm_route_bdai_pp_params { + u16 port_id; /* AFE port ID */ + unsigned long pp_params_config; + bool mute_on; + int latency; +}; + +static struct msm_pcm_route_bdai_pp_params + msm_bedais_pp_params[MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX] = { + {HDMI_RX, 0, 0, 0}, + {DISPLAY_PORT_RX, 0, 0, 0}, +}; + +static int msm_routing_send_device_pp_params(int port_id, int copp_idx); + +static int msm_routing_get_bit_width(unsigned int format) +{ + int bit_width; + + switch (format) { + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_width = 16; + } + return bit_width; +} + +static bool msm_is_resample_needed(int input_sr, int output_sr) +{ + bool rc = false; + + if (input_sr != output_sr) + rc = true; + + pr_debug("perform resampling (%s) for copp rate (%d)afe rate (%d)", + (rc ? "oh yes" : "not really"), + input_sr, output_sr); + + return rc; +} + +static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology, + int channels) +{ + int rc = 0; + + switch (topology) { + case SRS_TRUMEDIA_TOPOLOGY_ID: + pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__); + msm_dts_srs_tm_init(port_id, copp_idx); + break; + case DS2_ADM_COPP_TOPOLOGY_ID: + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY %d\n", + __func__, DS2_ADM_COPP_TOPOLOGY_ID); + rc = msm_ds2_dap_init(port_id, copp_idx, channels, + is_custom_stereo_on); + if (rc < 0) + pr_err("%s: DS2 topo_id 0x%x, port %d, CS %d rc %d\n", + __func__, topology, port_id, + is_custom_stereo_on, rc); + break; + case DOLBY_ADM_COPP_TOPOLOGY_ID: + if (is_ds2_on) { + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY\n", __func__); + rc = msm_ds2_dap_init(port_id, copp_idx, channels, + is_custom_stereo_on); + if (rc < 0) + pr_err("%s:DS2 topo_id 0x%x, port %d, rc %d\n", + __func__, topology, port_id, rc); + } else { + pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__); + rc = msm_dolby_dap_init(port_id, copp_idx, channels, + is_custom_stereo_on); + if (rc < 0) + pr_err("%s: DS1 topo_id 0x%x, port %d, rc %d\n", + __func__, topology, port_id, rc); + } + break; + case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX: + pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__); + msm_dts_eagle_init_post(port_id, copp_idx); + break; + case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE: + pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); + rc = msm_qti_pp_asphere_init(port_id, copp_idx); + if (rc < 0) + pr_err("%s: topo_id 0x%x, port %d, copp %d, rc %d\n", + __func__, topology, port_id, copp_idx, rc); + break; + default: + /* custom topology specific feature param handlers */ + break; + } +} + +static void msm_pcm_routing_deinit_pp(int port_id, int topology) +{ + switch (topology) { + case SRS_TRUMEDIA_TOPOLOGY_ID: + pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__); + msm_dts_srs_tm_deinit(port_id); + break; + case DS2_ADM_COPP_TOPOLOGY_ID: + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID %d\n", + __func__, DS2_ADM_COPP_TOPOLOGY_ID); + msm_ds2_dap_deinit(port_id); + break; + case DOLBY_ADM_COPP_TOPOLOGY_ID: + if (is_ds2_on) { + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID\n", __func__); + msm_ds2_dap_deinit(port_id); + } else { + pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__); + msm_dolby_dap_deinit(port_id); + } + break; + case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX: + pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__); + msm_dts_eagle_deinit_post(port_id, topology); + break; + case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE: + pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); + msm_qti_pp_asphere_deinit(port_id); + break; + default: + /* custom topology specific feature deinit handlers */ + break; + } +} + +static void msm_pcm_routng_cfg_matrix_map_pp(struct route_payload payload, + int path_type, int perf_mode) +{ + int itr = 0, rc = 0; + + if ((path_type == ADM_PATH_PLAYBACK) && + (perf_mode == LEGACY_PCM_MODE) && + is_custom_stereo_on) { + for (itr = 0; itr < payload.num_copps; itr++) { + if ((payload.port_id[itr] != SLIMBUS_0_RX) && + (payload.port_id[itr] != RT_PROXY_PORT_001_RX)) { + continue; + } + + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd( + payload.port_id[itr], + payload.copp_idx[itr], + payload.session_id, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE); + if (rc < 0) + pr_err("%s: err setting custom stereo\n", + __func__); + } + } +} + +#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID +struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = { + { PRIMARY_I2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_PRI_I2S_RX}, + { PRIMARY_I2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_PRI_I2S_TX}, + { SLIMBUS_0_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_0_RX}, + { SLIMBUS_0_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_0_TX}, + { HDMI_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_HDMI}, + { INT_BT_SCO_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_BT_SCO_RX}, + { INT_BT_SCO_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_BT_SCO_TX}, + { INT_FM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_FM_RX}, + { INT_FM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_FM_TX}, + { RT_PROXY_PORT_001_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_AFE_PCM_RX}, + { RT_PROXY_PORT_001_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_AFE_PCM_TX}, + { AFE_PORT_ID_PRIMARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_AUXPCM_RX}, + { AFE_PORT_ID_PRIMARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_AUXPCM_TX}, + { VOICE_PLAYBACK_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_VOICE_PLAYBACK_TX}, + { VOICE2_PLAYBACK_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_VOICE2_PLAYBACK_TX}, + { VOICE_RECORD_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INCALL_RECORD_RX}, + { VOICE_RECORD_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INCALL_RECORD_TX}, + { MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_MI2S_RX}, + { MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_MI2S_TX}, + { SECONDARY_I2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SEC_I2S_RX}, + { SLIMBUS_1_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_1_RX}, + { SLIMBUS_1_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_1_TX}, + { SLIMBUS_2_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_2_RX}, + { SLIMBUS_2_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_2_TX}, + { SLIMBUS_3_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_3_RX}, + { SLIMBUS_3_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_3_TX}, + { SLIMBUS_4_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_4_RX}, + { SLIMBUS_4_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_4_TX}, + { SLIMBUS_5_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_5_RX}, + { SLIMBUS_5_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_5_TX}, + { SLIMBUS_6_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_6_RX}, + { SLIMBUS_6_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_6_TX}, + { SLIMBUS_7_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_7_RX}, + { SLIMBUS_7_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_7_TX}, + { SLIMBUS_8_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_RX}, + { SLIMBUS_8_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_TX}, + { SLIMBUS_EXTPROC_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_STUB_RX}, + { SLIMBUS_EXTPROC_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_STUB_TX}, + { SLIMBUS_EXTPROC_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_STUB_1_TX}, + { AFE_PORT_ID_QUATERNARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_MI2S_RX}, + { AFE_PORT_ID_QUATERNARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_MI2S_TX}, + { AFE_PORT_ID_SECONDARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_MI2S_RX}, + { AFE_PORT_ID_SECONDARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_MI2S_TX}, + { AFE_PORT_ID_PRIMARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_MI2S_RX}, + { AFE_PORT_ID_PRIMARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_MI2S_TX}, + { AFE_PORT_ID_TERTIARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_MI2S_RX}, + { AFE_PORT_ID_TERTIARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_MI2S_TX}, + { AUDIO_PORT_ID_I2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_AUDIO_I2S_RX}, + { AFE_PORT_ID_SECONDARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_AUXPCM_RX}, + { AFE_PORT_ID_SECONDARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_AUXPCM_TX}, + { AFE_PORT_ID_SPDIF_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SPDIF_RX}, + { AFE_PORT_ID_SECONDARY_MI2S_RX_SD1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_MI2S_RX_SD1}, + { AFE_PORT_ID_QUINARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUIN_MI2S_RX}, + { AFE_PORT_ID_QUINARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUIN_MI2S_TX}, + { AFE_PORT_ID_SENARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SENARY_MI2S_TX}, + { AFE_PORT_ID_PRIMARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_0}, + { AFE_PORT_ID_PRIMARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_0}, + { AFE_PORT_ID_PRIMARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_1}, + { AFE_PORT_ID_PRIMARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_1}, + { AFE_PORT_ID_PRIMARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_2}, + { AFE_PORT_ID_PRIMARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_2}, + { AFE_PORT_ID_PRIMARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_3}, + { AFE_PORT_ID_PRIMARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_3}, + { AFE_PORT_ID_PRIMARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_4}, + { AFE_PORT_ID_PRIMARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_4}, + { AFE_PORT_ID_PRIMARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_5}, + { AFE_PORT_ID_PRIMARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_5}, + { AFE_PORT_ID_PRIMARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_6}, + { AFE_PORT_ID_PRIMARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_6}, + { AFE_PORT_ID_PRIMARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_7}, + { AFE_PORT_ID_PRIMARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_7}, + { AFE_PORT_ID_SECONDARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_0}, + { AFE_PORT_ID_SECONDARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_0}, + { AFE_PORT_ID_SECONDARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_1}, + { AFE_PORT_ID_SECONDARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_1}, + { AFE_PORT_ID_SECONDARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_2}, + { AFE_PORT_ID_SECONDARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_2}, + { AFE_PORT_ID_SECONDARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_3}, + { AFE_PORT_ID_SECONDARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_3}, + { AFE_PORT_ID_SECONDARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_4}, + { AFE_PORT_ID_SECONDARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_4}, + { AFE_PORT_ID_SECONDARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_5}, + { AFE_PORT_ID_SECONDARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_5}, + { AFE_PORT_ID_SECONDARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_6}, + { AFE_PORT_ID_SECONDARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_6}, + { AFE_PORT_ID_SECONDARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_7}, + { AFE_PORT_ID_SECONDARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_7}, + { AFE_PORT_ID_TERTIARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_0}, + { AFE_PORT_ID_TERTIARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_0}, + { AFE_PORT_ID_TERTIARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_1}, + { AFE_PORT_ID_TERTIARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_1}, + { AFE_PORT_ID_TERTIARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_2}, + { AFE_PORT_ID_TERTIARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_2}, + { AFE_PORT_ID_TERTIARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_3}, + { AFE_PORT_ID_TERTIARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_3}, + { AFE_PORT_ID_TERTIARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_4}, + { AFE_PORT_ID_TERTIARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_4}, + { AFE_PORT_ID_TERTIARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_5}, + { AFE_PORT_ID_TERTIARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_5}, + { AFE_PORT_ID_TERTIARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_6}, + { AFE_PORT_ID_TERTIARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_6}, + { AFE_PORT_ID_TERTIARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_7}, + { AFE_PORT_ID_TERTIARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_7}, + { AFE_PORT_ID_QUATERNARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_0}, + { AFE_PORT_ID_QUATERNARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_0}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_1}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_1}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_2}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_2}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_3}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_3}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_4}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_4}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_5}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_5}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_6}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_6}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_7}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_7}, + { INT_BT_A2DP_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_BT_A2DP_RX}, + { AFE_PORT_ID_USB_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_USB_AUDIO_RX}, + { AFE_PORT_ID_USB_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_USB_AUDIO_TX}, + { DISPLAY_PORT_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_DISPLAY_PORT}, + { AFE_PORT_ID_TERTIARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_AUXPCM_RX}, + { AFE_PORT_ID_TERTIARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_TERT_AUXPCM_TX}, + { AFE_PORT_ID_QUATERNARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_AUXPCM_RX}, + { AFE_PORT_ID_QUATERNARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_QUAT_AUXPCM_TX}, + { AFE_PORT_ID_INT0_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT0_MI2S_RX}, + { AFE_PORT_ID_INT0_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT0_MI2S_TX}, + { AFE_PORT_ID_INT1_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT1_MI2S_RX}, + { AFE_PORT_ID_INT1_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT1_MI2S_TX}, + { AFE_PORT_ID_INT2_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT2_MI2S_RX}, + { AFE_PORT_ID_INT2_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT2_MI2S_TX}, + { AFE_PORT_ID_INT3_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT3_MI2S_RX}, + { AFE_PORT_ID_INT3_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT3_MI2S_TX}, + { AFE_PORT_ID_INT4_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT4_MI2S_RX}, + { AFE_PORT_ID_INT4_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT4_MI2S_TX}, + { AFE_PORT_ID_INT5_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT5_MI2S_RX}, + { AFE_PORT_ID_INT5_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT5_MI2S_TX}, + { AFE_PORT_ID_INT6_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT6_MI2S_RX}, + { AFE_PORT_ID_INT6_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, + LPASS_BE_INT6_MI2S_TX}, +}; + +/* Track ASM playback & capture sessions of DAI */ +static struct msm_pcm_routing_fdai_data + fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = { + /* MULTIMEDIA1 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA2 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA3 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA4 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA5 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA6 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA7*/ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA8 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA9 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA10 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA11 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA12 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA13 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA14 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA15 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA16 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA17 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA18 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA19 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, +}; + +static unsigned long session_copp_map[MSM_FRONTEND_DAI_MM_SIZE][2] + [MSM_BACKEND_DAI_MAX]; +static struct msm_pcm_routing_app_type_data app_type_cfg[MAX_APP_TYPES]; +static struct msm_pcm_stream_app_type_cfg + fe_dai_app_type_cfg[MSM_FRONTEND_DAI_MM_SIZE][2]; + +/* The caller of this should aqcuire routing lock */ +void msm_pcm_routing_get_bedai_info(int be_idx, + struct msm_pcm_routing_bdai_data *be_dai) +{ + if (be_idx >= 0 && be_idx < MSM_BACKEND_DAI_MAX) + memcpy(be_dai, &msm_bedais[be_idx], + sizeof(struct msm_pcm_routing_bdai_data)); +} + +/* The caller of this should aqcuire routing lock */ +void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type, + struct msm_pcm_routing_fdai_data *fe_dai) +{ + if ((sess_type == SESSION_TYPE_TX) || (sess_type == SESSION_TYPE_RX)) + memcpy(fe_dai, &fe_dai_map[fe_idx][sess_type], + sizeof(struct msm_pcm_routing_fdai_data)); +} + +void msm_pcm_routing_acquire_lock(void) +{ + mutex_lock(&routing_lock); +} + +void msm_pcm_routing_release_lock(void) +{ + mutex_unlock(&routing_lock); +} + +static int msm_pcm_routing_get_app_type_idx(int app_type) +{ + int idx; + + pr_debug("%s: app_type: %d\n", __func__, app_type); + for (idx = 0; idx < MAX_APP_TYPES; idx++) { + if (app_type_cfg[idx].app_type == app_type) + return idx; + } + pr_info("%s: App type not available, fallback to default\n", __func__); + return 0; +} + +void msm_pcm_routing_reg_stream_app_type_cfg(int fedai_id, int app_type, + int acdb_dev_id, int sample_rate, int session_type) +{ + pr_debug("%s: fedai_id %d, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fedai_id, session_type, app_type, + acdb_dev_id, sample_rate); + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + pr_err("%s: Invalid machine driver ID %d\n", + __func__, fedai_id); + return; + } + if (session_type != SESSION_TYPE_RX && + session_type != SESSION_TYPE_TX) { + pr_err("%s: Invalid session type %d\n", + __func__, session_type); + return; + } + fe_dai_app_type_cfg[fedai_id][session_type].app_type = app_type; + fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id = acdb_dev_id; + fe_dai_app_type_cfg[fedai_id][session_type].sample_rate = sample_rate; +} + +/** + * msm_pcm_routing_get_stream_app_type_cfg + * + * Receives fedai_id, session_type and populates app_type, acdb_dev_id, & + * sample rate. Returns 0 on success. On failure returns + * -EINVAL and does not alter passed values. + * + * fedai_id - Passed value, front end ID for which app type config is wanted + * session_type - Passed value, session type for which app type config + * is wanted + * app_type - Returned value, app type used by app type config + * acdb_dev_id - Returned value, ACDB device ID used by app type config + * sample_rate - Returned value, sample rate used by app type config + */ +int msm_pcm_routing_get_stream_app_type_cfg(int fedai_id, int session_type, + int *app_type, int *acdb_dev_id, int *sample_rate) +{ + int ret = 0; + + if (app_type == NULL) { + pr_err("%s: NULL pointer sent for app_type\n", __func__); + ret = -EINVAL; + goto done; + } else if (acdb_dev_id == NULL) { + pr_err("%s: NULL pointer sent for acdb_dev_id\n", __func__); + ret = -EINVAL; + goto done; + } else if (sample_rate == NULL) { + pr_err("%s: NULL pointer sent for sample rate\n", __func__); + ret = -EINVAL; + goto done; + } else if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + pr_err("%s: Invalid FE ID %d\n", + __func__, fedai_id); + ret = -EINVAL; + goto done; + } else if (session_type != SESSION_TYPE_RX && + session_type != SESSION_TYPE_TX) { + pr_err("%s: Invalid session type %d\n", + __func__, session_type); + ret = -EINVAL; + goto done; + } + *app_type = fe_dai_app_type_cfg[fedai_id][session_type].app_type; + *acdb_dev_id = fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id; + *sample_rate = fe_dai_app_type_cfg[fedai_id][session_type].sample_rate; + + pr_debug("%s: fedai_id %d, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fedai_id, session_type, + *app_type, *acdb_dev_id, *sample_rate); +done: + return ret; +} +EXPORT_SYMBOL(msm_pcm_routing_get_stream_app_type_cfg); + +static struct cal_block_data *msm_routing_find_topology_by_path(int path) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &cal_data->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (((struct audio_cal_info_adm_top *)cal_block->cal_info) + ->path == path) { + return cal_block; + } + } + pr_debug("%s: Can't find topology for path %d\n", __func__, path); + return NULL; +} + +static struct cal_block_data *msm_routing_find_topology(int path, + int app_type, + int acdb_id, + int sample_rate) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_adm_top *cal_info; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &cal_data->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + cal_info = (struct audio_cal_info_adm_top *) + cal_block->cal_info; + if ((cal_info->path == path) && + (cal_info->app_type == app_type) && + (cal_info->acdb_id == acdb_id) && + (cal_info->sample_rate == sample_rate)) { + return cal_block; + } + } + pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d sample_rate %d defaulting to search by path\n", + __func__, path, app_type, acdb_id, sample_rate); + return msm_routing_find_topology_by_path(path); +} + +static int msm_routing_get_adm_topology(int path, int fedai_id, + int session_type) +{ + int topology = NULL_COPP_TOPOLOGY; + struct cal_block_data *cal_block = NULL; + int app_type = 0, acdb_dev_id = 0, sample_rate = 0; + + pr_debug("%s\n", __func__); + + path = get_cal_path(path); + if (cal_data == NULL) + goto done; + + mutex_lock(&cal_data->lock); + + app_type = fe_dai_app_type_cfg[fedai_id][session_type].app_type; + acdb_dev_id = fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id; + sample_rate = fe_dai_app_type_cfg[fedai_id][session_type].sample_rate; + + cal_block = msm_routing_find_topology(path, app_type, + acdb_dev_id, sample_rate); + if (cal_block == NULL) + goto unlock; + + topology = ((struct audio_cal_info_adm_top *) + cal_block->cal_info)->topology; +unlock: + mutex_unlock(&cal_data->lock); +done: + pr_debug("%s: Using topology %d\n", __func__, topology); + return topology; +} + +static uint8_t is_be_dai_extproc(int be_dai) +{ + if (be_dai == MSM_BACKEND_DAI_EXTPROC_RX || + be_dai == MSM_BACKEND_DAI_EXTPROC_TX || + be_dai == MSM_BACKEND_DAI_EXTPROC_EC_TX) + return 1; + else + return 0; +} + +static void msm_pcm_routing_build_matrix(int fedai_id, int sess_type, + int path_type, int perf_mode) +{ + int i, port_type, j, num_copps = 0; + struct route_payload payload; + + port_type = ((path_type == ADM_PATH_PLAYBACK || + path_type == ADM_PATH_COMPRESSED_RX) ? + MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX); + + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) { + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = + session_copp_map[fedai_id][sess_type][i]; + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = + msm_bedais[i].port_id; + payload.copp_idx[num_copps] = j; + num_copps++; + } + } + } + } + + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fedai_id][sess_type].strm_id; + payload.app_type = + fe_dai_app_type_cfg[fedai_id][sess_type].app_type; + payload.acdb_dev_id = + fe_dai_app_type_cfg[fedai_id][sess_type].acdb_dev_id; + payload.sample_rate = + fe_dai_app_type_cfg[fedai_id][sess_type].sample_rate; + adm_matrix_map(path_type, payload, perf_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } +} + +void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, + int stream_type) +{ + int i, session_type, path_type, port_type; + u32 mode = 0; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } else { + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + port_type = MSM_AFE_PORT_TYPE_TX; + } + + mutex_lock(&routing_lock); + + fe_dai_map[fedai_id][session_type].strm_id = dspst_id; + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) { + mode = afe_get_port_type(msm_bedais[i].port_id); + adm_connect_afe_port(mode, dspst_id, + msm_bedais[i].port_id); + break; + } + } + mutex_unlock(&routing_lock); +} + +int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode, + int dspst_id, int stream_type, + uint32_t compr_passthr_mode) +{ + int i, j, session_type, path_type, port_type, topology, num_copps = 0; + struct route_payload payload; + u32 channels, sample_rate; + u16 bit_width = 16; + + pr_debug("%s:fe_id[%d] perf_mode[%d] id[%d] stream_type[%d] passt[%d]", + __func__, fe_id, perf_mode, dspst_id, + stream_type, compr_passthr_mode); + + if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fe_id); + return -EINVAL; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + if (compr_passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } else if (stream_type == SNDRV_PCM_STREAM_CAPTURE) { + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + port_type = MSM_AFE_PORT_TYPE_TX; + } else { + pr_err("%s: invalid stream type %d\n", __func__, stream_type); + return -EINVAL; + } + + mutex_lock(&routing_lock); + + payload.num_copps = 0; /* only RX needs to use payload */ + fe_dai_map[fe_id][session_type].strm_id = dspst_id; + /* re-enable EQ if active */ + msm_qti_pp_send_eq_values(fe_id); + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (test_bit(fe_id, &msm_bedais[i].fe_sessions)) + msm_bedais[i].compr_passthr_mode = compr_passthr_mode; + + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == + port_type) && + (msm_bedais[i].active) && + (test_bit(fe_id, &msm_bedais[i].fe_sessions))) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!msm_bedais[i].adm_override_ch) + channels = msm_bedais[i].channel; + else + channels = msm_bedais[i].adm_override_ch; + + bit_width = msm_routing_get_bit_width( + msm_bedais[i].format); + app_type = + fe_dai_app_type_cfg[fe_id][session_type].app_type; + if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx( + app_type); + sample_rate = + fe_dai_app_type_cfg[fe_id][session_type].sample_rate; + bit_width = + app_type_cfg[app_type_idx].bit_width; + } else { + sample_rate = msm_bedais[i].sample_rate; + } + acdb_dev_id = + fe_dai_app_type_cfg[fe_id][session_type].acdb_dev_id; + topology = msm_routing_get_adm_topology(path_type, + fe_id, session_type); + if (compr_passthr_mode == COMPRESSED_PASSTHROUGH_DSD) + topology = COMPRESS_PASSTHROUGH_NONE_TOPOLOGY; + pr_err("%s: Before adm open topology %d\n", __func__, + topology); + + copp_idx = + adm_open(msm_bedais[i].port_id, + path_type, sample_rate, channels, + topology, perf_mode, bit_width, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s:adm open failed coppid:%d\n", + __func__, copp_idx); + mutex_unlock(&routing_lock); + return -EINVAL; + } + pr_debug("%s: set idx bit of fe:%d, type: %d, be:%d\n", + __func__, fe_id, session_type, i); + set_bit(copp_idx, + &session_copp_map[fe_id][session_type][i]); + + if (msm_is_resample_needed( + sample_rate, + msm_bedais[i].sample_rate)) + adm_copp_mfc_cfg( + msm_bedais[i].port_id, copp_idx, + msm_bedais[i].sample_rate); + + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = + session_copp_map[fe_id][session_type][i]; + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = + msm_bedais[i].port_id; + payload.copp_idx[num_copps] = j; + num_copps++; + } + } + if (compr_passthr_mode != COMPRESSED_PASSTHROUGH_DSD) { + msm_routing_send_device_pp_params( + msm_bedais[i].port_id, + copp_idx); + } + } + } + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fe_id][session_type].strm_id; + payload.app_type = + fe_dai_app_type_cfg[fe_id][session_type].app_type; + payload.acdb_dev_id = + fe_dai_app_type_cfg[fe_id][session_type].acdb_dev_id; + adm_matrix_map(path_type, payload, perf_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } + mutex_unlock(&routing_lock); + return 0; +} + +static u32 msm_pcm_routing_get_voc_sessionid(u16 val) +{ + u32 session_id; + + switch (val) { + case MSM_FRONTEND_DAI_CS_VOICE: + session_id = voc_get_session_id(VOICE_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOLTE: + session_id = voc_get_session_id(VOLTE_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOWLAN: + session_id = voc_get_session_id(VOWLAN_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOICE2: + session_id = voc_get_session_id(VOICE2_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_QCHAT: + session_id = voc_get_session_id(QCHAT_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOIP: + session_id = voc_get_session_id(VOIP_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOICEMMODE1: + session_id = voc_get_session_id(VOICEMMODE1_NAME); + break; + case MSM_FRONTEND_DAI_VOICEMMODE2: + session_id = voc_get_session_id(VOICEMMODE2_NAME); + break; + default: + session_id = 0; + } + + pr_debug("%s session_id 0x%x", __func__, session_id); + return session_id; +} + +int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, + int dspst_id, int stream_type) +{ + int i, j, session_type, path_type, port_type, topology, num_copps = 0; + struct route_payload payload; + u32 channels, sample_rate; + uint16_t bits_per_sample = 16; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fedai_id); + return -EINVAL; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } else { + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + port_type = MSM_AFE_PORT_TYPE_TX; + } + + mutex_lock(&routing_lock); + + payload.num_copps = 0; /* only RX needs to use payload */ + fe_dai_map[fedai_id][session_type].strm_id = dspst_id; + fe_dai_map[fedai_id][session_type].perf_mode = perf_mode; + + /* re-enable EQ if active */ + msm_qti_pp_send_eq_values(fedai_id); + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!msm_bedais[i].adm_override_ch) + channels = msm_bedais[i].channel; + else + channels = msm_bedais[i].adm_override_ch; + msm_bedais[i].compr_passthr_mode = + LEGACY_PCM; + + bits_per_sample = msm_routing_get_bit_width( + msm_bedais[i].format); + + app_type = + fe_dai_app_type_cfg[fedai_id][session_type].app_type; + if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[fedai_id][session_type]. + sample_rate; + bits_per_sample = + app_type_cfg[app_type_idx].bit_width; + } else + sample_rate = msm_bedais[i].sample_rate; + + acdb_dev_id = + fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id; + topology = msm_routing_get_adm_topology(path_type, + fedai_id, session_type); + copp_idx = adm_open(msm_bedais[i].port_id, path_type, + sample_rate, channels, topology, + perf_mode, bits_per_sample, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed copp_idx:%d\n", + __func__, copp_idx); + mutex_unlock(&routing_lock); + return -EINVAL; + } + pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n", + __func__, fedai_id, session_type, i); + set_bit(copp_idx, + &session_copp_map[fedai_id][session_type][i]); + + if (msm_is_resample_needed( + sample_rate, + msm_bedais[i].sample_rate)) + adm_copp_mfc_cfg( + msm_bedais[i].port_id, copp_idx, + msm_bedais[i].sample_rate); + + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = + session_copp_map[fedai_id][session_type][i]; + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = + msm_bedais[i].port_id; + payload.copp_idx[num_copps] = j; + num_copps++; + } + } + if ((perf_mode == LEGACY_PCM_MODE) && + (msm_bedais[i].compr_passthr_mode == + LEGACY_PCM)) + msm_pcm_routing_cfg_pp(msm_bedais[i].port_id, + copp_idx, topology, + channels); + } + } + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fedai_id][session_type].strm_id; + payload.app_type = + fe_dai_app_type_cfg[fedai_id][session_type].app_type; + payload.acdb_dev_id = + fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id; + payload.sample_rate = + fe_dai_app_type_cfg[fedai_id][session_type].sample_rate; + adm_matrix_map(path_type, payload, perf_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } + mutex_unlock(&routing_lock); + return 0; +} + +int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode, + int dspst_id, int stream_type, + struct msm_pcm_routing_evt event_info) +{ + if (msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id, + stream_type)) { + pr_err("%s: failed to reg phy stream\n", __func__); + return -EINVAL; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) + fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info; + else + fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info; + return 0; +} + +void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type) +{ + int i, port_type, session_type, path_type, topology; + struct msm_pcm_routing_fdai_data *fdai; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + port_type = MSM_AFE_PORT_TYPE_RX; + session_type = SESSION_TYPE_RX; + path_type = ADM_PATH_PLAYBACK; + } else { + port_type = MSM_AFE_PORT_TYPE_TX; + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + } + + mutex_lock(&routing_lock); + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) { + int idx; + unsigned long copp = + session_copp_map[fedai_id][session_type][i]; + fdai = &fe_dai_map[fedai_id][session_type]; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (test_bit(idx, &copp)) + break; + + if (idx >= MAX_COPPS_PER_PORT || idx < 0) { + pr_debug("%s: copp idx is invalid, exiting\n", + __func__); + continue; + } + topology = adm_get_topology_for_port_copp_idx( + msm_bedais[i].port_id, idx); + adm_close(msm_bedais[i].port_id, fdai->perf_mode, idx); + pr_debug("%s:copp:%ld,idx bit fe:%d,type:%d,be:%d\n", + __func__, copp, fedai_id, session_type, i); + clear_bit(idx, + &session_copp_map[fedai_id][session_type][i]); + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID || + topology == DS2_ADM_COPP_TOPOLOGY_ID) && + (fdai->perf_mode == LEGACY_PCM_MODE) && + (msm_bedais[i].compr_passthr_mode == + LEGACY_PCM)) + msm_pcm_routing_deinit_pp(msm_bedais[i].port_id, + topology); + } + } + + fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION; + fe_dai_map[fedai_id][session_type].be_srate = 0; + mutex_unlock(&routing_lock); +} + +/* Check if FE/BE route is set */ +static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id) +{ + bool rc = false; + + if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* recheck FE ID in the mixer control defined in this file */ + pr_err("%s: bad MM ID\n", __func__); + return rc; + } + + if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions)) + rc = true; + + return rc; +} + +static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) +{ + int session_type, path_type, topology; + u32 channels, sample_rate; + uint16_t bits_per_sample = 16; + struct msm_pcm_routing_fdai_data *fdai; + + pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); + + if (val > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* recheck FE ID in the mixer control defined in this file */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (afe_get_port_type(msm_bedais[reg].port_id) == + MSM_AFE_PORT_TYPE_RX) { + session_type = SESSION_TYPE_RX; + if (msm_bedais[reg].compr_passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + } else { + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + } + + mutex_lock(&routing_lock); + if (set) { + if (!test_bit(val, &msm_bedais[reg].fe_sessions) && + ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) || + (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX))) + voc_start_playback(set, msm_bedais[reg].port_id); + + set_bit(val, &msm_bedais[reg].fe_sessions); + fdai = &fe_dai_map[val][session_type]; + if (msm_bedais[reg].active && fdai->strm_id != + INVALID_SESSION) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!msm_bedais[reg].adm_override_ch) + channels = msm_bedais[reg].channel; + else + channels = msm_bedais[reg].adm_override_ch; + if (session_type == SESSION_TYPE_TX && + fdai->be_srate && + (fdai->be_srate != msm_bedais[reg].sample_rate)) { + pr_debug("%s: flush strm %d diff BE rates\n", + __func__, fdai->strm_id); + + if (fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_BUF_RECFG, + fdai->event_info.priv_data); + fdai->be_srate = 0; /* might not need it */ + } + + bits_per_sample = msm_routing_get_bit_width( + msm_bedais[reg].format); + + app_type = + fe_dai_app_type_cfg[val][session_type].app_type; + if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[val][session_type]. + sample_rate; + bits_per_sample = + app_type_cfg[app_type_idx].bit_width; + } else + sample_rate = msm_bedais[reg].sample_rate; + + topology = msm_routing_get_adm_topology(path_type, val, + session_type); + acdb_dev_id = + fe_dai_app_type_cfg[val][session_type].acdb_dev_id; + copp_idx = adm_open(msm_bedais[reg].port_id, path_type, + sample_rate, channels, topology, + fdai->perf_mode, bits_per_sample, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed\n", __func__); + mutex_unlock(&routing_lock); + return; + } + pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n", + __func__, val, session_type, reg); + set_bit(copp_idx, + &session_copp_map[val][session_type][reg]); + + if (msm_is_resample_needed( + sample_rate, + msm_bedais[reg].sample_rate)) + adm_copp_mfc_cfg( + msm_bedais[reg].port_id, copp_idx, + msm_bedais[reg].sample_rate); + + if (session_type == SESSION_TYPE_RX && + fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_DEVSWITCH, + fdai->event_info.priv_data); + + msm_pcm_routing_build_matrix(val, session_type, + path_type, + fdai->perf_mode); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (msm_bedais[reg].compr_passthr_mode == + LEGACY_PCM)) + msm_pcm_routing_cfg_pp(msm_bedais[reg].port_id, + copp_idx, topology, + channels); + } + } else { + if (test_bit(val, &msm_bedais[reg].fe_sessions) && + ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) || + (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX))) + voc_start_playback(set, msm_bedais[reg].port_id); + clear_bit(val, &msm_bedais[reg].fe_sessions); + fdai = &fe_dai_map[val][session_type]; + if (msm_bedais[reg].active && fdai->strm_id != + INVALID_SESSION) { + int idx; + int port_id; + unsigned long copp = + session_copp_map[val][session_type][reg]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (test_bit(idx, &copp)) + break; + + port_id = msm_bedais[reg].port_id; + topology = adm_get_topology_for_port_copp_idx(port_id, + idx); + adm_close(msm_bedais[reg].port_id, fdai->perf_mode, + idx); + pr_debug("%s: copp: %ld, reset idx bit fe:%d, type: %d, be:%d topology=0x%x\n", + __func__, copp, val, session_type, reg, + topology); + clear_bit(idx, + &session_copp_map[val][session_type][reg]); + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID || + topology == DS2_ADM_COPP_TOPOLOGY_ID) && + (fdai->perf_mode == LEGACY_PCM_MODE) && + (msm_bedais[reg].compr_passthr_mode == + LEGACY_PCM)) + msm_pcm_routing_deinit_pp( + msm_bedais[reg].port_id, + topology); + msm_pcm_routing_build_matrix(val, session_type, + path_type, + fdai->perf_mode); + } + } + if ((msm_bedais[reg].port_id == VOICE_RECORD_RX) + || (msm_bedais[reg].port_id == VOICE_RECORD_TX)) + voc_start_record(msm_bedais[reg].port_id, set, voc_session_id); + + mutex_unlock(&routing_lock); +} + +static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions)) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + if (ucontrol->value.integer.value[0] && + msm_pcm_routing_route_is_set(mc->reg, mc->shift) == false) { + msm_pcm_routing_process_audio(mc->reg, mc->shift, 1); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + } else if (!ucontrol->value.integer.value[0] && + msm_pcm_routing_route_is_set(mc->reg, mc->shift) == true) { + msm_pcm_routing_process_audio(mc->reg, mc->shift, 0); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + } + + return 1; +} + +static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set) +{ + u32 session_id = 0; + u16 path_type; + + pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); + + session_id = msm_pcm_routing_get_voc_sessionid(val); + + pr_debug("%s: FE DAI 0x%x session_id 0x%x\n", + __func__, val, session_id); + + mutex_lock(&routing_lock); + + if (set) + set_bit(val, &msm_bedais[reg].fe_sessions); + else + clear_bit(val, &msm_bedais[reg].fe_sessions); + + if (val == MSM_FRONTEND_DAI_DTMF_RX && + afe_get_port_type(msm_bedais[reg].port_id) == + MSM_AFE_PORT_TYPE_RX) { + pr_debug("%s(): set=%d port id=0x%x for dtmf generation\n", + __func__, set, msm_bedais[reg].port_id); + afe_set_dtmf_gen_rx_portid(msm_bedais[reg].port_id, set); + } + + if (afe_get_port_type(msm_bedais[reg].port_id) == + MSM_AFE_PORT_TYPE_RX) + path_type = RX_PATH; + else + path_type = TX_PATH; + + if (set) { + if (msm_bedais[reg].active) { + voc_set_route_flag(session_id, path_type, 1); + voc_set_device_config(session_id, path_type, + msm_bedais[reg].channel, msm_bedais[reg].port_id); + + if (voc_get_route_flag(session_id, TX_PATH) && + voc_get_route_flag(session_id, RX_PATH)) + voc_enable_device(session_id); + } else { + pr_debug("%s BE is not active\n", __func__); + } + } else { + voc_set_route_flag(session_id, path_type, 0); + voc_disable_device(session_id); + } + + mutex_unlock(&routing_lock); + +} + +static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + mutex_lock(&routing_lock); + + if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions)) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + mutex_unlock(&routing_lock); + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + if (ucontrol->value.integer.value[0]) { + msm_pcm_routing_process_voice(mc->reg, mc->shift, 1); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + } else { + msm_pcm_routing_process_voice(mc->reg, mc->shift, 0); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + } + + return 1; +} + +static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + mutex_lock(&routing_lock); + + if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions)) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + mutex_unlock(&routing_lock); + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_voice_stub_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + if (ucontrol->value.integer.value[0]) { + mutex_lock(&routing_lock); + set_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions); + mutex_unlock(&routing_lock); + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + } else { + mutex_lock(&routing_lock); + clear_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions); + mutex_unlock(&routing_lock); + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + } + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + return 1; +} + +static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = fm_switch_enable; + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + fm_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_hfp_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hfp_switch_enable; + pr_debug("%s: HFP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_hfp_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: HFP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 1, update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 0, update); + hfp_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = pri_mi2s_switch_enable; + pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + pri_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = sec_mi2s_switch_enable; + pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + sec_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_tert_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tert_mi2s_switch_enable; + pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_tert_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + tert_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_quat_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = quat_mi2s_switch_enable; + pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_quat_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + quat_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = fm_pcmrx_switch_enable; + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + fm_pcmrx_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_lsm_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lsm_mux_slim_port; + return 0; +} + +static int msm_routing_lsm_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int mux = ucontrol->value.enumerated.item[0]; + int lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: LSM enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + switch (ucontrol->value.integer.value[0]) { + case 1: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX; + break; + case 2: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX; + break; + case 3: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX; + break; + case 4: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX; + break; + case 5: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX; + break; + case 6: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX; + break; + case 7: + lsm_port = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + default: + pr_err("Default lsm port"); + break; + } + set_lsm_port(lsm_port); + + if (ucontrol->value.integer.value[0]) { + lsm_mux_slim_port = ucontrol->value.integer.value[0]; + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, + update); + } else { + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, + update); + lsm_mux_slim_port = ucontrol->value.integer.value[0]; + } + + return 0; +} + +static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + u16 port_id; + enum afe_mad_type mad_type; + + pr_debug("%s: enter\n", __func__); + for (i = 0; i < ARRAY_SIZE(mad_audio_mux_text); i++) + if (!strcmp(kcontrol->id.name, mad_audio_mux_text[i])) + break; + + if (i-- == ARRAY_SIZE(mad_audio_mux_text)) { + WARN(1, "Invalid id name %s\n", kcontrol->id.name); + return -EINVAL; + } + + /*Check for Tertiary TX port*/ + if (!strcmp(kcontrol->id.name, mad_audio_mux_text[7])) { + ucontrol->value.integer.value[0] = MADSWAUDIO; + return 0; + } + + port_id = i * 2 + 1 + SLIMBUS_0_RX; + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + switch (mad_type) { + case MAD_HW_NONE: + ucontrol->value.integer.value[0] = MADNONE; + break; + case MAD_HW_AUDIO: + ucontrol->value.integer.value[0] = MADAUDIO; + break; + case MAD_HW_BEACON: + ucontrol->value.integer.value[0] = MADBEACON; + break; + case MAD_HW_ULTRASOUND: + ucontrol->value.integer.value[0] = MADULTRASOUND; + break; + case MAD_SW_AUDIO: + ucontrol->value.integer.value[0] = MADSWAUDIO; + break; + default: + WARN(1, "Unknown\n"); + return -EINVAL; + } + return 0; +} + +static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + u16 port_id; + enum afe_mad_type mad_type; + + pr_debug("%s: enter\n", __func__); + for (i = 0; i < ARRAY_SIZE(mad_audio_mux_text); i++) + if (!strcmp(kcontrol->id.name, mad_audio_mux_text[i])) + break; + + if (i-- == ARRAY_SIZE(mad_audio_mux_text)) { + WARN(1, "Invalid id name %s\n", kcontrol->id.name); + return -EINVAL; + } + + port_id = i * 2 + 1 + SLIMBUS_0_RX; + switch (ucontrol->value.integer.value[0]) { + case MADNONE: + mad_type = MAD_HW_NONE; + break; + case MADAUDIO: + mad_type = MAD_HW_AUDIO; + break; + case MADBEACON: + mad_type = MAD_HW_BEACON; + break; + case MADULTRASOUND: + mad_type = MAD_HW_ULTRASOUND; + break; + case MADSWAUDIO: + mad_type = MAD_SW_AUDIO; + break; + default: + WARN(1, "Unknown\n"); + return -EINVAL; + } + + /*Check for Tertiary TX port*/ + if (!strcmp(kcontrol->id.name, mad_audio_mux_text[7])) { + port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + mad_type = MAD_SW_AUDIO; + } + + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + return afe_port_set_mad_type(port_id, mad_type); +} + +static const char *const adm_override_chs_text[] = {"Zero", "One", "Two"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_7_rx_adm_override_chs, + adm_override_chs_text); + +static int msm_routing_adm_get_backend_idx(struct snd_kcontrol *kcontrol) +{ + int backend_id; + + if (strnstr(kcontrol->id.name, "SLIM7_RX", sizeof("SLIM7_RX"))) { + backend_id = MSM_BACKEND_DAI_SLIMBUS_7_RX; + } else { + pr_err("%s: unsupported backend id: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return backend_id; +} +static int msm_routing_adm_channel_config_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int backend_id = msm_routing_adm_get_backend_idx(kcontrol); + + if (backend_id >= 0) { + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = + msm_bedais[backend_id].adm_override_ch; + pr_debug("%s: adm channel count %ld for BE:%d\n", __func__, + ucontrol->value.integer.value[0], backend_id); + mutex_unlock(&routing_lock); + } + + return 0; +} + +static int msm_routing_adm_channel_config_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int backend_id = msm_routing_adm_get_backend_idx(kcontrol); + + if (backend_id >= 0) { + mutex_lock(&routing_lock); + msm_bedais[backend_id].adm_override_ch = + ucontrol->value.integer.value[0]; + pr_debug("%s:updating BE :%d adm channels: %d\n", + __func__, backend_id, + msm_bedais[backend_id].adm_override_ch); + mutex_unlock(&routing_lock); + } + + return 0; +} + +static const struct snd_kcontrol_new adm_channel_config_controls[] = { + SOC_ENUM_EXT("SLIM7_RX ADM Channels", slim_7_rx_adm_override_chs, + msm_routing_adm_channel_config_get, + msm_routing_adm_channel_config_put), +}; + +static int msm_routing_slim_0_rx_aanc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = slim0_rx_aanc_fb_port; + mutex_unlock(&routing_lock); + pr_debug("%s: AANC Mux Port %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +}; + +static int msm_routing_slim_0_rx_aanc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aanc_data aanc_info; + + mutex_lock(&routing_lock); + memset(&aanc_info, 0x00, sizeof(aanc_info)); + pr_debug("%s: AANC Mux Port %ld\n", __func__, + ucontrol->value.integer.value[0]); + slim0_rx_aanc_fb_port = ucontrol->value.integer.value[0]; + if (ucontrol->value.integer.value[0] == 0) { + aanc_info.aanc_active = false; + aanc_info.aanc_tx_port = 0; + aanc_info.aanc_rx_port = 0; + } else { + aanc_info.aanc_active = true; + aanc_info.aanc_rx_port = SLIMBUS_0_RX; + aanc_info.aanc_tx_port = + (SLIMBUS_0_RX - 1 + (slim0_rx_aanc_fb_port * 2)); + } + afe_set_aanc_info(&aanc_info); + mutex_unlock(&routing_lock); + return 0; +}; +static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = 0, shift = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + idx = mc->shift/(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8); + shift = mc->shift%(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8); + + if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) { + pr_err("%s: Invalid idx = %d\n", __func__, idx); + return -EINVAL; + } + + if (test_bit(shift, + (unsigned long *)&msm_bedais[mc->reg].port_sessions[idx])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = 0, shift = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + idx = mc->shift/(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8); + shift = mc->shift%(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8); + + if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) { + pr_err("%s: Invalid idx = %d\n", __func__, idx); + return -EINVAL; + } + + pr_debug("%s: reg 0x%x shift 0x%x val %ld idx %d reminder shift %d\n", + __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0], idx, shift); + + if (ucontrol->value.integer.value[0]) { + afe_loopback(1, msm_bedais[mc->reg].port_id, + msm_bedais[mc->shift].port_id); + set_bit(shift, + (unsigned long *)&msm_bedais[mc->reg].port_sessions[idx]); + } else { + afe_loopback(0, msm_bedais[mc->reg].port_id, + msm_bedais[mc->shift].port_id); + clear_bit(shift, + (unsigned long *)&msm_bedais[mc->reg].port_sessions[idx]); + } + + return 1; +} + +static int msm_routing_ec_ref_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ec_ref_rx = %d", __func__, msm_route_ec_ref_rx); + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = msm_route_ec_ref_rx; + mutex_unlock(&routing_lock); + return 0; +} + +static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ec_ref_port_id; + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + int mux = ucontrol->value.enumerated.item[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + mutex_lock(&routing_lock); + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_route_ec_ref_rx = 0; + ec_ref_port_id = AFE_PORT_INVALID; + break; + case 1: + msm_route_ec_ref_rx = 1; + ec_ref_port_id = SLIMBUS_0_RX; + break; + case 2: + msm_route_ec_ref_rx = 2; + ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case 3: + msm_route_ec_ref_rx = 3; + ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case 4: + msm_route_ec_ref_rx = 4; + ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case 5: + msm_route_ec_ref_rx = 5; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case 6: + msm_route_ec_ref_rx = 6; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case 7: + msm_route_ec_ref_rx = 7; + ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case 9: + msm_route_ec_ref_rx = 9; + ec_ref_port_id = SLIMBUS_5_RX; + break; + case 10: + msm_route_ec_ref_rx = 10; + ec_ref_port_id = SLIMBUS_1_TX; + break; + case 11: + msm_route_ec_ref_rx = 11; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_TX_1; + break; + case 12: + msm_route_ec_ref_rx = 12; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX; + break; + case 13: + msm_route_ec_ref_rx = 13; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_1; + break; + case 14: + msm_route_ec_ref_rx = 14; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_2; + break; + case 15: + msm_route_ec_ref_rx = 15; + ec_ref_port_id = SLIMBUS_6_RX; + break; + case 16: + msm_route_ec_ref_rx = 16; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case 17: + msm_route_ec_ref_rx = 17; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case 18: + msm_route_ec_ref_rx = 18; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_TX; + break; + case 19: + msm_route_ec_ref_rx = 19; + ec_ref_port_id = AFE_PORT_ID_USB_RX; + break; + default: + msm_route_ec_ref_rx = 0; /* NONE */ + pr_err("%s EC ref rx %ld not valid\n", + __func__, ucontrol->value.integer.value[0]); + ec_ref_port_id = AFE_PORT_INVALID; + break; + } + adm_ec_ref_rx_id(ec_ref_port_id); + pr_debug("%s: msm_route_ec_ref_rx = %d\n", + __func__, msm_route_ec_ref_rx); + mutex_unlock(&routing_lock); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, update); + return 0; +} + +static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_RX", + "PRI_MI2S_TX", "SEC_MI2S_TX", + "TERT_MI2S_TX", "QUAT_MI2S_TX", "SEC_I2S_RX", "PROXY_RX", + "SLIM_5_RX", "SLIM_1_TX", "QUAT_TDM_TX_1", + "QUAT_TDM_RX_0", "QUAT_TDM_RX_1", "QUAT_TDM_RX_2", "SLIM_6_RX", + "TERT_MI2S_RX", "QUAT_MI2S_RX", "TERT_TDM_TX_0", "USB_AUDIO_RX"}; + +static const struct soc_enum msm_route_ec_ref_rx_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_rx), ec_ref_rx), +}; + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul1 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL1 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul2 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL2 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul3 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL3 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul4 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL4 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul5 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL5 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul6 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL6 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul8 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL8 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL9 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul17 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL17 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul18 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL18 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul19 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL19 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ext_ec_ref_rx = %x\n", __func__, msm_route_ext_ec_ref); + + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = msm_route_ext_ec_ref; + mutex_unlock(&routing_lock); + return 0; +} + +static int msm_routing_ext_ec_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + int mux = ucontrol->value.enumerated.item[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int ret = 1; + bool state = true; + uint16_t ext_ec_ref_port_id; + struct snd_soc_dapm_update *update = NULL; + + mutex_lock(&routing_lock); + msm_route_ext_ec_ref = ucontrol->value.integer.value[0]; + + switch (msm_route_ext_ec_ref) { + case EXT_EC_REF_PRI_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case EXT_EC_REF_SEC_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case EXT_EC_REF_TERT_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case EXT_EC_REF_QUAT_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case EXT_EC_REF_QUIN_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + case EXT_EC_REF_SLIM_1_TX: + ext_ec_ref_port_id = SLIMBUS_1_TX; + break; + case EXT_EC_REF_NONE: + default: + ext_ec_ref_port_id = AFE_PORT_INVALID; + state = false; + break; + } + + pr_debug("%s: val = %d ext_ec_ref_port_id = 0x%0x state = %d\n", + __func__, msm_route_ext_ec_ref, ext_ec_ref_port_id, state); + + if (!voc_set_ext_ec_ref(ext_ec_ref_port_id, state)) { + mutex_unlock(&routing_lock); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, + update); + } else { + ret = -EINVAL; + mutex_unlock(&routing_lock); + } + return ret; +} + +static const char * const ext_ec_ref_rx[] = {"NONE", "PRI_MI2S_TX", + "SEC_MI2S_TX", "TERT_MI2S_TX", + "QUAT_MI2S_TX", "QUIN_MI2S_TX", + "SLIM_1_TX"}; + +static const struct soc_enum msm_route_ext_ec_ref_rx_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ext_ec_ref_rx), ext_ec_ref_rx), +}; + +static const struct snd_kcontrol_new voc_ext_ec_mux = + SOC_DAPM_ENUM_EXT("VOC_EXT_EC MUX Mux", msm_route_ext_ec_ref_rx_enum[0], + msm_routing_ext_ec_get, msm_routing_ext_ec_put); + + +static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + +}; + +static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new hdmi_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new display_port_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + + /* incall music delivery mixer */ +static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new incall_music2_delivery_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul1_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul2_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul3_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul4_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul5_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul6_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul8_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul17_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul18_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul19_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; +static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new int0_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new int4_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_8_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new stub_rx_mixer_controls[] = { + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = { + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = { + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_Voice", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_Voice", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_Voice", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_Voice", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX_Voice", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_Voice", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_Voice", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_Voice2", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_Voice2", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_Voice2", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice2", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_Voice2", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_Voice2", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_Voice2", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_Voice2", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_Voice2", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_Voice2", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_volte_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_VoLTE", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_VoLTE", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOLTE, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_VoLTE", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_VoLTE", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_VoLTE", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_VoLTE", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_VoLTE", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_VoWLAN", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_VoWLAN", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOWLAN, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_VoWLAN", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_VoWLAN", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_VoWLAN", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_MMode1", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_MMode1", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_MMode1", + MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INT_BT_SCO_TX_MMode1", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_MMode1", + MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_MMode1", + MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_MMode1", + MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_MMode1", + MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_MMode1", + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_MMode1", + MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_MMode1", + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX_MMode1", + MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_MMode1", + MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_MMode1", + MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_MMode1", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_MMode2", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_MMode2", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_MMode2", + MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INT_BT_SCO_TX_MMode2", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_MMode2", + MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_MMode2", + MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_MMode2", + MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_MMode2", + MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_MMode2", + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_MMode2", + MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_MMode2", + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX_MMode2", + MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_MMode2", + MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_MMode2", + MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_MMode2", + MSM_BACKEND_DAI_USB_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voip_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_Voip", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_Voip", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_Voip", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_Voip", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_Voip", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX_Voip", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_Voip", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = { + SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_EXTPROC_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("STUB_1_TX_HL", MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = { + SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_EXTPROC_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("STUB_1_TX_HL", MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_volte_stub_mixer_controls[] = { + SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_EXTPROC_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("STUB_1_TX_HL", MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_qchat_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_TX_QCHAT", MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_0_TX_QCHAT", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_QCHAT", + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_QCHAT, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX_QCHAT", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("MI2S_TX_QCHAT", MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX_QCHAT", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX_QCHAT", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX_QCHAT", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_7_TX_QCHAT", MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("SLIM_8_TX_QCHAT", MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("USB_AUDIO_TX_QCHAT", MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_PRI_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_auxpcm_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_auxpcm_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_3_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_BT_SCO_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_INT_BT_SCO_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_AFE_PCM_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_AUXPCM_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_SLIMBUS_0_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_6_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new bt_sco_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new afe_pcm_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + + +static const struct snd_kcontrol_new hdmi_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_HDMI_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new display_port_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_i2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_0_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_1_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_2_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_3_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_0_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_1_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_2_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_3_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new slim_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim1_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim3_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim4_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim6_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new pcm_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_fm_pcmrx_switch_mixer, + msm_routing_put_fm_pcmrx_switch_mixer); + +static const struct snd_kcontrol_new pri_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_pri_mi2s_switch_mixer, + msm_routing_put_pri_mi2s_switch_mixer); + +static const struct snd_kcontrol_new sec_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_sec_mi2s_switch_mixer, + msm_routing_put_sec_mi2s_switch_mixer); + +static const struct snd_kcontrol_new tert_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_tert_mi2s_switch_mixer, + msm_routing_put_tert_mi2s_switch_mixer); + +static const struct snd_kcontrol_new quat_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_quat_mi2s_switch_mixer, + msm_routing_put_quat_mi2s_switch_mixer); + +static const struct snd_kcontrol_new hfp_pri_aux_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct snd_kcontrol_new hfp_aux_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct snd_kcontrol_new hfp_int_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct soc_enum lsm_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mad_audio_mux_text), mad_audio_mux_text); + +static const struct snd_kcontrol_new lsm1_mux = + SOC_DAPM_ENUM_EXT("LSM1 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); + +static const struct snd_kcontrol_new lsm2_mux = + SOC_DAPM_ENUM_EXT("LSM2 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); +static const struct snd_kcontrol_new lsm3_mux = + SOC_DAPM_ENUM_EXT("LSM3 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); + +static const struct snd_kcontrol_new lsm4_mux = + SOC_DAPM_ENUM_EXT("LSM4 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); +static const struct snd_kcontrol_new lsm5_mux = + SOC_DAPM_ENUM_EXT("LSM5 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); + +static const struct snd_kcontrol_new lsm6_mux = + SOC_DAPM_ENUM_EXT("LSM6 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); +static const struct snd_kcontrol_new lsm7_mux = + SOC_DAPM_ENUM_EXT("LSM7 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); + +static const struct snd_kcontrol_new lsm8_mux = + SOC_DAPM_ENUM_EXT("LSM8 MUX", lsm_mux_enum, + msm_routing_lsm_mux_get, + msm_routing_lsm_mux_put); + + +static const char * const lsm_func_text[] = { + "None", "AUDIO", "BEACON", "ULTRASOUND", "SWAUDIO", +}; +static const struct soc_enum lsm_func_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_func_text), lsm_func_text); +static const struct snd_kcontrol_new lsm_function[] = { + SOC_ENUM_EXT(SLIMBUS_0_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_1_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_2_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_3_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_4_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_5_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(TERT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), +}; + +static const char * const aanc_slim_0_rx_text[] = { + "ZERO", "SLIMBUS_0_TX", "SLIMBUS_1_TX", "SLIMBUS_2_TX", "SLIMBUS_3_TX", + "SLIMBUS_4_TX", "SLIMBUS_5_TX", "SLIMBUS_6_TX" +}; + +static const struct soc_enum aanc_slim_0_rx_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aanc_slim_0_rx_text), + aanc_slim_0_rx_text); + +static const struct snd_kcontrol_new aanc_slim_0_rx_mux[] = { + SOC_ENUM_EXT("AANC_SLIM_0_RX MUX", aanc_slim_0_rx_enum, + msm_routing_slim_0_rx_aanc_mux_get, + msm_routing_slim_0_rx_aanc_mux_put) +}; + +static int msm_routing_get_stereo_to_custom_stereo_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = is_custom_stereo_on; + return 0; +} + +static int msm_routing_put_stereo_to_custom_stereo_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int flag = 0, i = 0, rc = 0, idx = 0; + int be_index = 0, port_id, topo_id; + unsigned int session_id = 0; + uint16_t op_FL_ip_FL_weight = 0; + uint16_t op_FL_ip_FR_weight = 0; + uint16_t op_FR_ip_FL_weight = 0; + uint16_t op_FR_ip_FR_weight = 0; + + flag = ucontrol->value.integer.value[0]; + pr_debug("%s E flag %d\n", __func__, flag); + + if ((is_custom_stereo_on && flag) || (!is_custom_stereo_on && !flag)) { + pr_err("%s: is_custom_stereo_on %d, flag %d\n", + __func__, is_custom_stereo_on, flag); + return 0; + } + is_custom_stereo_on = flag ? true : false; + pr_debug("%s:is_custom_stereo_on %d\n", __func__, is_custom_stereo_on); + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active) + continue; + if ((port_id != SLIMBUS_0_RX) && + (port_id != RT_PROXY_PORT_001_RX) && + (port_id != AFE_PORT_ID_PRIMARY_MI2S_RX)) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions, + MSM_FRONTEND_DAI_MM_SIZE) { + if (fe_dai_map[i][SESSION_TYPE_RX].perf_mode != + LEGACY_PCM_MODE) + goto skip_send_custom_stereo; + session_id = + fe_dai_map[i][SESSION_TYPE_RX].strm_id; + if (is_custom_stereo_on) { + op_FL_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + } else { + op_FL_ip_FL_weight = Q14_GAIN_UNITY; + op_FL_ip_FR_weight = 0; + op_FR_ip_FL_weight = 0; + op_FR_ip_FR_weight = Q14_GAIN_UNITY; + } + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp = + session_copp_map[i] + [SESSION_TYPE_RX][be_index]; + if (!test_bit(idx, &copp)) + goto skip_send_custom_stereo; + topo_id = adm_get_topology_for_port_copp_idx( + msm_bedais[be_index].port_id, idx); + if (topo_id < 0) + pr_debug("%s:Err:custom stereo topo %d", + __func__, topo_id); + pr_debug("idx %d\n", idx); + if (topo_id == DS2_ADM_COPP_TOPOLOGY_ID) + rc = msm_ds2_dap_set_custom_stereo_onoff + (msm_bedais[be_index].port_id, + idx, is_custom_stereo_on); + else if (topo_id == DOLBY_ADM_COPP_TOPOLOGY_ID) + rc = dolby_dap_set_custom_stereo_onoff( + msm_bedais[be_index].port_id, + idx, is_custom_stereo_on); + else + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd + (msm_bedais[be_index].port_id, + idx, session_id, + op_FL_ip_FL_weight, + op_FL_ip_FR_weight, + op_FR_ip_FL_weight, + op_FR_ip_FR_weight); + if (rc < 0) +skip_send_custom_stereo: + pr_err("%s: err setting custom stereo\n", + __func__); + } + + } + } + return 0; +} + +static const struct snd_kcontrol_new stereo_to_custom_stereo_controls[] = { + SOC_SINGLE_EXT("Set Custom Stereo OnOff", SND_SOC_NOPM, 0, + 1, 0, msm_routing_get_stereo_to_custom_stereo_control, + msm_routing_put_stereo_to_custom_stereo_control), +}; + +static int msm_routing_get_app_type_cfg_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int msm_routing_put_app_type_cfg_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i = 0, j; + int num_app_types = ucontrol->value.integer.value[i++]; + + pr_debug("%s\n", __func__); + + memset(app_type_cfg, 0, MAX_APP_TYPES* + sizeof(struct msm_pcm_routing_app_type_data)); + if (num_app_types > MAX_APP_TYPES) { + pr_err("%s: number of app types exceed the max supported\n", + __func__); + return -EINVAL; + } + for (j = 0; j < num_app_types; j++) { + app_type_cfg[j].app_type = + ucontrol->value.integer.value[i++]; + app_type_cfg[j].sample_rate = + ucontrol->value.integer.value[i++]; + app_type_cfg[j].bit_width = + ucontrol->value.integer.value[i++]; + } + + return 0; +} + +static const struct snd_kcontrol_new app_type_cfg_controls[] = { + SOC_SINGLE_MULTI_EXT("App Type Config", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 128, msm_routing_get_app_type_cfg_control, + msm_routing_put_app_type_cfg_control), +}; + +static int msm_routing_get_use_ds1_or_ds2_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = is_ds2_on; + return 0; +} + +static int msm_routing_put_use_ds1_or_ds2_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + is_ds2_on = ucontrol->value.integer.value[0]; + return 0; +} + +static const struct snd_kcontrol_new use_ds1_or_ds2_controls[] = { + SOC_SINGLE_EXT("DS2 OnOff", SND_SOC_NOPM, 0, + 1, 0, msm_routing_get_use_ds1_or_ds2_control, + msm_routing_put_use_ds1_or_ds2_control), +}; + +int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + int rc = 0; + int be_idx = 0; + char *param_value; + int *update_param_value; + uint32_t param_length = sizeof(uint32_t); + uint32_t param_payload_len = RMS_PAYLOAD_LEN * sizeof(uint32_t); + + param_value = kzalloc(param_length, GFP_KERNEL); + if (!param_value) + return -ENOMEM; + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) + if (msm_bedais[be_idx].port_id == SLIMBUS_0_TX) + break; + if ((be_idx < MSM_BACKEND_DAI_MAX) && msm_bedais[be_idx].active) { + rc = adm_get_params(SLIMBUS_0_TX, 0, + RMS_MODULEID_APPI_PASSTHRU, + RMS_PARAM_FIRST_SAMPLE, + param_length + param_payload_len, + param_value); + if (rc) { + pr_err("%s: get parameters failed:%d\n", __func__, rc); + kfree(param_value); + return -EINVAL; + } + update_param_value = (int *)param_value; + ucontrol->value.integer.value[0] = update_param_value[0]; + + pr_debug("%s: FROM DSP value[0] 0x%x\n", + __func__, update_param_value[0]); + } + kfree(param_value); + return 0; +} + +static int msm_voc_session_id_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + voc_session_id = ucontrol->value.integer.value[0]; + + pr_debug("%s: voc_session_id=%u\n", __func__, voc_session_id); + + return 0; +} + +static int msm_voc_session_id_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_session_id; + + return 0; +} + +static struct snd_kcontrol_new msm_voc_session_controls[] = { + SOC_SINGLE_MULTI_EXT("Voc VSID", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 1, msm_voc_session_id_get, + msm_voc_session_id_put), +}; + +static int msm_sound_focus_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct sound_focus_param); + + return 0; +} + +static int msm_voice_sound_focus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + + memcpy((void *)&soundFocusData, ucontrol->value.bytes.data, + sizeof(struct sound_focus_param)); + ret = voc_set_sound_focus(soundFocusData); + if (ret) { + pr_err("%s: Error setting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + } + + return ret; +} + +static int msm_voice_sound_focus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + + memset(&soundFocusData, 0, sizeof(struct sound_focus_param)); + + ret = voc_get_sound_focus(&soundFocusData); + if (ret) { + pr_err("%s: Error getting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData, + sizeof(struct sound_focus_param)); + +done: + return ret; +} + +static int msm_source_tracking_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct source_tracking_param); + + return 0; +} + +static int msm_voice_source_tracking_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct source_tracking_param sourceTrackingData; + + memset(&sourceTrackingData, 0, sizeof(struct source_tracking_param)); + + ret = voc_get_source_tracking(&sourceTrackingData); + if (ret) { + pr_err("%s: Error getting Source Tracking Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData, + sizeof(struct source_tracking_param)); + +done: + return ret; +} + +static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type, + int *copp_idx) +{ + int i, idx, be_idx; + int ret = 0; + unsigned long copp; + + pr_debug("%s: Enter, port_id=%d\n", __func__, port_id); + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port validation failed id 0x%x ret %d\n", + __func__, port_id, ret); + + ret = -EINVAL; + goto done; + } + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + if (msm_bedais[be_idx].port_id == port_id) + break; + } + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Invalid be id %d\n", __func__, be_idx); + + ret = -EINVAL; + goto done; + } + + for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions, + MSM_FRONTEND_DAI_MM_SIZE) { + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + copp = session_copp_map[i] + [session_type][be_idx]; + if (test_bit(idx, &copp)) + break; + } + if (idx >= MAX_COPPS_PER_PORT) + continue; + else + break; + } + if (i >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: Invalid FE, exiting\n", __func__); + + ret = -EINVAL; + goto done; + } + *copp_idx = idx; + pr_debug("%s: copp_idx=%d\n", __func__, *copp_idx); + +done: + return ret; +} + +static int msm_audio_sound_focus_derive_port_id(struct snd_kcontrol *kcontrol, + const char *prefix, int *port_id) +{ + int ret = 0; + + pr_debug("%s: Enter, prefix:%s\n", __func__, prefix); + + /* + * Mixer control name will be like "Sound Focus Audio Tx SLIMBUS_0" + * where the prefix is "Sound Focus Audio Tx ". Skip the prefix + * and compare the string with the backend name to derive the port id. + */ + if (!strcmp(kcontrol->id.name + strlen(prefix), + "SLIMBUS_0")) { + *port_id = SLIMBUS_0_TX; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "TERT_MI2S")) { + *port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + } else { + pr_err("%s: mixer ctl name=%s, could not derive valid port id\n", + __func__, kcontrol->id.name); + + ret = -EINVAL; + goto done; + } + pr_debug("%s: mixer ctl name=%s, derived port_id=%d\n", + __func__, kcontrol->id.name, *port_id); + +done: + return ret; +} + +static int msm_audio_sound_focus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + int port_id, copp_idx; + + ret = msm_audio_sound_focus_derive_port_id(kcontrol, + "Sound Focus Audio Tx ", &port_id); + if (ret != 0) { + pr_err("%s: Error in deriving port id, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX, + &copp_idx); + if (ret) { + pr_err("%s: Could not get copp idx for port_id=%d\n", + __func__, port_id); + + ret = -EINVAL; + goto done; + } + + memcpy((void *)&soundFocusData, ucontrol->value.bytes.data, + sizeof(struct sound_focus_param)); + + ret = adm_set_sound_focus(port_id, copp_idx, soundFocusData); + if (ret) { + pr_err("%s: Error setting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + +done: + return ret; +} + +static int msm_audio_sound_focus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + int port_id, copp_idx; + + ret = msm_audio_sound_focus_derive_port_id(kcontrol, + "Sound Focus Audio Tx ", &port_id); + if (ret) { + pr_err("%s: Error in deriving port id, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX, + &copp_idx); + if (ret) { + pr_err("%s: Could not get copp idx for port_id=%d\n", + __func__, port_id); + + ret = -EINVAL; + goto done; + } + + ret = adm_get_sound_focus(port_id, copp_idx, &soundFocusData); + if (ret) { + pr_err("%s: Error getting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData, + sizeof(struct sound_focus_param)); + +done: + return ret; +} + +static int msm_audio_source_tracking_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct source_tracking_param sourceTrackingData; + int port_id, copp_idx; + + ret = msm_audio_sound_focus_derive_port_id(kcontrol, + "Source Tracking Audio Tx ", &port_id); + if (ret) { + pr_err("%s: Error in deriving port id, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX, + &copp_idx); + if (ret) { + pr_err("%s: Could not get copp idx for port_id=%d\n", + __func__, port_id); + + ret = -EINVAL; + goto done; + } + + ret = adm_get_source_tracking(port_id, copp_idx, &sourceTrackingData); + if (ret) { + pr_err("%s: Error getting Source Tracking Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData, + sizeof(struct source_tracking_param)); + +done: + return ret; +} + +static const struct snd_kcontrol_new msm_source_tracking_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx SLIMBUS_0", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx SLIMBUS_0", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx SLIMBUS_0", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx SLIMBUS_0", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx TERT_MI2S", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx TERT_MI2S", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx TERT_MI2S", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx TERT_MI2S", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, +}; + +static int spkr_prot_put_vi_lch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int item; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + pr_debug("%s item is %d\n", __func__, + ucontrol->value.enumerated.item[0]); + mutex_lock(&routing_lock); + item = ucontrol->value.enumerated.item[0]; + if (item < e->items) { + pr_debug("%s RX DAI ID %d TX DAI id %d\n", + __func__, e->shift_l, e->values[item]); + if (e->shift_l < MSM_BACKEND_DAI_MAX && + e->values[item] < MSM_BACKEND_DAI_MAX) + /* Enable feedback TX path */ + ret = afe_spk_prot_feed_back_cfg( + msm_bedais[e->values[item]].port_id, + msm_bedais[e->shift_l].port_id, 1, 0, 1); + else { + pr_debug("%s values are out of range item %d\n", + __func__, e->values[item]); + /* Disable feedback TX path */ + if (e->values[item] == MSM_BACKEND_DAI_MAX) + ret = afe_spk_prot_feed_back_cfg(0, 0, 0, 0, 0); + else + ret = -EINVAL; + } + } else { + pr_err("%s item value is out of range item\n", __func__); + ret = -EINVAL; + } + mutex_unlock(&routing_lock); + return ret; +} + +static int spkr_prot_put_vi_rch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int item; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + pr_debug("%s item is %d\n", __func__, + ucontrol->value.enumerated.item[0]); + mutex_lock(&routing_lock); + item = ucontrol->value.enumerated.item[0]; + if (item < e->items) { + pr_debug("%s RX DAI ID %d TX DAI id %d\n", + __func__, e->shift_l, e->values[item]); + if (e->shift_l < MSM_BACKEND_DAI_MAX && + e->values[item] < MSM_BACKEND_DAI_MAX) + /* Enable feedback TX path */ + ret = afe_spk_prot_feed_back_cfg( + msm_bedais[e->values[item]].port_id, + msm_bedais[e->shift_l].port_id, + 1, 1, 1); + else { + pr_debug("%s values are out of range item %d\n", + __func__, e->values[item]); + /* Disable feedback TX path */ + if (e->values[item] == MSM_BACKEND_DAI_MAX) + ret = afe_spk_prot_feed_back_cfg(0, + 0, 0, 0, 0); + else + ret = -EINVAL; + } + } else { + pr_err("%s item value is out of range item\n", __func__); + ret = -EINVAL; + } + mutex_unlock(&routing_lock); + return ret; +} + +static int spkr_prot_get_vi_lch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int spkr_prot_get_vi_rch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static const char * const slim0_rx_vi_fb_tx_lch_mux_text[] = { + "ZERO", "SLIM4_TX" +}; + +static const char * const slim0_rx_vi_fb_tx_rch_mux_text[] = { + "ZERO", "SLIM4_TX" +}; + +static const char * const mi2s_rx_vi_fb_tx_mux_text[] = { + "ZERO", "SENARY_TX" +}; + +static const int const slim0_rx_vi_fb_tx_lch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX +}; + +static const int const slim0_rx_vi_fb_tx_rch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX +}; + +static const int const mi2s_rx_vi_fb_tx_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SENARY_MI2S_TX +}; + +static const struct soc_enum slim0_rx_vi_fb_lch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0, + ARRAY_SIZE(slim0_rx_vi_fb_tx_lch_mux_text), + slim0_rx_vi_fb_tx_lch_mux_text, slim0_rx_vi_fb_tx_lch_value); + +static const struct soc_enum slim0_rx_vi_fb_rch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0, + ARRAY_SIZE(slim0_rx_vi_fb_tx_rch_mux_text), + slim0_rx_vi_fb_tx_rch_mux_text, slim0_rx_vi_fb_tx_rch_value); + +static const struct soc_enum mi2s_rx_vi_fb_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_PRI_MI2S_RX, 0, 0, + ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text), + mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value); + +static const struct snd_kcontrol_new slim0_rx_vi_fb_lch_mux = + SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_LCH_MUX", + slim0_rx_vi_fb_lch_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new slim0_rx_vi_fb_rch_mux = + SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_RCH_MUX", + slim0_rx_vi_fb_rch_mux_enum, spkr_prot_get_vi_rch_port, + spkr_prot_put_vi_rch_port); + +static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux = + SOC_DAPM_ENUM_EXT("PRI_MI2S_RX_VI_FB_MUX", + mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { + /* Frontend AIF */ + /* Widget name equals to Front-End DAI name, + * Stream name must contains substring of front-end dai name + */ + SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL9", "MultiMedia9 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL10", "MultiMedia10 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL11", "MultiMedia11 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL12", "MultiMedia12 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL13", "MultiMedia13 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL14", "MultiMedia14 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL15", "MultiMedia15 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL16", "MultiMedia16 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL17", "MultiMedia17 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL18", "MultiMedia18 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL19", "MultiMedia19 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE2_UL", "Voice2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VoLTE_UL", "VoLTE Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VoWLAN_DL", "VoWLAN Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VoWLAN_UL", "VoWLAN Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICEMMODE1_DL", + "VoiceMMode1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICEMMODE1_UL", + "VoiceMMode1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICEMMODE2_DL", + "VoiceMMode2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICEMMODE2_UL", + "VoiceMMode2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("CPE_LSM_UL_HL", "CPE LSM capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM1_DL_HL", "SLIMBUS1_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM1_UL_HL", "SLIMBUS1_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM3_DL_HL", "SLIMBUS3_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM3_UL_HL", "SLIMBUS3_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM4_DL_HL", "SLIMBUS4_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM4_UL_HL", "SLIMBUS4_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM6_DL_HL", "SLIMBUS6_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM6_UL_HL", "SLIMBUS6_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM8_DL_HL", "SLIMBUS8_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM8_UL_HL", "SLIMBUS8_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INTHFP_DL_HL", "INT_HFP_BT_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INTHFP_UL_HL", "INT_HFP_BT_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_I2S_DL_HL", "SEC_I2S_RX_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT0_MI2S_DL_HL", + "INT0 MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT4_MI2S_DL_HL", + "INT4 MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_MI2S_DL_HL", + "Primary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_MI2S_DL_HL", + "Secondary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_MI2S_DL_HL", + "Tertiary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_MI2S_DL_HL", + "Quaternary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_MI2S_UL_HL", + "Tertiary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_UL_HL", + "Secondary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_MI2S_UL_HL", + "Primary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_UL_HL", + "Quaternary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_0_DL_HL", + "Primary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_0_UL_HL", + "Primary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_1_DL_HL", + "Primary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_1_UL_HL", + "Primary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_2_DL_HL", + "Primary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_2_UL_HL", + "Primary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_3_DL_HL", + "Primary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_3_UL_HL", + "Primary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_4_DL_HL", + "Primary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_4_UL_HL", + "Primary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_5_DL_HL", + "Primary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_5_UL_HL", + "Primary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_6_DL_HL", + "Primary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_6_UL_HL", + "Primary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_7_DL_HL", + "Primary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_7_UL_HL", + "Primary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_0_DL_HL", + "Secondary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_0_UL_HL", + "Secondary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_1_DL_HL", + "Secondary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_1_UL_HL", + "Secondary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_2_DL_HL", + "Secondary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_2_UL_HL", + "Secondary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_3_DL_HL", + "Secondary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_3_UL_HL", + "Secondary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_4_DL_HL", + "Secondary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_4_UL_HL", + "Secondary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_5_DL_HL", + "Secondary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_5_UL_HL", + "Secondary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_6_DL_HL", + "Secondary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_6_UL_HL", + "Secondary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_7_DL_HL", + "Secondary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_7_UL_HL", + "Secondary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_0_DL_HL", + "Tertiary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_0_UL_HL", + "Tertiary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_1_DL_HL", + "Tertiary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_1_UL_HL", + "Tertiary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_2_DL_HL", + "Tertiary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_2_UL_HL", + "Tertiary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_3_DL_HL", + "Tertiary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_3_UL_HL", + "Tertiary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_4_DL_HL", + "Tertiary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_4_UL_HL", + "Tertiary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_5_DL_HL", + "Tertiary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_5_UL_HL", + "Tertiary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_6_DL_HL", + "Tertiary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_6_UL_HL", + "Tertiary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_7_DL_HL", + "Tertiary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_7_UL_HL", + "Tertiary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_0_DL_HL", + "Quaternary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_0_UL_HL", + "Quaternary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_1_DL_HL", + "Quaternary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_1_UL_HL", + "Quaternary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_2_DL_HL", + "Quaternary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_2_UL_HL", + "Quaternary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_3_DL_HL", + "Quaternary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_3_UL_HL", + "Quaternary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_4_DL_HL", + "Quaternary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_4_UL_HL", + "Quaternary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_5_DL_HL", + "Quaternary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_5_UL_HL", + "Quaternary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_6_DL_HL", + "Quaternary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_6_UL_HL", + "Quaternary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_7_DL_HL", + "Quaternary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7_UL_HL", + "Quaternary TDM7 Hostless Capture", + 0, 0, 0, 0), + + /* LSM */ + SND_SOC_DAPM_AIF_OUT("LSM1_UL_HL", "Listen 1 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM2_UL_HL", "Listen 2 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM3_UL_HL", "Listen 3 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM4_UL_HL", "Listen 4 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM5_UL_HL", "Listen 5 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM6_UL_HL", "Listen 6 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM7_UL_HL", "Listen 7 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM8_UL_HL", "Listen 8 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QCHAT_DL", "QCHAT Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QCHAT_UL", "QCHAT Capture", 0, 0, 0, 0), + /* Backend AIF */ + /* Stream name equals to backend dai link stream name */ + SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_I2S_RX", "Secondary I2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SPDIF_RX", "SPDIF Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT", "Display Port Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_SD1", + "Secondary MI2S Playback SD1", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT0_MI2S_RX", "INT0 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT4_MI2S_RX", "INT4 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_RX", "Quinary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT2_MI2S_TX", "INT2 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT3_MI2S_TX", "INT3 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_MI2S_TX", "Quinary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SENARY_MI2S_TX", "Senary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT_BT_A2DP_RX", "Internal BT-A2DP Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_0", "Primary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_0", "Primary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_1", "Primary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_1", "Primary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_2", "Primary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_2", "Primary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_3", "Primary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_3", "Primary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_4", "Primary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_4", "Primary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_5", "Primary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_5", "Primary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_6", "Primary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_6", "Primary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_7", "Primary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_7", "Primary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_0", "Secondary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_0", "Secondary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_1", "Secondary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_1", "Secondary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_2", "Secondary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_2", "Secondary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_3", "Secondary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_3", "Secondary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_4", "Secondary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_4", "Secondary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_5", "Secondary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_5", "Secondary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_6", "Secondary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_6", "Secondary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_7", "Secondary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_7", "Secondary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_0", "Tertiary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_0", "Tertiary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_1", "Tertiary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_1", "Tertiary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_2", "Tertiary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_2", "Tertiary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_3", "Tertiary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_3", "Tertiary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_4", "Tertiary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_4", "Tertiary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_5", "Tertiary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_5", "Tertiary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_6", "Tertiary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_6", "Tertiary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_7", "Tertiary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_7", "Tertiary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_0", "Quaternary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_0", "Quaternary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_1", "Quaternary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_1", "Quaternary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_2", "Quaternary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_2", "Quaternary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_3", "Quaternary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_3", "Quaternary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_4", "Quaternary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_4", "Quaternary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_5", "Quaternary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_5", "Quaternary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_6", "Quaternary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_6", "Quaternary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_7", "Quaternary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture", + 0, 0, 0, 0), + /* incall */ + SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE2_PLAYBACK_TX", "Voice2 Farend Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SENARY_TX", "Senary_mi2s Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_AUX_PCM_RX", "Sec AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_AUX_PCM_TX", "Sec AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_AUX_PCM_RX", "Tert AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_AUX_PCM_TX", "Tert AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_AUX_PCM_RX", "Quat AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_AUX_PCM_TX", "Quat AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICE2_STUB_DL", "VOICE2_STUB Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE2_STUB_UL", "VOICE2_STUB Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOLTE_STUB_DL", "VOLTE_STUB Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOLTE_STUB_UL", "VOLTE_STUB Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("STUB_TX", "Stub Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("STUB_1_TX", "Stub1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0), + /* In- call recording */ + SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("SLIMBUS_7_RX", "Slimbus7 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_7_TX", "Slimbus7 Capture", 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("SLIMBUS_8_RX", "Slimbus8 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_8_TX", "Slimbus8 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("USB_AUDIO_RX", "USB Audio Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("USB_AUDIO_TX", "USB Audio Capture", 0, 0, 0, 0), + + /* Switch Definitions */ + SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0, + &slim_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS1_DL_HL", SND_SOC_NOPM, 0, 0, + &slim1_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS3_DL_HL", SND_SOC_NOPM, 0, 0, + &slim3_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS4_DL_HL", SND_SOC_NOPM, 0, 0, + &slim4_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS6_DL_HL", SND_SOC_NOPM, 0, 0, + &slim6_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("PCM_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &pcm_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("PRI_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &pri_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SEC_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &sec_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("TERT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &tert_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("QUAT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &quat_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_PRI_AUX_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_pri_aux_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_AUX_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_aux_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_INT_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_int_switch_mixer_controls), + + /* Mux Definitions */ + SND_SOC_DAPM_MUX("LSM1 MUX", SND_SOC_NOPM, 0, 0, &lsm1_mux), + SND_SOC_DAPM_MUX("LSM2 MUX", SND_SOC_NOPM, 0, 0, &lsm2_mux), + SND_SOC_DAPM_MUX("LSM3 MUX", SND_SOC_NOPM, 0, 0, &lsm3_mux), + SND_SOC_DAPM_MUX("LSM4 MUX", SND_SOC_NOPM, 0, 0, &lsm4_mux), + SND_SOC_DAPM_MUX("LSM5 MUX", SND_SOC_NOPM, 0, 0, &lsm5_mux), + SND_SOC_DAPM_MUX("LSM6 MUX", SND_SOC_NOPM, 0, 0, &lsm6_mux), + SND_SOC_DAPM_MUX("LSM7 MUX", SND_SOC_NOPM, 0, 0, &lsm7_mux), + SND_SOC_DAPM_MUX("LSM8 MUX", SND_SOC_NOPM, 0, 0, &lsm8_mux), + + /* Mixer definitions */ + SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_i2s_rx_mixer_controls, ARRAY_SIZE(sec_i2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_2_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_2_rx_mixer_controls, ARRAY_SIZE(slimbus_2_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_5_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_5_rx_mixer_controls, ARRAY_SIZE(slimbus_5_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_7_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_7_rx_mixer_controls, ARRAY_SIZE(slimbus_7_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0, + hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT Mixer", SND_SOC_NOPM, 0, 0, + display_port_mixer_controls, ARRAY_SIZE(display_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SPDIF_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + spdif_rx_mixer_controls, ARRAY_SIZE(spdif_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quaternary_mi2s_rx_mixer_controls, + ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + tertiary_mi2s_rx_mixer_controls, + ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + secondary_mi2s_rx_mixer_controls, + ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX_SD1 Audio Mixer", SND_SOC_NOPM, 0, 0, + secondary_mi2s_rx2_mixer_controls, + ARRAY_SIZE(secondary_mi2s_rx2_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + primary_mi2s_rx_mixer_controls, + ARRAY_SIZE(primary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INT0_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int0_mi2s_rx_mixer_controls, + ARRAY_SIZE(int0_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INT4_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int4_mi2s_rx_mixer_controls, + ARRAY_SIZE(int4_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quinary_mi2s_rx_mixer_controls, + ARRAY_SIZE(quinary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_tx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_tx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_0_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_tx_0_mixer_controls, + ARRAY_SIZE(tert_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_1_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_2_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_3_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_0_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_tx_0_mixer_controls, + ARRAY_SIZE(quat_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_1_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_3_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, + mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, + mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia3 Mixer", SND_SOC_NOPM, 0, 0, + mmul3_mixer_controls, ARRAY_SIZE(mmul3_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0, + mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0, + mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0, + mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0, + mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0, + mmul17_mixer_controls, ARRAY_SIZE(mmul17_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia18 Mixer", SND_SOC_NOPM, 0, 0, + mmul18_mixer_controls, ARRAY_SIZE(mmul18_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia19 Mixer", SND_SOC_NOPM, 0, 0, + mmul19_mixer_controls, ARRAY_SIZE(mmul19_mixer_controls)), + SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_auxpcm_rx_mixer_controls, + ARRAY_SIZE(tert_auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_auxpcm_rx_mixer_controls, + ARRAY_SIZE(quat_auxpcm_rx_mixer_controls)), + /* incall */ + SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0, + incall_music_delivery_mixer_controls, + ARRAY_SIZE(incall_music_delivery_mixer_controls)), + SND_SOC_DAPM_MIXER("Incall_Music_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + incall_music2_delivery_mixer_controls, + ARRAY_SIZE(incall_music2_delivery_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_4_rx_mixer_controls, + ARRAY_SIZE(slimbus_4_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_6_rx_mixer_controls, + ARRAY_SIZE(slimbus_6_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("USB_AUDIO_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + usb_audio_rx_mixer_controls, + ARRAY_SIZE(usb_audio_rx_mixer_controls)), + /* Voice Mixer */ + SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls, + ARRAY_SIZE(pri_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + sec_i2s_rx_voice_mixer_controls, + ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + sec_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(sec_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + slimbus_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + bt_sco_rx_voice_mixer_controls, + ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + afe_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + sec_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(sec_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + tert_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(tert_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quat_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(quat_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + hdmi_rx_voice_mixer_controls, + ARRAY_SIZE(hdmi_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + pri_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(pri_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("INT0_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + int0_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(int0_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("INT4_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + int4_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(int4_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + tert_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(tert_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quat_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(quat_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quin_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(quin_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("Voice_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls, + ARRAY_SIZE(tx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("Voice2_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voice2_mixer_controls, + ARRAY_SIZE(tx_voice2_mixer_controls)), + SND_SOC_DAPM_MIXER("Voip_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls, + ARRAY_SIZE(tx_voip_mixer_controls)), + SND_SOC_DAPM_MIXER("VoLTE_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_volte_mixer_controls, + ARRAY_SIZE(tx_volte_mixer_controls)), + SND_SOC_DAPM_MIXER("VoWLAN_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_vowlan_mixer_controls, + ARRAY_SIZE(tx_vowlan_mixer_controls)), + SND_SOC_DAPM_MIXER("VoiceMMode1_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voicemmode1_mixer_controls, + ARRAY_SIZE(tx_voicemmode1_mixer_controls)), + SND_SOC_DAPM_MIXER("VoiceMMode2_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voicemmode2_mixer_controls, + ARRAY_SIZE(tx_voicemmode2_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_A2DP_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int_bt_a2dp_rx_mixer_controls, + ARRAY_SIZE(int_bt_a2dp_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("Voice Stub Tx Mixer", SND_SOC_NOPM, 0, 0, + tx_voice_stub_mixer_controls, ARRAY_SIZE(tx_voice_stub_mixer_controls)), + SND_SOC_DAPM_MIXER("Voice2 Stub Tx Mixer", SND_SOC_NOPM, 0, 0, + tx_voice2_stub_mixer_controls, + ARRAY_SIZE(tx_voice2_stub_mixer_controls)), + SND_SOC_DAPM_MIXER("VoLTE Stub Tx Mixer", SND_SOC_NOPM, 0, 0, + tx_volte_stub_mixer_controls, ARRAY_SIZE(tx_volte_stub_mixer_controls)), + SND_SOC_DAPM_MIXER("STUB_RX Mixer", SND_SOC_NOPM, 0, 0, + stub_rx_mixer_controls, ARRAY_SIZE(stub_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Mixer", SND_SOC_NOPM, 0, 0, + slimbus_1_rx_mixer_controls, ARRAY_SIZE(slimbus_1_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_3_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, + slimbus_3_rx_mixer_controls, ARRAY_SIZE(slimbus_3_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_6_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + slimbus_6_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_6_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_7_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, + slimbus_7_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_7_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_8_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, + slimbus_8_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_8_rx_voice_mixer_controls)), + /* port mixer */ + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls, + ARRAY_SIZE(sbus_0_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("AUX_PCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, aux_pcm_rx_port_mixer_controls, + ARRAY_SIZE(aux_pcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sec_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(sec_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, tert_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(tert_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, quat_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(quat_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0, + sbus_1_rx_port_mixer_controls, + ARRAY_SIZE(sbus_1_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Port Mixer", SND_SOC_NOPM, 0, 0, + bt_sco_rx_port_mixer_controls, + ARRAY_SIZE(bt_sco_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("AFE_PCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, afe_pcm_rx_port_mixer_controls, + ARRAY_SIZE(afe_pcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("HDMI_RX Port Mixer", + SND_SOC_NOPM, 0, 0, hdmi_rx_port_mixer_controls, + ARRAY_SIZE(hdmi_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Port Mixer", + SND_SOC_NOPM, 0, 0, display_port_rx_port_mixer_controls, + ARRAY_SIZE(display_port_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_I2S_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sec_i2s_rx_port_mixer_controls, + ARRAY_SIZE(sec_i2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_3_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sbus_3_rx_port_mixer_controls, + ARRAY_SIZE(sbus_3_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sbus_6_rx_port_mixer_controls, + ARRAY_SIZE(sbus_6_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + mi2s_rx_port_mixer_controls, ARRAY_SIZE(mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + primary_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(primary_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + sec_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(sec_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + tert_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(tert_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + quat_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(quat_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QCHAT_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_qchat_mixer_controls, + ARRAY_SIZE(tx_qchat_mixer_controls)), + SND_SOC_DAPM_MIXER("USB_AUDIO_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, usb_audio_rx_voice_mixer_controls, + ARRAY_SIZE(usb_audio_rx_voice_mixer_controls)), + /* Virtual Pins to force backends ON atm */ + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_INPUT("BE_IN"), + + SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_LCH_MUX", SND_SOC_NOPM, 0, 0, + &slim0_rx_vi_fb_lch_mux), + SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_RCH_MUX", SND_SOC_NOPM, 0, 0, + &slim0_rx_vi_fb_rch_mux), + SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, + &mi2s_rx_vi_fb_mux), + + SND_SOC_DAPM_MUX("VOC_EXT_EC MUX", SND_SOC_NOPM, 0, 0, + &voc_ext_ec_mux), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL1 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul1), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL2 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul2), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL3 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul3), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL4 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul4), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL5 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul5), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL6 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul6), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL8 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul8), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul9), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul17), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL18 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul18), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL19 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul19), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"}, + + {"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"}, + + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"}, + + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_2_RX", NULL, "SLIMBUS_2_RX Audio Mixer"}, + + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_5_RX", NULL, "SLIMBUS_5_RX Audio Mixer"}, + + {"HDMI Mixer", "MultiMedia1", "MM_DL1"}, + {"HDMI Mixer", "MultiMedia2", "MM_DL2"}, + {"HDMI Mixer", "MultiMedia3", "MM_DL3"}, + {"HDMI Mixer", "MultiMedia4", "MM_DL4"}, + {"HDMI Mixer", "MultiMedia5", "MM_DL5"}, + {"HDMI Mixer", "MultiMedia6", "MM_DL6"}, + {"HDMI Mixer", "MultiMedia7", "MM_DL7"}, + {"HDMI Mixer", "MultiMedia8", "MM_DL8"}, + {"HDMI Mixer", "MultiMedia9", "MM_DL9"}, + {"HDMI Mixer", "MultiMedia10", "MM_DL10"}, + {"HDMI Mixer", "MultiMedia11", "MM_DL11"}, + {"HDMI Mixer", "MultiMedia12", "MM_DL12"}, + {"HDMI Mixer", "MultiMedia13", "MM_DL13"}, + {"HDMI Mixer", "MultiMedia14", "MM_DL14"}, + {"HDMI Mixer", "MultiMedia15", "MM_DL15"}, + {"HDMI Mixer", "MultiMedia16", "MM_DL16"}, + {"HDMI", NULL, "HDMI Mixer"}, + + {"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"}, + {"DISPLAY_PORT Mixer", "MultiMedia2", "MM_DL2"}, + {"DISPLAY_PORT Mixer", "MultiMedia3", "MM_DL3"}, + {"DISPLAY_PORT Mixer", "MultiMedia4", "MM_DL4"}, + {"DISPLAY_PORT Mixer", "MultiMedia5", "MM_DL5"}, + {"DISPLAY_PORT Mixer", "MultiMedia6", "MM_DL6"}, + {"DISPLAY_PORT Mixer", "MultiMedia7", "MM_DL7"}, + {"DISPLAY_PORT Mixer", "MultiMedia8", "MM_DL8"}, + {"DISPLAY_PORT Mixer", "MultiMedia9", "MM_DL9"}, + {"DISPLAY_PORT Mixer", "MultiMedia10", "MM_DL10"}, + {"DISPLAY_PORT Mixer", "MultiMedia11", "MM_DL11"}, + {"DISPLAY_PORT Mixer", "MultiMedia12", "MM_DL12"}, + {"DISPLAY_PORT Mixer", "MultiMedia13", "MM_DL13"}, + {"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"}, + {"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"}, + {"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"}, + {"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"}, + + {"SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SPDIF_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SPDIF_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SPDIF_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SPDIF_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SPDIF_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SPDIF_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SPDIF_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SPDIF_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SPDIF_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SPDIF_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SPDIF_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SPDIF_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SPDIF_RX", NULL, "SPDIF_RX Audio Mixer"}, + + /* incall */ + {"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"Incall_Music Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"Incall_Music Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"VOICE2_PLAYBACK_TX", NULL, "Incall_Music_2 Audio Mixer"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_4_RX", NULL, "SLIMBUS_4_RX Audio Mixer"}, + + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Audio Mixer"}, + + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_7_RX", NULL, "SLIMBUS_7_RX Audio Mixer"}, + + {"USB_AUDIO_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Audio Mixer"}, + + {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, + {"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, + {"MultiMedia8 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, + {"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"}, + {"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"}, + {"MultiMedia8 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"}, + {"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"}, + {"MultiMedia1 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia1 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia1 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MultiMedia8 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia8 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia4 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia8 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia2 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia4 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia17 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia18 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia19 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"MI2S_RX", NULL, "MI2S_RX Audio Mixer"}, + + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"}, + + {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"}, + + {"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"}, + + {"SEC_MI2S_RX_SD1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_MI2S_RX_SD1", NULL, "SEC_MI2S_RX_SD1 Audio Mixer"}, + + {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + + {"PRI_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"}, + + {"INT0_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Audio Mixer"}, + + {"INT4_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Audio Mixer"}, + + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"}, + + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"}, + + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_TX_0", NULL, "PRI_TDM_TX_0 Audio Mixer"}, + + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"}, + + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_TX_0", NULL, "SEC_TDM_TX_0 Audio Mixer"}, + + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"}, + + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_TX_0", NULL, "TERT_TDM_TX_0 Audio Mixer"}, + + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"}, + + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"}, + + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"}, + + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"}, + + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_TX_0", NULL, "QUAT_TDM_TX_0 Audio Mixer"}, + + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"}, + + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"}, + + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"}, + + {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"}, + {"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia1 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia2 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia6 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + + {"MultiMedia1 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia2 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia3 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia4 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia5 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia6 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia8 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia1 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia2 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia4 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia5 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_UL6"}, + {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"}, + + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_UL6"}, + {"INT_BT_A2DP_RX", NULL, "INTERNAL_A2DP_RX Audio Mixer"}, + + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"}, + + {"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"}, + + {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia3 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia4 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia17 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia18 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia4 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia8 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + + {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia3 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia4 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia17 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia18 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MM_UL1", NULL, "MultiMedia1 Mixer"}, + {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MM_UL2", NULL, "MultiMedia2 Mixer"}, + {"MM_UL3", NULL, "MultiMedia3 Mixer"}, + {"MM_UL4", NULL, "MultiMedia4 Mixer"}, + {"MM_UL5", NULL, "MultiMedia5 Mixer"}, + {"MM_UL6", NULL, "MultiMedia6 Mixer"}, + {"MM_UL8", NULL, "MultiMedia8 Mixer"}, + {"MM_UL17", NULL, "MultiMedia17 Mixer"}, + {"MM_UL18", NULL, "MultiMedia18 Mixer"}, + {"MM_UL19", NULL, "MultiMedia19 Mixer"}, + + {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"}, + + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"}, + + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX Audio Mixer"}, + + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX Audio Mixer"}, + + {"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"}, + + {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"PRI_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"PRI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"}, + + {"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SEC_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SEC_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SEC_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"}, + + {"SEC_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SEC_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SEC_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_Voice Mixer"}, + + {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SLIM_0_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_0_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SLIM_0_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIM_0_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"}, + + {"SLIM_6_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SLIM_6_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SLIM_6_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_6_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_6_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SLIM_6_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIM_6_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_6_RX", NULL, "SLIM_6_RX_Voice Mixer"}, + + {"USB_AUDIO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"USB_AUDIO_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"USB_AUDIO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX_Voice Mixer"}, + + {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"}, + + {"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"AFE_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"AFE_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"}, + + {"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"AUX_PCM_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"}, + + {"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"}, + + {"TERT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX_Voice Mixer"}, + + {"QUAT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"}, + + {"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"HDMI_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"HDMI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"HDMI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"HDMI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"HDMI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"HDMI", NULL, "HDMI_RX_Voice Mixer"}, + {"HDMI", NULL, "HDMI_DL_HL"}, + + {"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"}, + + {"PRI_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"PRI_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_Voice Mixer"}, + + {"INT0_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"INT0_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"INT0_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"INT0_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"INT0_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_Voice Mixer"}, + + {"INT4_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"INT4_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"INT4_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"INT4_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"INT4_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_Voice Mixer"}, + + {"TERT_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"TERT_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_Voice Mixer"}, + + {"QUAT_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"QUAT_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_Voice Mixer"}, + + {"QUIN_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"QUIN_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"}, + + {"VOC_EXT_EC MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"VOC_EXT_EC MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"VOC_EXT_EC MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"VOC_EXT_EC MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"VOC_EXT_EC MUX", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"CS-VOICE_UL1", NULL, "VOC_EXT_EC MUX"}, + {"VOIP_UL", NULL, "VOC_EXT_EC MUX"}, + {"VoLTE_UL", NULL, "VOC_EXT_EC MUX"}, + {"VOICE2_UL", NULL, "VOC_EXT_EC MUX"}, + {"VOICEMMODE1_UL", NULL, "VOC_EXT_EC MUX"}, + {"VOICEMMODE2_UL", NULL, "VOC_EXT_EC MUX"}, + + {"AUDIO_REF_EC_UL1 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_0", "QUAT_TDM_RX_0"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_1", "QUAT_TDM_RX_1"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_2", "QUAT_TDM_RX_2"}, + {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + + {"AUDIO_REF_EC_UL2 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL2 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL2 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL2 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL3 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL3 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL3 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL3 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL4 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL4 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL4 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL4 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL5 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL5 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL5 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL5 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL6 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL6 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL6 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL6 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL8 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL8 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL8 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL8 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL9 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL9 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL9 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL9 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL17 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL17 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL17 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL17 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL18 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL18 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL18 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL18 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL19 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL19 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"}, + {"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"}, + {"MM_UL4", NULL, "AUDIO_REF_EC_UL4 MUX"}, + {"MM_UL5", NULL, "AUDIO_REF_EC_UL5 MUX"}, + {"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"}, + {"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"}, + {"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"}, + {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"}, + {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"}, + {"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"}, + + {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"}, + {"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"}, + {"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"}, + {"Voice_Tx Mixer", "TERT_MI2S_TX_Voice", "TERT_MI2S_TX"}, + {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"}, + {"Voice_Tx Mixer", "SLIM_7_TX_Voice", "SLIMBUS_7_TX"}, + {"Voice_Tx Mixer", "SLIM_8_TX_Voice", "SLIMBUS_8_TX"}, + {"Voice_Tx Mixer", "USB_AUDIO_TX_Voice", "USB_AUDIO_TX"}, + {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"}, + {"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"}, + {"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"}, + {"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"}, + {"Voice_Tx Mixer", "TERT_AUX_PCM_TX_Voice", "TERT_AUX_PCM_TX"}, + {"Voice_Tx Mixer", "QUAT_AUX_PCM_TX_Voice", "QUAT_AUX_PCM_TX"}, + {"Voice_Tx Mixer", "SEC_MI2S_TX_Voice", "SEC_MI2S_TX"}, + {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"}, + + {"Voice2_Tx Mixer", "PRI_TX_Voice2", "PRI_I2S_TX"}, + {"Voice2_Tx Mixer", "PRI_MI2S_TX_Voice2", "PRI_MI2S_TX"}, + {"Voice2_Tx Mixer", "MI2S_TX_Voice2", "MI2S_TX"}, + {"Voice2_Tx Mixer", "TERT_MI2S_TX_Voice2", "TERT_MI2S_TX"}, + {"Voice2_Tx Mixer", "SLIM_0_TX_Voice2", "SLIMBUS_0_TX"}, + {"Voice2_Tx Mixer", "SLIM_7_TX_Voice2", "SLIMBUS_7_TX"}, + {"Voice2_Tx Mixer", "SLIM_8_TX_Voice2", "SLIMBUS_8_TX"}, + {"Voice2_Tx Mixer", "USB_AUDIO_TX_Voice2", "USB_AUDIO_TX"}, + {"Voice2_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice2", "INT_BT_SCO_TX"}, + {"Voice2_Tx Mixer", "AFE_PCM_TX_Voice2", "PCM_TX"}, + {"Voice2_Tx Mixer", "AUX_PCM_TX_Voice2", "AUX_PCM_TX"}, + {"Voice2_Tx Mixer", "SEC_AUX_PCM_TX_Voice2", "SEC_AUX_PCM_TX"}, + {"Voice2_Tx Mixer", "TERT_AUX_PCM_TX_Voice2", "TERT_AUX_PCM_TX"}, + {"Voice2_Tx Mixer", "QUAT_AUX_PCM_TX_Voice2", "QUAT_AUX_PCM_TX"}, + {"VOICE2_UL", NULL, "Voice2_Tx Mixer"}, + + {"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"}, + {"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"}, + {"VoLTE_Tx Mixer", "SLIM_7_TX_VoLTE", "SLIMBUS_7_TX"}, + {"VoLTE_Tx Mixer", "SLIM_8_TX_VoLTE", "SLIMBUS_8_TX"}, + {"VoLTE_Tx Mixer", "USB_AUDIO_TX_VoLTE", "USB_AUDIO_TX"}, + {"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"}, + {"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"}, + {"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"}, + {"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"}, + {"VoLTE_Tx Mixer", "TERT_AUX_PCM_TX_VoLTE", "TERT_AUX_PCM_TX"}, + {"VoLTE_Tx Mixer", "QUAT_AUX_PCM_TX_VoLTE", "QUAT_AUX_PCM_TX"}, + {"VoLTE_Tx Mixer", "MI2S_TX_VoLTE", "MI2S_TX"}, + {"VoLTE_Tx Mixer", "PRI_MI2S_TX_VoLTE", "PRI_MI2S_TX"}, + {"VoLTE_Tx Mixer", "TERT_MI2S_TX_VoLTE", "TERT_MI2S_TX"}, + {"VoLTE_UL", NULL, "VoLTE_Tx Mixer"}, + + {"VoWLAN_Tx Mixer", "PRI_TX_VoWLAN", "PRI_I2S_TX"}, + {"VoWLAN_Tx Mixer", "SLIM_0_TX_VoWLAN", "SLIMBUS_0_TX"}, + {"VoWLAN_Tx Mixer", "SLIM_7_TX_VoWLAN", "SLIMBUS_7_TX"}, + {"VoWLAN_Tx Mixer", "SLIM_8_TX_VoWLAN", "SLIMBUS_8_TX"}, + {"VoWLAN_Tx Mixer", "USB_AUDIO_TX_VoWLAN", "USB_AUDIO_TX"}, + {"VoWLAN_Tx Mixer", "INTERNAL_BT_SCO_TX_VoWLAN", "INT_BT_SCO_TX"}, + {"VoWLAN_Tx Mixer", "AFE_PCM_TX_VoWLAN", "PCM_TX"}, + {"VoWLAN_Tx Mixer", "AUX_PCM_TX_VoWLAN", "AUX_PCM_TX"}, + {"VoWLAN_Tx Mixer", "SEC_AUX_PCM_TX_VoWLAN", "SEC_AUX_PCM_TX"}, + {"VoWLAN_Tx Mixer", "TERT_AUX_PCM_TX_VoWLAN", "TERT_AUX_PCM_TX"}, + {"VoWLAN_Tx Mixer", "QUAT_AUX_PCM_TX_VoWLAN", "QUAT_AUX_PCM_TX"}, + {"VoWLAN_Tx Mixer", "MI2S_TX_VoWLAN", "MI2S_TX"}, + {"VoWLAN_Tx Mixer", "PRI_MI2S_TX_VoWLAN", "PRI_MI2S_TX"}, + {"VoWLAN_Tx Mixer", "TERT_MI2S_TX_VoWLAN", "TERT_MI2S_TX"}, + {"VoWLAN_UL", NULL, "VoWLAN_Tx Mixer"}, + + {"VoiceMMode1_Tx Mixer", "PRI_TX_MMode1", "PRI_I2S_TX"}, + {"VoiceMMode1_Tx Mixer", "PRI_MI2S_TX_MMode1", "PRI_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "MI2S_TX_MMode1", "MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "TERT_MI2S_TX_MMode1", "TERT_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "INT3_MI2S_TX_MMode1", "INT3_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "SLIM_0_TX_MMode1", "SLIMBUS_0_TX"}, + {"VoiceMMode1_Tx Mixer", "SLIM_7_TX_MMode1", "SLIMBUS_7_TX"}, + {"VoiceMMode1_Tx Mixer", "SLIM_8_TX_MMode1", "SLIMBUS_8_TX"}, + {"VoiceMMode1_Tx Mixer", "USB_AUDIO_TX_MMode1", "USB_AUDIO_TX"}, + {"VoiceMMode1_Tx Mixer", "INT_BT_SCO_TX_MMode1", "INT_BT_SCO_TX"}, + {"VoiceMMode1_Tx Mixer", "AFE_PCM_TX_MMode1", "PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "AUX_PCM_TX_MMode1", "AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "TERT_AUX_PCM_TX_MMode1", "TERT_AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "QUAT_AUX_PCM_TX_MMode1", "QUAT_AUX_PCM_TX"}, + {"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"}, + + {"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"}, + {"VoiceMMode2_Tx Mixer", "PRI_MI2S_TX_MMode2", "PRI_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "MI2S_TX_MMode2", "MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "TERT_MI2S_TX_MMode2", "TERT_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "INT3_MI2S_TX_MMode2", "INT3_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "SLIM_0_TX_MMode2", "SLIMBUS_0_TX"}, + {"VoiceMMode2_Tx Mixer", "SLIM_7_TX_MMode2", "SLIMBUS_7_TX"}, + {"VoiceMMode2_Tx Mixer", "SLIM_8_TX_MMode2", "SLIMBUS_8_TX"}, + {"VoiceMMode2_Tx Mixer", "USB_AUDIO_TX_MMode2", "USB_AUDIO_TX"}, + {"VoiceMMode2_Tx Mixer", "INT_BT_SCO_TX_MMode2", "INT_BT_SCO_TX"}, + {"VoiceMMode2_Tx Mixer", "AFE_PCM_TX_MMode2", "PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "AUX_PCM_TX_MMode2", "AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "SEC_AUX_PCM_TX_MMode2", "SEC_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "TERT_AUX_PCM_TX_MMode2", "TERT_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "QUAT_AUX_PCM_TX_MMode2", "QUAT_AUX_PCM_TX"}, + {"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"}, + + {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"}, + {"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"}, + {"Voip_Tx Mixer", "TERT_MI2S_TX_Voip", "TERT_MI2S_TX"}, + {"Voip_Tx Mixer", "INT3_MI2S_TX_Voip", "INT3_MI2S_TX"}, + {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"}, + {"Voip_Tx Mixer", "SLIM_7_TX_Voip", "SLIMBUS_7_TX"}, + {"Voip_Tx Mixer", "SLIM_8_TX_Voip", "SLIMBUS_8_TX"}, + {"Voip_Tx Mixer", "USB_AUDIO_TX_Voip", "USB_AUDIO_TX"}, + {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"}, + {"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"}, + {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"}, + {"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "TERT_AUX_PCM_TX_Voip", "TERT_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "QUAT_AUX_PCM_TX_Voip", "QUAT_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"}, + {"VOIP_UL", NULL, "Voip_Tx Mixer"}, + + {"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"}, + {"SLIMBUS1_DL_HL", "Switch", "SLIM1_DL_HL"}, + {"SLIMBUS_1_RX", NULL, "SLIMBUS1_DL_HL"}, + {"SLIMBUS3_DL_HL", "Switch", "SLIM3_DL_HL"}, + {"SLIMBUS_3_RX", NULL, "SLIMBUS3_DL_HL"}, + {"SLIMBUS4_DL_HL", "Switch", "SLIM4_DL_HL"}, + {"SLIMBUS_4_RX", NULL, "SLIMBUS4_DL_HL"}, + {"SLIMBUS6_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"SLIMBUS_6_RX", NULL, "SLIMBUS6_DL_HL"}, + {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"}, + {"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"}, + {"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"}, + {"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"}, + {"SLIM8_UL_HL", NULL, "SLIMBUS_8_TX"}, + + {"LSM1 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM1 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM1 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM1 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM1 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM1 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM1_UL_HL", NULL, "LSM1 MUX"}, + + {"LSM2 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM2 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM2 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM2 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM2 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM2 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM2_UL_HL", NULL, "LSM2 MUX"}, + + + {"LSM3 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM3 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM3 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM3 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM3 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM3 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM3_UL_HL", NULL, "LSM3 MUX"}, + + + {"LSM4 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM4 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM4 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM4 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM4 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM4 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM4_UL_HL", NULL, "LSM4 MUX"}, + + {"LSM5 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM5 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM5 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM5 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM5 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM5 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM5_UL_HL", NULL, "LSM5 MUX"}, + + {"LSM6 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM6 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM6 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM6 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM6 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM6_UL_HL", NULL, "LSM6 MUX"}, + + + {"LSM7 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM7 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM7 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM7 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM7 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM7_UL_HL", NULL, "LSM7 MUX"}, + + + {"LSM8 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM8 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM8 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM8 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM8 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM8_UL_HL", NULL, "LSM8 MUX"}, + + + {"CPE_LSM_UL_HL", NULL, "BE_IN"}, + {"QCHAT_Tx Mixer", "PRI_TX_QCHAT", "PRI_I2S_TX"}, + {"QCHAT_Tx Mixer", "SLIM_0_TX_QCHAT", "SLIMBUS_0_TX"}, + {"QCHAT_Tx Mixer", "SLIM_7_TX_QCHAT", "SLIMBUS_7_TX"}, + {"QCHAT_Tx Mixer", "SLIM_8_TX_QCHAT", "SLIMBUS_8_TX"}, + {"QCHAT_Tx Mixer", "INTERNAL_BT_SCO_TX_QCHAT", "INT_BT_SCO_TX"}, + {"QCHAT_Tx Mixer", "AFE_PCM_TX_QCHAT", "PCM_TX"}, + {"QCHAT_Tx Mixer", "AUX_PCM_TX_QCHAT", "AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "SEC_AUX_PCM_TX_QCHAT", "SEC_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "TERT_AUX_PCM_TX_QCHAT", "TERT_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "QUAT_AUX_PCM_TX_QCHAT", "QUAT_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "MI2S_TX_QCHAT", "MI2S_TX"}, + {"QCHAT_Tx Mixer", "PRI_MI2S_TX_QCHAT", "PRI_MI2S_TX"}, + {"QCHAT_Tx Mixer", "TERT_MI2S_TX_QCHAT", "TERT_MI2S_TX"}, + {"QCHAT_Tx Mixer", "INT3_MI2S_TX_QCHAT", "INT3_MI2S_TX"}, + {"QCHAT_Tx Mixer", "USB_AUDIO_TX_QCHAT", "USB_AUDIO_TX"}, + {"QCHAT_UL", NULL, "QCHAT_Tx Mixer"}, + + {"INT_FM_RX", NULL, "INTFM_DL_HL"}, + {"INTFM_UL_HL", NULL, "INT_FM_TX"}, + {"INTHFP_UL_HL", NULL, "HFP_PRI_AUX_UL_HL"}, + {"HFP_PRI_AUX_UL_HL", "Switch", "AUX_PCM_TX"}, + {"INTHFP_UL_HL", NULL, "HFP_AUX_UL_HL"}, + {"HFP_AUX_UL_HL", "Switch", "SEC_AUX_PCM_TX"}, + {"INTHFP_UL_HL", NULL, "HFP_INT_UL_HL"}, + {"HFP_INT_UL_HL", "Switch", "INT_BT_SCO_TX"}, + {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, + {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, + {"MI2S_RX", NULL, "MI2S_DL_HL"}, + {"MI2S_UL_HL", NULL, "MI2S_TX"}, + {"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"PCM_RX", NULL, "PCM_RX_DL_HL"}, + {"INT0_MI2S_RX_DL_HL", "Switch", "INT0_MI2S_DL_HL"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_DL_HL"}, + {"INT4_MI2S_RX_DL_HL", "Switch", "INT4_MI2S_DL_HL"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_DL_HL"}, + {"PRI_MI2S_RX_DL_HL", "Switch", "PRI_MI2S_DL_HL"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_DL_HL"}, + {"SEC_MI2S_RX_DL_HL", "Switch", "SEC_MI2S_DL_HL"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_DL_HL"}, + {"TERT_MI2S_RX_DL_HL", "Switch", "TERT_MI2S_DL_HL"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_DL_HL"}, + + {"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"}, + {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, + {"TERT_MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, + {"SEC_I2S_RX", NULL, "SEC_I2S_DL_HL"}, + {"PRI_MI2S_UL_HL", NULL, "PRI_MI2S_TX"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_DL_HL"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_DL_HL"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_DL_HL"}, + {"QUAT_MI2S_UL_HL", NULL, "QUAT_MI2S_TX"}, + + {"PRI_TDM_TX_0_UL_HL", NULL, "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_DL_HL"}, + {"SEC_TDM_TX_0_UL_HL", NULL, "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0_DL_HL"}, + {"TERT_TDM_TX_0_UL_HL", NULL, "TERT_TDM_TX_0"}, + {"TERT_TDM_TX_1_UL_HL", NULL, "TERT_TDM_TX_1"}, + {"TERT_TDM_TX_2_UL_HL", NULL, "TERT_TDM_TX_2"}, + {"TERT_TDM_TX_3_UL_HL", NULL, "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0_DL_HL"}, + {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_0_DL_HL"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_0_DL_HL"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_0_DL_HL"}, + {"QUAT_TDM_TX_0_UL_HL", NULL, "QUAT_TDM_TX_0"}, + {"QUAT_TDM_TX_1_UL_HL", NULL, "QUAT_TDM_TX_1"}, + {"QUAT_TDM_TX_2_UL_HL", NULL, "QUAT_TDM_TX_2"}, + {"QUAT_TDM_TX_3_UL_HL", NULL, "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0_DL_HL"}, + {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1_DL_HL"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_DL_HL"}, + {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3_DL_HL"}, + + {"TERT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Port Mixer"}, + + {"TERT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Port Mixer"}, + + {"TERT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Port Mixer"}, + + {"TERT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Port Mixer"}, + + {"QUAT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Port Mixer"}, + + {"QUAT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Port Mixer"}, + + {"QUAT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Port Mixer"}, + + {"QUAT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"}, + + {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"}, + {"AFE_PCM_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"AFE_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"PCM_RX", NULL, "AFE_PCM_RX Port Mixer"}, + + {"AUX_PCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"}, + + {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"}, + + {"TERT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_AUXPCM_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"TERT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"TERT_AUX_PCM_RX", NULL, "TERT_AUXPCM_RX Port Mixer"}, + + {"QUAT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_AUXPCM_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"QUAT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUXPCM_RX Port Mixer"}, + + {"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, + {"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"}, + {"Voice Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"}, + {"Voice Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"Voice Stub Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"Voice Stub Tx Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"Voice Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"Voice Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"}, + {"Voice Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"Voice Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"}, + + {"VoLTE Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"VoLTE Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"}, + {"VoLTE Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"VoLTE Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"VoLTE Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"VoLTE Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"VOLTE_STUB_UL", NULL, "VoLTE Stub Tx Mixer"}, + + {"Voice2 Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"Voice2 Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"}, + {"Voice2 Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"Voice2 Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"Voice2 Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"Voice2 Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"VOICE2_STUB_UL", NULL, "Voice2 Stub Tx Mixer"}, + + {"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"STUB_RX Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"STUB_RX Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"STUB_RX", NULL, "STUB_RX Mixer"}, + {"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SLIMBUS_1_RX Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"SLIMBUS_1_RX Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"}, + {"AFE_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"AFE_PCM_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIMBUS_3_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SLIMBUS_3_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"SLIMBUS_3_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX_Voice Mixer"}, + + {"SLIM_7_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SLIM_7_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SLIM_7_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_7_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_7_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SLIM_7_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIM_7_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_7_RX", NULL, "SLIM_7_RX_Voice Mixer"}, + + {"SLIM_8_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"SLIM_8_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"SLIM_8_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_8_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_8_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SLIM_8_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"SLIM_8_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_8_RX", NULL, "SLIM_8_RX_Voice Mixer"}, + + {"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SLIMBUS_1_RX Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SLIMBUS_1_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"}, + {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"}, + {"SLIMBUS_3_RX Port Mixer", "INTERNAL_BT_SCO_RX", "INT_BT_SCO_RX"}, + {"SLIMBUS_3_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SLIMBUS_3_RX Port Mixer", "AFE_PCM_RX", "PCM_RX"}, + {"SLIMBUS_3_RX Port Mixer", "AUX_PCM_RX", "AUX_PCM_RX"}, + {"SLIMBUS_3_RX Port Mixer", "SLIM_0_RX", "SLIMBUS_0_RX"}, + {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX Port Mixer"}, + + {"SLIMBUS_6_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"SLIMBUS_6_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SLIMBUS_6_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Port Mixer"}, + + {"HDMI_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"HDMI", NULL, "HDMI_RX Port Mixer"}, + + {"DISPLAY_PORT_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX Port Mixer"}, + + {"SEC_I2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"}, + + {"MI2S_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"MI2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"MI2S_RX", NULL, "MI2S_RX Port Mixer"}, + + {"PRI_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"}, + + {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"}, + + {"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"}, + + {"QUAT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"}, + + /* Backend Enablement */ + + {"BE_OUT", NULL, "PRI_I2S_RX"}, + {"BE_OUT", NULL, "SEC_I2S_RX"}, + {"BE_OUT", NULL, "SLIMBUS_0_RX"}, + {"BE_OUT", NULL, "SLIMBUS_1_RX"}, + {"BE_OUT", NULL, "SLIMBUS_2_RX"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "SLIMBUS_4_RX"}, + {"BE_OUT", NULL, "SLIMBUS_5_RX"}, + {"BE_OUT", NULL, "SLIMBUS_6_RX"}, + {"BE_OUT", NULL, "SLIMBUS_7_RX"}, + {"BE_OUT", NULL, "SLIMBUS_8_RX"}, + {"BE_OUT", NULL, "USB_AUDIO_RX"}, + {"BE_OUT", NULL, "HDMI"}, + {"BE_OUT", NULL, "DISPLAY_PORT"}, + {"BE_OUT", NULL, "SPDIF_RX"}, + {"BE_OUT", NULL, "MI2S_RX"}, + {"BE_OUT", NULL, "QUAT_MI2S_RX"}, + {"BE_OUT", NULL, "QUIN_MI2S_RX"}, + {"BE_OUT", NULL, "TERT_MI2S_RX"}, + {"BE_OUT", NULL, "SEC_MI2S_RX"}, + {"BE_OUT", NULL, "SEC_MI2S_RX_SD1"}, + {"BE_OUT", NULL, "PRI_MI2S_RX"}, + {"BE_OUT", NULL, "INT0_MI2S_RX"}, + {"BE_OUT", NULL, "INT4_MI2S_RX"}, + {"BE_OUT", NULL, "INT_BT_SCO_RX"}, + {"BE_OUT", NULL, "INT_BT_A2DP_RX"}, + {"BE_OUT", NULL, "INT_FM_RX"}, + {"BE_OUT", NULL, "PCM_RX"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "AUX_PCM_RX"}, + {"BE_OUT", NULL, "SEC_AUX_PCM_RX"}, + {"BE_OUT", NULL, "TERT_AUX_PCM_RX"}, + {"BE_OUT", NULL, "QUAT_AUX_PCM_RX"}, + {"BE_OUT", NULL, "INT_BT_SCO_RX"}, + {"BE_OUT", NULL, "INT_FM_RX"}, + {"BE_OUT", NULL, "PCM_RX"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "VOICE_PLAYBACK_TX"}, + {"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"}, + {"BE_OUT", NULL, "PRI_TDM_RX_0"}, + {"BE_OUT", NULL, "SEC_TDM_RX_0"}, + {"BE_OUT", NULL, "TERT_TDM_RX_0"}, + {"BE_OUT", NULL, "TERT_TDM_RX_1"}, + {"BE_OUT", NULL, "TERT_TDM_RX_2"}, + {"BE_OUT", NULL, "TERT_TDM_RX_3"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_0"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_1"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_2"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_3"}, + + {"PRI_I2S_TX", NULL, "BE_IN"}, + {"MI2S_TX", NULL, "BE_IN"}, + {"QUAT_MI2S_TX", NULL, "BE_IN"}, + {"QUIN_MI2S_TX", NULL, "BE_IN"}, + {"PRI_MI2S_TX", NULL, "BE_IN"}, + {"TERT_MI2S_TX", NULL, "BE_IN"}, + {"INT2_MI2S_TX", NULL, "BE_IN"}, + {"INT3_MI2S_TX", NULL, "BE_IN"}, + {"SEC_MI2S_TX", NULL, "BE_IN"}, + {"SENARY_MI2S_TX", NULL, "BE_IN" }, + {"SLIMBUS_0_TX", NULL, "BE_IN" }, + {"SLIMBUS_1_TX", NULL, "BE_IN" }, + {"SLIMBUS_3_TX", NULL, "BE_IN" }, + {"SLIMBUS_4_TX", NULL, "BE_IN" }, + {"SLIMBUS_5_TX", NULL, "BE_IN" }, + {"SLIMBUS_6_TX", NULL, "BE_IN" }, + {"SLIMBUS_7_TX", NULL, "BE_IN" }, + {"SLIMBUS_8_TX", NULL, "BE_IN" }, + {"USB_AUDIO_TX", NULL, "BE_IN" }, + {"INT_BT_SCO_TX", NULL, "BE_IN"}, + {"INT_FM_TX", NULL, "BE_IN"}, + {"PCM_TX", NULL, "BE_IN"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "STUB_RX"}, + {"STUB_TX", NULL, "BE_IN"}, + {"STUB_1_TX", NULL, "BE_IN"}, + {"BE_OUT", NULL, "AUX_PCM_RX"}, + {"AUX_PCM_TX", NULL, "BE_IN"}, + {"SEC_AUX_PCM_TX", NULL, "BE_IN"}, + {"TERT_AUX_PCM_TX", NULL, "BE_IN"}, + {"QUAT_AUX_PCM_TX", NULL, "BE_IN"}, + {"INCALL_RECORD_TX", NULL, "BE_IN"}, + {"INCALL_RECORD_RX", NULL, "BE_IN"}, + {"SLIM0_RX_VI_FB_LCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"}, + {"SLIM0_RX_VI_FB_RCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"}, + {"PRI_MI2S_RX_VI_FB_MUX", "SENARY_TX", "SENARY_TX"}, + {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_LCH_MUX"}, + {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_RCH_MUX"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, + {"PRI_TDM_TX_0", NULL, "BE_IN"}, + {"SEC_TDM_TX_0", NULL, "BE_IN"}, + {"TERT_TDM_TX_0", NULL, "BE_IN"}, + {"TERT_TDM_TX_1", NULL, "BE_IN"}, + {"TERT_TDM_TX_2", NULL, "BE_IN"}, + {"TERT_TDM_TX_3", NULL, "BE_IN"}, + {"QUAT_TDM_TX_0", NULL, "BE_IN"}, + {"QUAT_TDM_TX_1", NULL, "BE_IN"}, + {"QUAT_TDM_TX_2", NULL, "BE_IN"}, + {"QUAT_TDM_TX_3", NULL, "BE_IN"}, +}; + +static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->dai_link->be_id; + + if (be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: unexpected be_id %d\n", __func__, be_id); + return -EINVAL; + } + + mutex_lock(&routing_lock); + msm_bedais[be_id].sample_rate = params_rate(params); + msm_bedais[be_id].channel = params_channels(params); + msm_bedais[be_id].format = params_format(params); + pr_debug("%s: BE Sample Rate (%d) format (%d) be_id %d\n", + __func__, msm_bedais[be_id].sample_rate, + msm_bedais[be_id].format, be_id); + mutex_unlock(&routing_lock); + return 0; +} + +static int msm_pcm_routing_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->dai_link->be_id; + int i, session_type, path_type, topology; + struct msm_pcm_routing_bdai_data *bedai; + struct msm_pcm_routing_fdai_data *fdai; + + pr_debug("%s: substream->pcm->id:%s\n", + __func__, substream->pcm->id); + + if (be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: unexpected be_id %d\n", __func__, be_id); + return -EINVAL; + } + + bedai = &msm_bedais[be_id]; + session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + 0 : 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + path_type = ADM_PATH_PLAYBACK; + else + path_type = ADM_PATH_LIVE_REC; + + mutex_lock(&routing_lock); + for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) { + fdai = &fe_dai_map[i][session_type]; + if (fdai->strm_id != INVALID_SESSION) { + int idx; + int port_id; + unsigned long copp = + session_copp_map[i][session_type][be_id]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (test_bit(idx, &copp)) + break; + fdai->be_srate = bedai->sample_rate; + port_id = bedai->port_id; + topology = adm_get_topology_for_port_copp_idx(port_id, + idx); + adm_close(bedai->port_id, fdai->perf_mode, idx); + pr_debug("%s: copp:%ld,idx bit fe:%d, type:%d,be:%d topology=0x%x\n", + __func__, copp, i, session_type, be_id, + topology); + clear_bit(idx, + &session_copp_map[i][session_type][be_id]); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (bedai->compr_passthr_mode == LEGACY_PCM)) + msm_pcm_routing_deinit_pp(bedai->port_id, + topology); + } + } + + bedai->compr_passthr_mode = LEGACY_PCM; + bedai->active = 0; + bedai->sample_rate = 0; + bedai->channel = 0; + mutex_unlock(&routing_lock); + + return 0; +} + +static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->dai_link->be_id; + int i, path_type, session_type, topology; + struct msm_pcm_routing_bdai_data *bedai; + u32 channels, sample_rate; + uint16_t bits_per_sample = 16, voc_path_type; + struct msm_pcm_routing_fdai_data *fdai; + u32 session_id; + + pr_debug("%s: substream->pcm->id:%s\n", + __func__, substream->pcm->id); + + if (be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: unexpected be_id %d\n", __func__, be_id); + return -EINVAL; + } + + bedai = &msm_bedais[be_id]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (bedai->compr_passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + session_type = SESSION_TYPE_RX; + } else { + path_type = ADM_PATH_LIVE_REC; + session_type = SESSION_TYPE_TX; + } + + mutex_lock(&routing_lock); + if (bedai->active == 1) + goto done; /* Ignore prepare if back-end already active */ + + /* AFE port is not active at this point. However, still + * go ahead setting active flag under the notion that + * QDSP6 is able to handle ADM starting before AFE port + * is started. + */ + bedai->active = 1; + + for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) { + fdai = &fe_dai_map[i][session_type]; + if (fdai->strm_id != INVALID_SESSION) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + + if (session_type == SESSION_TYPE_TX && + fdai->be_srate && + (fdai->be_srate != bedai->sample_rate)) { + pr_debug("%s: flush strm %d diff BE rates\n", + __func__, + fdai->strm_id); + + if (fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_BUF_RECFG, + fdai->event_info.priv_data); + fdai->be_srate = 0; /* might not need it */ + } + bits_per_sample = msm_routing_get_bit_width( + bedai->format); + + app_type = + fe_dai_app_type_cfg[i][session_type].app_type; + if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[i][session_type]. + sample_rate; + bits_per_sample = + app_type_cfg[app_type_idx].bit_width; + } else + sample_rate = bedai->sample_rate; + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!bedai->adm_override_ch) + channels = bedai->channel; + else + channels = bedai->adm_override_ch; + acdb_dev_id = + fe_dai_app_type_cfg[i][session_type].acdb_dev_id; + topology = msm_routing_get_adm_topology(path_type, i, + session_type); + copp_idx = adm_open(bedai->port_id, path_type, + sample_rate, channels, topology, + fdai->perf_mode, bits_per_sample, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed\n", __func__); + mutex_unlock(&routing_lock); + return -EINVAL; + } + pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n", + __func__, i, session_type, be_id); + set_bit(copp_idx, + &session_copp_map[i][session_type][be_id]); + + if (msm_is_resample_needed( + sample_rate, + bedai->sample_rate)) + adm_copp_mfc_cfg( + bedai->port_id, copp_idx, + bedai->sample_rate); + + msm_pcm_routing_build_matrix(i, session_type, path_type, + fdai->perf_mode); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (bedai->compr_passthr_mode == + LEGACY_PCM)) + msm_pcm_routing_cfg_pp(bedai->port_id, copp_idx, + topology, channels); + } + } + + for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MAX) { + session_id = msm_pcm_routing_get_voc_sessionid(i); + if (session_id) { + pr_debug("%s voice session_id: 0x%x", + __func__, session_id); + + if (session_type == SESSION_TYPE_TX) + voc_path_type = TX_PATH; + else + voc_path_type = RX_PATH; + + voc_set_route_flag(session_id, voc_path_type, 1); + voc_set_device_config(session_id, voc_path_type, + bedai->channel, bedai->port_id); + + if (voc_get_route_flag(session_id, RX_PATH) && + voc_get_route_flag(session_id, TX_PATH)) + voc_enable_device(session_id); + } + } + +done: + mutex_unlock(&routing_lock); + + return 0; +} + +static int msm_routing_send_device_pp_params(int port_id, int copp_idx) +{ + int index, topo_id, be_idx; + unsigned long pp_config = 0; + bool mute_on; + int latency; + + pr_debug("%s: port_id %d, copp_idx %d\n", __func__, port_id, copp_idx); + + if (port_id != HDMI_RX && port_id != DISPLAY_PORT_RX) { + pr_err("%s: Device pp params on invalid port %d\n", + __func__, port_id); + return -EINVAL; + } + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + if (port_id == msm_bedais[be_idx].port_id) + break; + } + + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_debug("%s: Invalid be id %d\n", __func__, be_idx); + return -EINVAL; + } + + for (index = 0; index < MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX; index++) { + if (msm_bedais_pp_params[index].port_id == port_id) + break; + } + if (index >= MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX) { + pr_err("%s: Invalid backend pp params index %d\n", + __func__, index); + return -EINVAL; + } + + topo_id = adm_get_topology_for_port_copp_idx(port_id, copp_idx); + if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY) { + pr_err("%s: Invalid passthrough topology 0x%x\n", + __func__, topo_id); + return -EINVAL; + } + + pp_config = msm_bedais_pp_params[index].pp_params_config; + if (test_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config)) { + pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__); + clear_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config); + mute_on = msm_bedais_pp_params[index].mute_on; + if ((msm_bedais[be_idx].active) && + (msm_bedais[be_idx].compr_passthr_mode != + LEGACY_PCM)) + adm_send_compressed_device_mute(port_id, + copp_idx, + mute_on); + } + if (test_bit(ADM_PP_PARAM_LATENCY_BIT, &pp_config)) { + pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__); + clear_bit(ADM_PP_PARAM_LATENCY_BIT, + &pp_config); + latency = msm_bedais_pp_params[index].latency; + if ((msm_bedais[be_idx].active) && + (msm_bedais[be_idx].compr_passthr_mode != + LEGACY_PCM)) + adm_send_compressed_device_latency(port_id, + copp_idx, + latency); + } + return 0; +} + +static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int pp_id = ucontrol->value.integer.value[0]; + int port_id = 0; + int index, be_idx, i, topo_id, idx; + bool mute; + int latency; + + pr_debug("%s: pp_id: 0x%x\n", __func__, pp_id); + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + port_id = msm_bedais[be_idx].port_id; + if (port_id == HDMI_RX || port_id == DISPLAY_PORT_RX) + break; + } + + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_debug("%s: Invalid be id %d\n", __func__, be_idx); + return -EINVAL; + } + + for (index = 0; index < MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX; index++) { + if (msm_bedais_pp_params[index].port_id == port_id) + break; + } + if (index >= MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX) { + pr_err("%s: Invalid pp params backend index %d\n", + __func__, index); + return -EINVAL; + } + + for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions, + MSM_FRONTEND_DAI_MM_SIZE) { + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp = + session_copp_map[i] + [SESSION_TYPE_RX][be_idx]; + if (!test_bit(idx, &copp)) + continue; + topo_id = adm_get_topology_for_port_copp_idx(port_id, + idx); + if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY) + continue; + pr_debug("%s: port: 0x%x, copp %ld, be active: %d, passt: %d\n", + __func__, port_id, copp, msm_bedais[be_idx].active, + msm_bedais[be_idx].compr_passthr_mode); + switch (pp_id) { + case ADM_PP_PARAM_MUTE_ID: + pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__); + mute = ucontrol->value.integer.value[1] ? true : false; + msm_bedais_pp_params[index].mute_on = mute; + set_bit(ADM_PP_PARAM_MUTE_BIT, + &msm_bedais_pp_params[index].pp_params_config); + if ((msm_bedais[be_idx].active) && + (msm_bedais[be_idx].compr_passthr_mode != + LEGACY_PCM)) + adm_send_compressed_device_mute(port_id, + idx, mute); + break; + case ADM_PP_PARAM_LATENCY_ID: + pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__); + msm_bedais_pp_params[index].latency = + ucontrol->value.integer.value[1]; + set_bit(ADM_PP_PARAM_LATENCY_BIT, + &msm_bedais_pp_params[index].pp_params_config); + latency = msm_bedais_pp_params[index].latency = + ucontrol->value.integer.value[1]; + if ((msm_bedais[be_idx].active) && + (msm_bedais[be_idx].compr_passthr_mode != + LEGACY_PCM)) + adm_send_compressed_device_latency(port_id, + idx, latency); + break; + default: + pr_info("%s, device pp param %d not supported\n", + __func__, pp_id); + break; + } + } + } + return 0; +} + +static int msm_routing_get_device_pp_params_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s:msm_routing_get_device_pp_params_mixer", __func__); + return 0; +} + +static const struct snd_kcontrol_new device_pp_params_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("Device PP Params", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, 3, msm_routing_get_device_pp_params_mixer, + msm_routing_put_device_pp_params_mixer), +}; + +static struct snd_pcm_ops msm_routing_pcm_ops = { + .hw_params = msm_pcm_routing_hw_params, + .close = msm_pcm_routing_close, + .prepare = msm_pcm_routing_prepare, +}; + +/* Not used but frame seems to require it */ +static int msm_routing_probe(struct snd_soc_platform *platform) +{ + snd_soc_dapm_new_controls(&platform->component.dapm, msm_qdsp6_widgets, + ARRAY_SIZE(msm_qdsp6_widgets)); + snd_soc_dapm_add_routes(&platform->component.dapm, intercon, + ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(platform->component.dapm.card); + + snd_soc_add_platform_controls(platform, lsm_function, + ARRAY_SIZE(lsm_function)); + + snd_soc_add_platform_controls(platform, aanc_slim_0_rx_mux, + ARRAY_SIZE(aanc_slim_0_rx_mux)); + + snd_soc_add_platform_controls(platform, msm_voc_session_controls, + ARRAY_SIZE(msm_voc_session_controls)); + + snd_soc_add_platform_controls(platform, app_type_cfg_controls, + ARRAY_SIZE(app_type_cfg_controls)); + + snd_soc_add_platform_controls(platform, + stereo_to_custom_stereo_controls, + ARRAY_SIZE(stereo_to_custom_stereo_controls)); + + msm_qti_pp_add_controls(platform); + + msm_dts_srs_tm_add_controls(platform); + + msm_dolby_dap_add_controls(platform); + + snd_soc_add_platform_controls(platform, + use_ds1_or_ds2_controls, + ARRAY_SIZE(use_ds1_or_ds2_controls)); + + snd_soc_add_platform_controls(platform, + device_pp_params_mixer_controls, + ARRAY_SIZE(device_pp_params_mixer_controls)); + + msm_dts_eagle_add_controls(platform); + + snd_soc_add_platform_controls(platform, msm_source_tracking_controls, + ARRAY_SIZE(msm_source_tracking_controls)); + snd_soc_add_platform_controls(platform, adm_channel_config_controls, + ARRAY_SIZE(adm_channel_config_controls)); + return 0; +} + +int msm_routing_pcm_new(struct snd_soc_pcm_runtime *runtime) +{ + return msm_pcm_routing_hwdep_new(runtime, msm_bedais); +} + +void msm_routing_pcm_free(struct snd_pcm *pcm) +{ + msm_pcm_routing_hwdep_free(pcm); +} + +static struct snd_soc_platform_driver msm_soc_routing_platform = { + .ops = &msm_routing_pcm_ops, + .probe = msm_routing_probe, + .pcm_new = msm_routing_pcm_new, + .pcm_free = msm_routing_pcm_free, +}; + +static int msm_routing_pcm_probe(struct platform_device *pdev) +{ + + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_routing_platform); +} + +static int msm_routing_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_routing_dt_match[] = { + {.compatible = "qcom,msm-pcm-routing"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_routing_dt_match); + +static struct platform_driver msm_routing_pcm_driver = { + .driver = { + .name = "msm-pcm-routing", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_routing_dt_match, + }, + .probe = msm_routing_pcm_probe, + .remove = msm_routing_pcm_remove, +}; + +int msm_routing_check_backend_enabled(int fedai_id) +{ + int i; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID\n", __func__); + return 0; + } + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (test_bit(fedai_id, &msm_bedais[i].fe_sessions)) + return msm_bedais[i].active; + } + return 0; +} + +static int get_cal_path(int path_type) +{ + if (path_type == ADM_PATH_PLAYBACK || + path_type == ADM_PATH_COMPRESSED_RX) + return RX_DEVICE; + else + return TX_DEVICE; +} + +static int msm_routing_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + ret = cal_utils_set_cal(data_size, data, cal_data, 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static void msm_routing_delete_cal_data(void) +{ + pr_debug("%s\n", __func__); + + cal_utils_destroy_cal_types(1, &cal_data); +} + +static int msm_routing_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info = { + {ADM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + msm_routing_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} + }; + pr_debug("%s\n", __func__); + + ret = cal_utils_create_cal_types(1, &cal_data, + &cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type!\n", + __func__); + ret = -EINVAL; + goto err; + } + + return ret; +err: + msm_routing_delete_cal_data(); + return ret; +} + +static int __init msm_soc_routing_platform_init(void) +{ + mutex_init(&routing_lock); + if (msm_routing_init_cal_data()) + pr_err("%s: could not init cal data!\n", __func__); + + return platform_driver_register(&msm_routing_pcm_driver); +} +module_init(msm_soc_routing_platform_init); + +static void __exit msm_soc_routing_platform_exit(void) +{ + msm_routing_delete_cal_data(); + platform_driver_unregister(&msm_routing_pcm_driver); +} +module_exit(msm_soc_routing_platform_exit); + +MODULE_DESCRIPTION("MSM routing platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h new file mode 100644 index 000000000000..d64fd640618e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h @@ -0,0 +1,472 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MSM_PCM_ROUTING_H +#define _MSM_PCM_ROUTING_H +#include + +#define LPASS_BE_PRI_I2S_RX "PRIMARY_I2S_RX" +#define LPASS_BE_PRI_I2S_TX "PRIMARY_I2S_TX" +#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX" +#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX" +#define LPASS_BE_HDMI "HDMI" +#define LPASS_BE_DISPLAY_PORT "DISPLAY_PORT" +#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX" +#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX" +#define LPASS_BE_INT_BT_A2DP_RX "INT_BT_A2DP_RX" +#define LPASS_BE_INT_FM_RX "INT_FM_RX" +#define LPASS_BE_INT_FM_TX "INT_FM_TX" +#define LPASS_BE_AFE_PCM_RX "RT_PROXY_DAI_001_RX" +#define LPASS_BE_AFE_PCM_TX "RT_PROXY_DAI_002_TX" +#define LPASS_BE_AUXPCM_RX "AUX_PCM_RX" +#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX" +#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX" +#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX" +#define LPASS_BE_TERT_AUXPCM_RX "TERT_AUX_PCM_RX" +#define LPASS_BE_TERT_AUXPCM_TX "TERT_AUX_PCM_TX" +#define LPASS_BE_QUAT_AUXPCM_RX "QUAT_AUX_PCM_RX" +#define LPASS_BE_QUAT_AUXPCM_TX "QUAT_AUX_PCM_TX" +#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX" +#define LPASS_BE_VOICE2_PLAYBACK_TX "VOICE2_PLAYBACK_TX" +#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_RX" +#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_TX" +#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX" +#define LPASS_BE_SPDIF_RX "SPDIF_RX" + +#define LPASS_BE_MI2S_RX "MI2S_RX" +#define LPASS_BE_MI2S_TX "MI2S_TX" +#define LPASS_BE_QUAT_MI2S_RX "QUAT_MI2S_RX" +#define LPASS_BE_QUAT_MI2S_TX "QUAT_MI2S_TX" +#define LPASS_BE_SEC_MI2S_RX "SEC_MI2S_RX" +#define LPASS_BE_SEC_MI2S_RX_SD1 "SEC_MI2S_RX_SD1" +#define LPASS_BE_SEC_MI2S_TX "SEC_MI2S_TX" +#define LPASS_BE_PRI_MI2S_RX "PRI_MI2S_RX" +#define LPASS_BE_PRI_MI2S_TX "PRI_MI2S_TX" +#define LPASS_BE_TERT_MI2S_RX "TERTIARY_MI2S_RX" +#define LPASS_BE_TERT_MI2S_TX "TERTIARY_MI2S_TX" +#define LPASS_BE_AUDIO_I2S_RX "AUDIO_I2S_RX" +#define LPASS_BE_STUB_RX "STUB_RX" +#define LPASS_BE_STUB_TX "STUB_TX" +#define LPASS_BE_SLIMBUS_1_RX "SLIMBUS_1_RX" +#define LPASS_BE_SLIMBUS_1_TX "SLIMBUS_1_TX" +#define LPASS_BE_STUB_1_TX "STUB_1_TX" +#define LPASS_BE_SLIMBUS_2_RX "SLIMBUS_2_RX" +#define LPASS_BE_SLIMBUS_2_TX "SLIMBUS_2_TX" +#define LPASS_BE_SLIMBUS_3_RX "SLIMBUS_3_RX" +#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX" +#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX" +#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX" +#define LPASS_BE_SLIMBUS_5_RX "SLIMBUS_5_RX" +#define LPASS_BE_SLIMBUS_5_TX "SLIMBUS_5_TX" +#define LPASS_BE_SLIMBUS_6_RX "SLIMBUS_6_RX" +#define LPASS_BE_SLIMBUS_6_TX "SLIMBUS_6_TX" +#define LPASS_BE_QUIN_MI2S_RX "QUIN_MI2S_RX" +#define LPASS_BE_QUIN_MI2S_TX "QUIN_MI2S_TX" +#define LPASS_BE_SENARY_MI2S_TX "SENARY_MI2S_TX" + +#define LPASS_BE_PRI_TDM_RX_0 "PRI_TDM_RX_0" +#define LPASS_BE_PRI_TDM_TX_0 "PRI_TDM_TX_0" +#define LPASS_BE_PRI_TDM_RX_1 "PRI_TDM_RX_1" +#define LPASS_BE_PRI_TDM_TX_1 "PRI_TDM_TX_1" +#define LPASS_BE_PRI_TDM_RX_2 "PRI_TDM_RX_2" +#define LPASS_BE_PRI_TDM_TX_2 "PRI_TDM_TX_2" +#define LPASS_BE_PRI_TDM_RX_3 "PRI_TDM_RX_3" +#define LPASS_BE_PRI_TDM_TX_3 "PRI_TDM_TX_3" +#define LPASS_BE_PRI_TDM_RX_4 "PRI_TDM_RX_4" +#define LPASS_BE_PRI_TDM_TX_4 "PRI_TDM_TX_4" +#define LPASS_BE_PRI_TDM_RX_5 "PRI_TDM_RX_5" +#define LPASS_BE_PRI_TDM_TX_5 "PRI_TDM_TX_5" +#define LPASS_BE_PRI_TDM_RX_6 "PRI_TDM_RX_6" +#define LPASS_BE_PRI_TDM_TX_6 "PRI_TDM_TX_6" +#define LPASS_BE_PRI_TDM_RX_7 "PRI_TDM_RX_7" +#define LPASS_BE_PRI_TDM_TX_7 "PRI_TDM_TX_7" +#define LPASS_BE_SEC_TDM_RX_0 "SEC_TDM_RX_0" +#define LPASS_BE_SEC_TDM_TX_0 "SEC_TDM_TX_0" +#define LPASS_BE_SEC_TDM_RX_1 "SEC_TDM_RX_1" +#define LPASS_BE_SEC_TDM_TX_1 "SEC_TDM_TX_1" +#define LPASS_BE_SEC_TDM_RX_2 "SEC_TDM_RX_2" +#define LPASS_BE_SEC_TDM_TX_2 "SEC_TDM_TX_2" +#define LPASS_BE_SEC_TDM_RX_3 "SEC_TDM_RX_3" +#define LPASS_BE_SEC_TDM_TX_3 "SEC_TDM_TX_3" +#define LPASS_BE_SEC_TDM_RX_4 "SEC_TDM_RX_4" +#define LPASS_BE_SEC_TDM_TX_4 "SEC_TDM_TX_4" +#define LPASS_BE_SEC_TDM_RX_5 "SEC_TDM_RX_5" +#define LPASS_BE_SEC_TDM_TX_5 "SEC_TDM_TX_5" +#define LPASS_BE_SEC_TDM_RX_6 "SEC_TDM_RX_6" +#define LPASS_BE_SEC_TDM_TX_6 "SEC_TDM_TX_6" +#define LPASS_BE_SEC_TDM_RX_7 "SEC_TDM_RX_7" +#define LPASS_BE_SEC_TDM_TX_7 "SEC_TDM_TX_7" +#define LPASS_BE_TERT_TDM_RX_0 "TERT_TDM_RX_0" +#define LPASS_BE_TERT_TDM_TX_0 "TERT_TDM_TX_0" +#define LPASS_BE_TERT_TDM_RX_1 "TERT_TDM_RX_1" +#define LPASS_BE_TERT_TDM_TX_1 "TERT_TDM_TX_1" +#define LPASS_BE_TERT_TDM_RX_2 "TERT_TDM_RX_2" +#define LPASS_BE_TERT_TDM_TX_2 "TERT_TDM_TX_2" +#define LPASS_BE_TERT_TDM_RX_3 "TERT_TDM_RX_3" +#define LPASS_BE_TERT_TDM_TX_3 "TERT_TDM_TX_3" +#define LPASS_BE_TERT_TDM_RX_4 "TERT_TDM_RX_4" +#define LPASS_BE_TERT_TDM_TX_4 "TERT_TDM_TX_4" +#define LPASS_BE_TERT_TDM_RX_5 "TERT_TDM_RX_5" +#define LPASS_BE_TERT_TDM_TX_5 "TERT_TDM_TX_5" +#define LPASS_BE_TERT_TDM_RX_6 "TERT_TDM_RX_6" +#define LPASS_BE_TERT_TDM_TX_6 "TERT_TDM_TX_6" +#define LPASS_BE_TERT_TDM_RX_7 "TERT_TDM_RX_7" +#define LPASS_BE_TERT_TDM_TX_7 "TERT_TDM_TX_7" +#define LPASS_BE_QUAT_TDM_RX_0 "QUAT_TDM_RX_0" +#define LPASS_BE_QUAT_TDM_TX_0 "QUAT_TDM_TX_0" +#define LPASS_BE_QUAT_TDM_RX_1 "QUAT_TDM_RX_1" +#define LPASS_BE_QUAT_TDM_TX_1 "QUAT_TDM_TX_1" +#define LPASS_BE_QUAT_TDM_RX_2 "QUAT_TDM_RX_2" +#define LPASS_BE_QUAT_TDM_TX_2 "QUAT_TDM_TX_2" +#define LPASS_BE_QUAT_TDM_RX_3 "QUAT_TDM_RX_3" +#define LPASS_BE_QUAT_TDM_TX_3 "QUAT_TDM_TX_3" +#define LPASS_BE_QUAT_TDM_RX_4 "QUAT_TDM_RX_4" +#define LPASS_BE_QUAT_TDM_TX_4 "QUAT_TDM_TX_4" +#define LPASS_BE_QUAT_TDM_RX_5 "QUAT_TDM_RX_5" +#define LPASS_BE_QUAT_TDM_TX_5 "QUAT_TDM_TX_5" +#define LPASS_BE_QUAT_TDM_RX_6 "QUAT_TDM_RX_6" +#define LPASS_BE_QUAT_TDM_TX_6 "QUAT_TDM_TX_6" +#define LPASS_BE_QUAT_TDM_RX_7 "QUAT_TDM_RX_7" +#define LPASS_BE_QUAT_TDM_TX_7 "QUAT_TDM_TX_7" + +#define LPASS_BE_SLIMBUS_7_RX "SLIMBUS_7_RX" +#define LPASS_BE_SLIMBUS_7_TX "SLIMBUS_7_TX" +#define LPASS_BE_SLIMBUS_8_RX "SLIMBUS_8_RX" +#define LPASS_BE_SLIMBUS_8_TX "SLIMBUS_8_TX" + +#define LPASS_BE_USB_AUDIO_RX "USB_AUDIO_RX" +#define LPASS_BE_USB_AUDIO_TX "USB_AUDIO_TX" + +#define LPASS_BE_INT0_MI2S_RX "INT0_MI2S_RX" +#define LPASS_BE_INT0_MI2S_TX "INT0_MI2S_TX" +#define LPASS_BE_INT1_MI2S_RX "INT1_MI2S_RX" +#define LPASS_BE_INT1_MI2S_TX "INT1_MI2S_TX" +#define LPASS_BE_INT2_MI2S_RX "INT2_MI2S_RX" +#define LPASS_BE_INT2_MI2S_TX "INT2_MI2S_TX" +#define LPASS_BE_INT3_MI2S_RX "INT3_MI2S_RX" +#define LPASS_BE_INT3_MI2S_TX "INT3_MI2S_TX" +#define LPASS_BE_INT4_MI2S_RX "INT4_MI2S_RX" +#define LPASS_BE_INT4_MI2S_TX "INT4_MI2S_TX" +#define LPASS_BE_INT5_MI2S_RX "INT5_MI2S_RX" +#define LPASS_BE_INT5_MI2S_TX "INT5_MI2S_TX" +#define LPASS_BE_INT6_MI2S_RX "INT6_MI2S_RX" +#define LPASS_BE_INT6_MI2S_TX "INT6_MI2S_TX" +/* For multimedia front-ends, asm session is allocated dynamically. + * Hence, asm session/multimedia front-end mapping has to be maintained. + * Due to this reason, additional multimedia front-end must be placed before + * non-multimedia front-ends. + */ + +enum { + MSM_FRONTEND_DAI_MULTIMEDIA1 = 0, + MSM_FRONTEND_DAI_MULTIMEDIA2, + MSM_FRONTEND_DAI_MULTIMEDIA3, + MSM_FRONTEND_DAI_MULTIMEDIA4, + MSM_FRONTEND_DAI_MULTIMEDIA5, + MSM_FRONTEND_DAI_MULTIMEDIA6, + MSM_FRONTEND_DAI_MULTIMEDIA7, + MSM_FRONTEND_DAI_MULTIMEDIA8, + MSM_FRONTEND_DAI_MULTIMEDIA9, + MSM_FRONTEND_DAI_MULTIMEDIA10, + MSM_FRONTEND_DAI_MULTIMEDIA11, + MSM_FRONTEND_DAI_MULTIMEDIA12, + MSM_FRONTEND_DAI_MULTIMEDIA13, + MSM_FRONTEND_DAI_MULTIMEDIA14, + MSM_FRONTEND_DAI_MULTIMEDIA15, + MSM_FRONTEND_DAI_MULTIMEDIA16, + MSM_FRONTEND_DAI_MULTIMEDIA17, + MSM_FRONTEND_DAI_MULTIMEDIA18, + MSM_FRONTEND_DAI_MULTIMEDIA19, + MSM_FRONTEND_DAI_CS_VOICE, + MSM_FRONTEND_DAI_VOIP, + MSM_FRONTEND_DAI_AFE_RX, + MSM_FRONTEND_DAI_AFE_TX, + MSM_FRONTEND_DAI_VOICE_STUB, + MSM_FRONTEND_DAI_VOLTE, + MSM_FRONTEND_DAI_DTMF_RX, + MSM_FRONTEND_DAI_VOICE2, + MSM_FRONTEND_DAI_QCHAT, + MSM_FRONTEND_DAI_VOLTE_STUB, + MSM_FRONTEND_DAI_LSM1, + MSM_FRONTEND_DAI_LSM2, + MSM_FRONTEND_DAI_LSM3, + MSM_FRONTEND_DAI_LSM4, + MSM_FRONTEND_DAI_LSM5, + MSM_FRONTEND_DAI_LSM6, + MSM_FRONTEND_DAI_LSM7, + MSM_FRONTEND_DAI_LSM8, + MSM_FRONTEND_DAI_VOICE2_STUB, + MSM_FRONTEND_DAI_VOWLAN, + MSM_FRONTEND_DAI_VOICEMMODE1, + MSM_FRONTEND_DAI_VOICEMMODE2, + MSM_FRONTEND_DAI_MAX, +}; + +#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA19 + 1) +#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA19 + +enum { + MSM_BACKEND_DAI_PRI_I2S_RX = 0, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_BACKEND_DAI_HDMI_RX, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_BACKEND_DAI_MI2S_RX, + MSM_BACKEND_DAI_MI2S_TX, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_BACKEND_DAI_SLIMBUS_2_TX, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_BACKEND_DAI_EXTPROC_RX, + MSM_BACKEND_DAI_EXTPROC_TX, + MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_BACKEND_DAI_AUDIO_I2S_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_BACKEND_DAI_SPDIF_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_BACKEND_DAI_SENARY_MI2S_TX, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_BACKEND_DAI_PRI_TDM_RX_4, + MSM_BACKEND_DAI_PRI_TDM_TX_4, + MSM_BACKEND_DAI_PRI_TDM_RX_5, + MSM_BACKEND_DAI_PRI_TDM_TX_5, + MSM_BACKEND_DAI_PRI_TDM_RX_6, + MSM_BACKEND_DAI_PRI_TDM_TX_6, + MSM_BACKEND_DAI_PRI_TDM_RX_7, + MSM_BACKEND_DAI_PRI_TDM_TX_7, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_BACKEND_DAI_SEC_TDM_RX_4, + MSM_BACKEND_DAI_SEC_TDM_TX_4, + MSM_BACKEND_DAI_SEC_TDM_RX_5, + MSM_BACKEND_DAI_SEC_TDM_TX_5, + MSM_BACKEND_DAI_SEC_TDM_RX_6, + MSM_BACKEND_DAI_SEC_TDM_TX_6, + MSM_BACKEND_DAI_SEC_TDM_RX_7, + MSM_BACKEND_DAI_SEC_TDM_TX_7, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_BACKEND_DAI_TERT_TDM_TX_4, + MSM_BACKEND_DAI_TERT_TDM_RX_5, + MSM_BACKEND_DAI_TERT_TDM_TX_5, + MSM_BACKEND_DAI_TERT_TDM_RX_6, + MSM_BACKEND_DAI_TERT_TDM_TX_6, + MSM_BACKEND_DAI_TERT_TDM_RX_7, + MSM_BACKEND_DAI_TERT_TDM_TX_7, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_BACKEND_DAI_QUAT_TDM_RX_4, + MSM_BACKEND_DAI_QUAT_TDM_TX_4, + MSM_BACKEND_DAI_QUAT_TDM_RX_5, + MSM_BACKEND_DAI_QUAT_TDM_TX_5, + MSM_BACKEND_DAI_QUAT_TDM_RX_6, + MSM_BACKEND_DAI_QUAT_TDM_TX_6, + MSM_BACKEND_DAI_QUAT_TDM_RX_7, + MSM_BACKEND_DAI_QUAT_TDM_TX_7, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_BACKEND_DAI_USB_RX, + MSM_BACKEND_DAI_USB_TX, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT0_MI2S_TX, + MSM_BACKEND_DAI_INT1_MI2S_RX, + MSM_BACKEND_DAI_INT1_MI2S_TX, + MSM_BACKEND_DAI_INT2_MI2S_RX, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_BACKEND_DAI_INT3_MI2S_RX, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT4_MI2S_TX, + MSM_BACKEND_DAI_INT5_MI2S_RX, + MSM_BACKEND_DAI_INT5_MI2S_TX, + MSM_BACKEND_DAI_INT6_MI2S_RX, + MSM_BACKEND_DAI_INT6_MI2S_TX, + MSM_BACKEND_DAI_MAX, +}; + +enum msm_pcm_routing_event { + MSM_PCM_RT_EVT_BUF_RECFG, + MSM_PCM_RT_EVT_DEVSWITCH, + MSM_PCM_RT_EVT_MAX, +}; + +enum { + EXT_EC_REF_NONE = 0, + EXT_EC_REF_PRI_MI2S_TX, + EXT_EC_REF_SEC_MI2S_TX, + EXT_EC_REF_TERT_MI2S_TX, + EXT_EC_REF_QUAT_MI2S_TX, + EXT_EC_REF_QUIN_MI2S_TX, + EXT_EC_REF_SLIM_1_TX, +}; + +#define INVALID_SESSION -1 +#define SESSION_TYPE_RX 0 +#define SESSION_TYPE_TX 1 +#define INT_RX_VOL_MAX_STEPS 0x2000 +#define INT_RX_VOL_GAIN 0x2000 + +#define RELEASE_LOCK 0 +#define ACQUIRE_LOCK 1 + +#define MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX 2 +#define HDMI_RX_ID 0x8001 +#define ADM_PP_PARAM_MUTE_ID 0 +#define ADM_PP_PARAM_MUTE_BIT 1 +#define ADM_PP_PARAM_LATENCY_ID 1 +#define ADM_PP_PARAM_LATENCY_BIT 2 +#define BE_DAI_PORT_SESSIONS_IDX_MAX 4 + +struct msm_pcm_routing_evt { + void (*event_func)(enum msm_pcm_routing_event, void *); + void *priv_data; +}; + +struct msm_pcm_routing_bdai_data { + u16 port_id; /* AFE port ID */ + u8 active; /* track if this backend is enabled */ + unsigned long fe_sessions; /* Front-end sessions */ + /* + * Track Tx BE ports -> Rx BE ports. + * port_sessions[0] used to track BE 0 to BE 63. + * port_sessions[1] used to track BE 64 to BE 127. + * port_sessions[2] used to track BE 128 to BE 191. + * port_sessions[3] used to track BE 192 to BE 255. + */ + u64 port_sessions[BE_DAI_PORT_SESSIONS_IDX_MAX]; + + unsigned int sample_rate; + unsigned int channel; + unsigned int format; + unsigned int adm_override_ch; + u32 compr_passthr_mode; + char *name; +}; + +struct msm_pcm_routing_fdai_data { + u16 be_srate; /* track prior backend sample rate for flushing purpose */ + int strm_id; /* ASM stream ID */ + int perf_mode; + struct msm_pcm_routing_evt event_info; +}; + +#define MAX_APP_TYPES 16 +struct msm_pcm_routing_app_type_data { + int app_type; + u32 sample_rate; + int bit_width; +}; + +struct msm_pcm_stream_app_type_cfg { + int app_type; + int acdb_dev_id; + int sample_rate; +}; + +/* dai_id: front-end ID, + * dspst_id: DSP audio stream ID + * stream_type: playback or capture + */ +int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id, + int stream_type); +void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, + int stream_type); +int msm_pcm_routing_reg_phy_compr_stream(int fedai_id, int perf_mode, + int dspst_id, int stream_type, + uint32_t compr_passthr); + +int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode, + int dspst_id, int stream_type, + struct msm_pcm_routing_evt event_info); + +void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type); + +int msm_routing_check_backend_enabled(int fedai_id); + + +void msm_pcm_routing_get_bedai_info(int be_idx, + struct msm_pcm_routing_bdai_data *bedai); +void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type, + struct msm_pcm_routing_fdai_data *fe_dai); +void msm_pcm_routing_acquire_lock(void); +void msm_pcm_routing_release_lock(void); + +void msm_pcm_routing_reg_stream_app_type_cfg(int fedai_id, int app_type, + int acdb_dev_id, int sample_rate, int session_type); +int msm_pcm_routing_get_stream_app_type_cfg(int fedai_id, int session_type, + int *app_type, int *acdb_dev_id, int *sample_rate); +#endif /*_MSM_PCM_H*/ diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c new file mode 100644 index 000000000000..f4acdeb2a184 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c @@ -0,0 +1,758 @@ +/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-voice-v2.h" +#include "q6voice.h" + +static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX]; + +static struct snd_pcm_hardware msm_pcm_hardware = { + + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + + .buffer_bytes_max = 4096 * 2, + .period_bytes_min = 2048, + .period_bytes_max = 4096, + .periods_min = 2, + .periods_max = 4, + + .fifo_size = 0, +}; +static bool is_volte(struct msm_voice *pvolte) +{ + if (pvolte == &voice_info[VOLTE_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_voice2(struct msm_voice *pvoice2) +{ + if (pvoice2 == &voice_info[VOICE2_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_qchat(struct msm_voice *pqchat) +{ + if (pqchat == &voice_info[QCHAT_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_vowlan(struct msm_voice *pvowlan) +{ + if (pvowlan == &voice_info[VOWLAN_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_voicemmode1(struct msm_voice *pvoicemmode1) +{ + if (pvoicemmode1 == &voice_info[VOICEMMODE1_INDEX]) + return true; + else + return false; +} + +static bool is_voicemmode2(struct msm_voice *pvoicemmode2) +{ + if (pvoicemmode2 == &voice_info[VOICEMMODE2_INDEX]) + return true; + else + return false; +} + +static uint32_t get_session_id(struct msm_voice *pvoc) +{ + uint32_t session_id = 0; + + if (is_volte(pvoc)) + session_id = voc_get_session_id(VOLTE_SESSION_NAME); + else if (is_voice2(pvoc)) + session_id = voc_get_session_id(VOICE2_SESSION_NAME); + else if (is_qchat(pvoc)) + session_id = voc_get_session_id(QCHAT_SESSION_NAME); + else if (is_vowlan(pvoc)) + session_id = voc_get_session_id(VOWLAN_SESSION_NAME); + else if (is_voicemmode1(pvoc)) + session_id = voc_get_session_id(VOICEMMODE1_NAME); + else if (is_voicemmode2(pvoc)) + session_id = voc_get_session_id(VOICEMMODE2_NAME); + else + session_id = voc_get_session_id(VOICE_SESSION_NAME); + + return session_id; +} + + +static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (!prtd->playback_start) + prtd->playback_start = 1; + + return 0; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (!prtd->capture_start) + prtd->capture_start = 1; + + return 0; +} +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *voice; + + if (!strcmp("VoLTE", substream->pcm->id)) { + voice = &voice_info[VOLTE_SESSION_INDEX]; + pr_debug("%s: Open VoLTE Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strcmp("Voice2", substream->pcm->id)) { + voice = &voice_info[VOICE2_SESSION_INDEX]; + pr_debug("%s: Open Voice2 Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strcmp("QCHAT", substream->pcm->id)) { + voice = &voice_info[QCHAT_SESSION_INDEX]; + pr_debug("%s: Open QCHAT Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strcmp("VoWLAN", substream->pcm->id)) { + voice = &voice_info[VOWLAN_SESSION_INDEX]; + pr_debug("%s: Open VoWLAN Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strcmp("VoiceMMode1", substream->pcm->id)) { + voice = &voice_info[VOICEMMODE1_INDEX]; + pr_debug("%s: Open VoiceMMode1 Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strcmp("VoiceMMode2", substream->pcm->id)) { + voice = &voice_info[VOICEMMODE2_INDEX]; + pr_debug("%s: Open VoiceMMode2 Substream Id=%s\n", + __func__, substream->pcm->id); + } else { + voice = &voice_info[VOICE_SESSION_INDEX]; + pr_debug("%s: Open VOICE Substream Id=%s\n", + __func__, substream->pcm->id); + } + mutex_lock(&voice->lock); + + runtime->hw = msm_pcm_hardware; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + voice->playback_substream = substream; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + voice->capture_substream = substream; + + voice->instance++; + pr_debug("%s: Instance = %d, Stream ID = %s\n", + __func__, voice->instance, substream->pcm->id); + runtime->private_data = voice; + + mutex_unlock(&voice->lock); + + return 0; +} +static int msm_pcm_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (prtd->playback_start) + prtd->playback_start = 0; + + prtd->playback_substream = NULL; + + return 0; +} +static int msm_pcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (prtd->capture_start) + prtd->capture_start = 0; + prtd->capture_substream = NULL; + + return 0; +} +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = 0; + int ret = 0; + + mutex_lock(&prtd->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_close(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_close(substream); + + prtd->instance--; + if (!prtd->playback_start && !prtd->capture_start) { + pr_debug("end voice call\n"); + + session_id = get_session_id(prtd); + if (session_id) + voc_end_voice_call(session_id); + } + mutex_unlock(&prtd->lock); + + return ret; +} +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = 0; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + + if (prtd->playback_start && prtd->capture_start) { + session_id = get_session_id(prtd); + if (session_id) + voc_start_voice_call(session_id); + } + mutex_unlock(&prtd->lock); + + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + + pr_debug("%s: Voice\n", __func__); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = 0; + + pr_debug("%s: cmd = %d\n", __func__, cmd); + + session_id = get_session_id(prtd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("Start & Stop Voice call not handled in Trigger.\n"); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: resume call session_id = %d\n", __func__, + session_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + if (prtd->playback_start && prtd->capture_start) { + if (session_id) + voc_resume_voice_call(session_id); + } + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("%s: pause call session_id=%d\n", + __func__, session_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (prtd->playback_start) + prtd->playback_start = 0; + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (prtd->capture_start) + prtd->capture_start = 0; + } + if (session_id) + voc_standby_voice_call(session_id); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int msm_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = get_session_id(prtd); + enum voice_lch_mode lch_mode; + int ret = 0; + + switch (cmd) { + case SNDRV_VOICE_IOCTL_LCH: + if (copy_from_user(&lch_mode, (void *)arg, + sizeof(enum voice_lch_mode))) { + pr_err("%s: Copy from user failed, size %zd\n", + __func__, sizeof(enum voice_lch_mode)); + + ret = -EFAULT; + break; + } + + pr_debug("%s: %s lch_mode:%d\n", + __func__, substream->pcm->id, lch_mode); + + switch (lch_mode) { + case VOICE_LCH_START: + case VOICE_LCH_STOP: + ret = voc_set_lch(session_id, lch_mode); + break; + + default: + pr_err("%s: Invalid LCH MODE %d\n", __func__, lch_mode); + + ret = -EFAULT; + } + + break; + default: + pr_debug("%s: Falling into default snd_lib_ioctl cmd 0x%x\n", + __func__, cmd); + + ret = snd_pcm_lib_ioctl(substream, cmd, arg); + break; + } + + if (!ret) + pr_debug("%s: ret %d\n", __func__, ret); + else + pr_err("%s: cmd 0x%x failed %d\n", __func__, cmd, ret); + + return ret; +} + +static int msm_voice_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int volume = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((volume < 0) || (ramp_duration < 0) + || (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: volume: %d session_id: %#x ramp_duration: %d\n", __func__, + volume, session_id, ramp_duration); + + voc_set_rx_vol_step(session_id, RX_PATH, volume, ramp_duration); + +done: + return ret; +} + +static int msm_voice_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0) + || (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__, + mute, session_id, ramp_duration); + + ret = voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration); + +done: + return ret; +} + +static int msm_voice_tx_device_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0) || + (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__, + mute, session_id, ramp_duration); + + ret = voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_TX, + mute, ramp_duration); + +done: + return ret; +} + +static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0) || + (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__, + mute, session_id, ramp_duration); + + voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_RX, + mute, ramp_duration); + +done: + return ret; +} + + + +static const char * const tty_mode[] = {"OFF", "HCO", "VCO", "FULL"}; +static const struct soc_enum msm_tty_mode_enum[] = { + SOC_ENUM_SINGLE_EXT(4, tty_mode), +}; + +static int msm_voice_tty_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + voc_get_tty_mode(voc_get_session_id(VOICE_SESSION_NAME)); + return 0; +} + +static int msm_voice_tty_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int tty_mode = ucontrol->value.integer.value[0]; + + pr_debug("%s: tty_mode=%d\n", __func__, tty_mode); + + voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOWLAN_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOICEMMODE1_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOICEMMODE2_NAME), tty_mode); + + return 0; +} + +static int msm_voice_slowtalk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int st_enable = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + + pr_debug("%s: st enable=%d session_id=%#x\n", __func__, st_enable, + session_id); + + voc_set_pp_enable(session_id, + MODULE_ID_VOICE_MODULE_ST, st_enable); + + return 0; +} + +static int msm_voice_hd_voice_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + uint32_t hd_enable = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + + pr_debug("%s: HD Voice enable=%d session_id=%#x\n", __func__, hd_enable, + session_id); + + ret = voc_set_hd_enable(session_id, hd_enable); + + return ret; +} + +static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int disable = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + + if ((disable < 0) || (disable > 1)) { + pr_err(" %s Invalid arguments: %d\n", __func__, disable); + + ret = -EINVAL; + goto done; + } + pr_debug("%s: disable = %d, session_id = %d\n", __func__, disable, + session_id); + + ret = voc_disable_topology(session_id, disable); + +done: + return ret; +} + +static int msm_voice_cvd_version_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int ret = 0; + + pr_debug("%s:\n", __func__); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = CVD_VERSION_STRING_MAX_SIZE; + + return ret; +} + +static int msm_voice_cvd_version_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + char cvd_version[CVD_VERSION_STRING_MAX_SIZE] = CVD_VERSION_DEFAULT; + int ret; + + pr_debug("%s:\n", __func__); + + ret = voc_get_cvd_version(cvd_version); + + if (ret) + pr_err("%s: Error retrieving CVD version, error:%d\n", + __func__, ret); + + memcpy(ucontrol->value.bytes.data, cvd_version, sizeof(cvd_version)); + + return 0; +} +static struct snd_kcontrol_new msm_voice_controls[] = { + SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX, + 0, 3, NULL, msm_voice_rx_device_mute_put), + SOC_SINGLE_MULTI_EXT("Voice Tx Device Mute", SND_SOC_NOPM, 0, VSID_MAX, + 0, 3, NULL, msm_voice_tx_device_mute_put), + SOC_SINGLE_MULTI_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, VSID_MAX, + 0, 3, NULL, msm_voice_mute_put), + SOC_SINGLE_MULTI_EXT("Voice Rx Gain", SND_SOC_NOPM, 0, VSID_MAX, 0, 3, + NULL, msm_voice_gain_put), + SOC_ENUM_EXT("TTY Mode", msm_tty_mode_enum[0], msm_voice_tty_mode_get, + msm_voice_tty_mode_put), + SOC_SINGLE_MULTI_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2, + NULL, msm_voice_slowtalk_put), + SOC_SINGLE_MULTI_EXT("Voice Topology Disable", SND_SOC_NOPM, 0, + VSID_MAX, 0, 2, NULL, + msm_voice_topology_disable_put), + SOC_SINGLE_MULTI_EXT("HD Voice Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2, + NULL, msm_voice_hd_voice_put), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CVD Version", + .info = msm_voice_cvd_version_info, + .get = msm_voice_cvd_version_get, + }, +}; + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .ioctl = msm_pcm_ioctl, + .compat_ioctl = msm_pcm_ioctl, +}; + + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static int msm_pcm_voice_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_voice_controls, + ARRAY_SIZE(msm_voice_controls)); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_voice_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + bool destroy_cvd = false; + bool vote_bms = false; + const char *is_destroy_cvd = "qcom,destroy-cvd"; + const char *is_vote_bms = "qcom,vote-bms"; + + if (!is_voc_initialized()) { + pr_debug("%s: voice module not initialized yet, deferring probe()\n", + __func__); + + rc = -EPROBE_DEFER; + goto done; + } + + rc = voc_alloc_cal_shared_memory(); + if (rc == -EPROBE_DEFER) { + pr_debug("%s: memory allocation for calibration deferred %d\n", + __func__, rc); + + goto done; + } else if (rc < 0) { + pr_err("%s: memory allocation for calibration failed %d\n", + __func__, rc); + } + + pr_debug("%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + destroy_cvd = of_property_read_bool(pdev->dev.of_node, + is_destroy_cvd); + voc_set_destroy_cvd_flag(destroy_cvd); + + vote_bms = of_property_read_bool(pdev->dev.of_node, + is_vote_bms); + voc_set_vote_bms_flag(vote_bms); + + rc = snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); + +done: + return rc; +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_voice_dt_match[] = { + {.compatible = "qcom,msm-pcm-voice"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_voice_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-voice", + .owner = THIS_MODULE, + .of_match_table = msm_voice_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + int i = 0; + + memset(&voice_info, 0, sizeof(voice_info)); + + for (i = 0; i < VOICE_SESSION_INDEX_MAX; i++) + mutex_init(&voice_info[i].lock); + + return platform_driver_register(&msm_pcm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("Voice PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h new file mode 100644 index 000000000000..e00cebc51e7e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MSM_PCM_VOICE_H +#define _MSM_PCM_VOICE_H +#include + +enum { + VOICE_SESSION_INDEX, + VOLTE_SESSION_INDEX, + VOICE2_SESSION_INDEX, + QCHAT_SESSION_INDEX, + VOWLAN_SESSION_INDEX, + VOICEMMODE1_INDEX, + VOICEMMODE2_INDEX, + VOICE_SESSION_INDEX_MAX, +}; + +struct msm_voice { + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + int instance; + + struct mutex lock; + + uint32_t samp_rate; + uint32_t channel_mode; + + int playback_start; + int capture_start; +}; + +#endif /*_MSM_PCM_VOICE_H*/ diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c new file mode 100644 index 000000000000..65fd947d42a4 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c @@ -0,0 +1,1705 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" +#include "q6voice.h" + +#define SHARED_MEM_BUF 2 +#define VOIP_MAX_Q_LEN 10 +#define VOIP_MAX_VOC_PKT_SIZE 4096 +#define VOIP_MIN_VOC_PKT_SIZE 320 + +/* Length of the DSP frame info header added to the voc packet. */ +#define DSP_FRAME_HDR_LEN 1 + +#define MODE_IS127 0x2 +#define MODE_4GV_NB 0x3 +#define MODE_4GV_WB 0x4 +#define MODE_AMR 0x5 +#define MODE_AMR_WB 0xD +#define MODE_PCM 0xC +#define MODE_4GV_NW 0xE +#define MODE_G711 0xA +#define MODE_G711A 0xF + +enum msm_audio_g711a_frame_type { + MVS_G711A_SPEECH_GOOD, + MVS_G711A_SID, + MVS_G711A_NO_DATA, + MVS_G711A_ERASURE +}; + +enum msm_audio_g711a_mode { + MVS_G711A_MODE_MULAW, + MVS_G711A_MODE_ALAW +}; + +enum msm_audio_g711_mode { + MVS_G711_MODE_MULAW, + MVS_G711_MODE_ALAW +}; + +#define VOIP_MODE_MAX MODE_G711A +#define VOIP_RATE_MAX 23850 + +enum format { + FORMAT_S16_LE = 2, + FORMAT_SPECIAL = 31, +}; + + +enum amr_rate_type { + AMR_RATE_4750, /* AMR 4.75 kbps */ + AMR_RATE_5150, /* AMR 5.15 kbps */ + AMR_RATE_5900, /* AMR 5.90 kbps */ + AMR_RATE_6700, /* AMR 6.70 kbps */ + AMR_RATE_7400, /* AMR 7.40 kbps */ + AMR_RATE_7950, /* AMR 7.95 kbps */ + AMR_RATE_10200, /* AMR 10.20 kbps */ + AMR_RATE_12200, /* AMR 12.20 kbps */ + AMR_RATE_6600, /* AMR-WB 6.60 kbps */ + AMR_RATE_8850, /* AMR-WB 8.85 kbps */ + AMR_RATE_12650, /* AMR-WB 12.65 kbps */ + AMR_RATE_14250, /* AMR-WB 14.25 kbps */ + AMR_RATE_15850, /* AMR-WB 15.85 kbps */ + AMR_RATE_18250, /* AMR-WB 18.25 kbps */ + AMR_RATE_19850, /* AMR-WB 19.85 kbps */ + AMR_RATE_23050, /* AMR-WB 23.05 kbps */ + AMR_RATE_23850, /* AMR-WB 23.85 kbps */ + AMR_RATE_UNDEF +}; + +enum voip_state { + VOIP_STOPPED, + VOIP_STARTED, +}; + +struct voip_frame_hdr { + uint32_t timestamp; + union { + /* + * Bits 0-3: Frame type + * [optional] Bits 16-19: Frame rate + */ + uint32_t frame_type; + uint32_t packet_rate; + }; +}; +struct voip_frame { + struct voip_frame_hdr frm_hdr; + uint32_t pktlen; + uint8_t voc_pkt[VOIP_MAX_VOC_PKT_SIZE]; +}; + +struct voip_buf_node { + struct list_head list; + struct voip_frame frame; +}; + +struct voip_drv_info { + enum voip_state state; + + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + struct list_head in_queue; + struct list_head free_in_queue; + + struct list_head out_queue; + struct list_head free_out_queue; + + wait_queue_head_t out_wait; + wait_queue_head_t in_wait; + + struct mutex lock; + + spinlock_t dsp_lock; + spinlock_t dsp_ul_lock; + + bool voip_reset; + uint32_t mode; + uint32_t rate_type; + uint32_t rate; + uint32_t dtx_mode; + + uint8_t capture_start; + uint8_t playback_start; + + uint8_t playback_prepare; + uint8_t capture_prepare; + + unsigned int play_samp_rate; + unsigned int cap_samp_rate; + + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_playback_irq_pos; /* IRQ position */ + unsigned int pcm_playback_buf_pos; /* position in buffer */ + + unsigned int pcm_capture_size; + unsigned int pcm_capture_count; + unsigned int pcm_capture_irq_pos; /* IRQ position */ + unsigned int pcm_capture_buf_pos; /* position in buffer */ + + uint32_t evrc_min_rate; + uint32_t evrc_max_rate; +}; + +static int voip_get_media_type(uint32_t mode, uint32_t rate_type, + unsigned int samp_rate, + unsigned int *media_type); +static int voip_get_rate_type(uint32_t mode, + uint32_t rate, + uint32_t *rate_type); +static int voip_config_vocoder(struct snd_pcm_substream *substream); +static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static struct voip_drv_info voip_info; + +static struct snd_pcm_hardware msm_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = sizeof(struct voip_buf_node) * VOIP_MAX_Q_LEN, + .period_bytes_min = VOIP_MIN_VOC_PKT_SIZE, + .period_bytes_max = VOIP_MAX_VOC_PKT_SIZE, + .periods_min = VOIP_MAX_Q_LEN, + .periods_max = VOIP_MAX_Q_LEN, + .fifo_size = 0, +}; + + +static int msm_voip_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + int ramp_duration = ucontrol->value.integer.value[1]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d ramp_duration=%d\n", __func__, mute, + ramp_duration); + + voc_set_tx_mute(voc_get_session_id(VOIP_SESSION_NAME), TX_PATH, mute, + ramp_duration); + +done: + return ret; +} + +static int msm_voip_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int volume = ucontrol->value.integer.value[0]; + int ramp_duration = ucontrol->value.integer.value[1]; + + if ((volume < 0) || (ramp_duration < 0)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: volume: %d ramp_duration: %d\n", __func__, volume, + ramp_duration); + + voc_set_rx_vol_step(voc_get_session_id(VOIP_SESSION_NAME), + RX_PATH, + volume, + ramp_duration); + +done: + return ret; +} + +static int msm_voip_dtx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + voip_info.dtx_mode = ucontrol->value.integer.value[0]; + + pr_debug("%s: dtx: %d\n", __func__, voip_info.dtx_mode); + + mutex_unlock(&voip_info.lock); + + return 0; +} +static int msm_voip_dtx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + ucontrol->value.integer.value[0] = voip_info.dtx_mode; + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static struct snd_kcontrol_new msm_voip_controls[] = { + SOC_SINGLE_MULTI_EXT("Voip Tx Mute", SND_SOC_NOPM, 0, + MAX_RAMP_DURATION, + 0, 2, NULL, msm_voip_mute_put), + SOC_SINGLE_MULTI_EXT("Voip Rx Gain", SND_SOC_NOPM, 0, + MAX_RAMP_DURATION, + 0, 2, NULL, msm_voip_gain_put), + SOC_SINGLE_EXT("Voip Mode Config", SND_SOC_NOPM, 0, VOIP_MODE_MAX, 0, + msm_voip_mode_config_get, msm_voip_mode_config_put), + SOC_SINGLE_EXT("Voip Rate Config", SND_SOC_NOPM, 0, VOIP_RATE_MAX, 0, + NULL, msm_voip_rate_config_put), + SOC_SINGLE_MULTI_EXT("Voip Evrc Min Max Rate Config", SND_SOC_NOPM, + 0, VOC_1_RATE, 0, 2, + msm_voip_evrc_min_max_rate_config_get, + msm_voip_evrc_min_max_rate_config_put), + SOC_SINGLE_EXT("Voip Dtx Mode", SND_SOC_NOPM, 0, 1, 0, + msm_voip_dtx_mode_get, msm_voip_dtx_mode_put), +}; + +static int msm_pcm_voip_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_voip_controls, + ARRAY_SIZE(msm_voip_controls)); + + return 0; +} + +/* sample rate supported */ +static unsigned int supported_sample_rates[] = {8000, 16000, 32000, 48000}; + +static void voip_ssr_cb_fn(uint32_t opcode, void *private_data) +{ + + /* Notify ASoC to send next playback/Capture to unblock write/read */ + struct voip_drv_info *prtd = private_data; + + if (opcode == 0xFFFFFFFF) { + + prtd->voip_reset = true; + pr_debug("%s: Notify ASoC to send next playback/Capture\n", + __func__); + + prtd->pcm_playback_irq_pos += prtd->pcm_count; + if (prtd->state == VOIP_STARTED) + snd_pcm_period_elapsed(prtd->playback_substream); + wake_up(&prtd->out_wait); + + prtd->pcm_capture_irq_pos += prtd->pcm_capture_count; + if (prtd->state == VOIP_STARTED) + snd_pcm_period_elapsed(prtd->capture_substream); + wake_up(&prtd->in_wait); + + } else { + pr_err("%s: Invalid opcode during reset : %d\n", + __func__, opcode); + } +} + +/* capture path */ +static void voip_process_ul_pkt(uint8_t *voc_pkt, + uint32_t pkt_len, + uint32_t timestamp, + void *private_data) +{ + struct voip_buf_node *buf_node = NULL; + struct voip_drv_info *prtd = private_data; + unsigned long dsp_flags; + + if (prtd->capture_substream == NULL) + return; + + /* Copy up-link packet into out_queue. */ + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + + /* discarding UL packets till start is received */ + if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) { + buf_node = list_first_entry(&prtd->free_out_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + switch (prtd->mode) { + case MODE_AMR_WB: + case MODE_AMR: { + /* Remove the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + buf_node->frame.frm_hdr.timestamp = timestamp; + buf_node->frame.frm_hdr.frame_type = + ((*voc_pkt) & 0xF0) >> 4; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN; + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, &prtd->out_queue); + break; + } + case MODE_IS127: + case MODE_4GV_NB: + case MODE_4GV_WB: + case MODE_4GV_NW: { + /* Remove the DSP frame info header. + * Header format: + * Bits 0-3: frame rate + */ + buf_node->frame.frm_hdr.timestamp = timestamp; + buf_node->frame.frm_hdr.packet_rate = (*voc_pkt) & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, &prtd->out_queue); + break; + } + case MODE_G711: + case MODE_G711A:{ + /* G711 frames are 10ms each, but the DSP works with + * 20ms frames and sends two 10ms frames per buffer. + * Extract the two frames and put them in separate + * buffers. + */ + /* Remove the first DSP frame info header. + * Header format: G711A + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + * + * Header format: G711 + * Bits 2-3: Frame rate + */ + if (prtd->mode == MODE_G711A) + buf_node->frame.frm_hdr.frame_type = + (*voc_pkt) & 0x03; + buf_node->frame.frm_hdr.timestamp = timestamp; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length of the + * first frame: + */ + buf_node->frame.pktlen = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + voc_pkt = voc_pkt + buf_node->frame.pktlen; + + list_add_tail(&buf_node->list, &prtd->out_queue); + + /* Get another buffer from the free Q and fill in the + * second frame. + */ + if (!list_empty(&prtd->free_out_queue)) { + buf_node = + list_first_entry(&prtd->free_out_queue, + struct voip_buf_node, + list); + list_del(&buf_node->list); + + /* Remove the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + + if (prtd->mode == MODE_G711A) + buf_node->frame.frm_hdr.frame_type = + (*voc_pkt) & 0x03; + buf_node->frame.frm_hdr.timestamp = timestamp; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length + * of the second frame: + */ + buf_node->frame.pktlen = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, + &prtd->out_queue); + } else { + /* Drop the second frame */ + pr_err("%s: UL data dropped, read is slow\n", + __func__); + } + break; + } + default: { + buf_node->frame.frm_hdr.timestamp = timestamp; + buf_node->frame.pktlen = pkt_len; + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + list_add_tail(&buf_node->list, &prtd->out_queue); + } + } + pr_debug("%s: pkt_len =%d, frame.pktlen=%d, timestamp=%d\n", + __func__, pkt_len, buf_node->frame.pktlen, timestamp); + + if (prtd->mode == MODE_PCM) + prtd->pcm_capture_irq_pos += buf_node->frame.pktlen; + else + prtd->pcm_capture_irq_pos += prtd->pcm_capture_count; + + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + snd_pcm_period_elapsed(prtd->capture_substream); + } else { + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + pr_err("UL data dropped\n"); + } + + wake_up(&prtd->out_wait); +} + +/* playback path */ +static void voip_process_dl_pkt(uint8_t *voc_pkt, void *private_data) +{ + struct voip_buf_node *buf_node = NULL; + struct voip_drv_info *prtd = private_data; + unsigned long dsp_flags; + uint32_t rate_type; + uint32_t frame_rate; + u32 pkt_len; + u8 *voc_addr = NULL; + + if (prtd->playback_substream == NULL) + return; + + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + + if (!list_empty(&prtd->in_queue) && prtd->playback_start) { + buf_node = list_first_entry(&prtd->in_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + switch (prtd->mode) { + case MODE_AMR: + case MODE_AMR_WB: { + *((uint32_t *)voc_pkt) = buf_node->frame.pktlen + + DSP_FRAME_HDR_LEN; + /* Advance to the header of voip packet */ + voc_pkt = voc_pkt + sizeof(uint32_t); + /* + * Add the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + *voc_pkt = ((buf_node->frame.frm_hdr.frame_type & + 0x0F) << 4); + frame_rate = (buf_node->frame.frm_hdr.frame_type & + 0xFFFF0000) >> 16; + if (frame_rate) { + if (voip_get_rate_type(prtd->mode, frame_rate, + &rate_type)) { + pr_err("%s(): fail at getting rate_type\n", + __func__); + } else + prtd->rate_type = rate_type; + } + *voc_pkt |= prtd->rate_type & 0x0F; + + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + list_add_tail(&buf_node->list, &prtd->free_in_queue); + break; + } + case MODE_IS127: + case MODE_4GV_NB: + case MODE_4GV_WB: + case MODE_4GV_NW: { + *((uint32_t *)voc_pkt) = buf_node->frame.pktlen + + DSP_FRAME_HDR_LEN; + /* Advance to the header of voip packet */ + voc_pkt = voc_pkt + sizeof(uint32_t); + /* + * Add the DSP frame info header. Header format: + * Bits 0-3 : Frame rate + */ + *voc_pkt = buf_node->frame.frm_hdr.packet_rate & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, &prtd->free_in_queue); + break; + } + case MODE_G711: + case MODE_G711A:{ + /* G711 frames are 10ms each but the DSP expects 20ms + * worth of data, so send two 10ms frames per buffer. + */ + /* Add the first DSP frame info header. Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + voc_addr = voc_pkt; + voc_pkt = voc_pkt + sizeof(uint32_t); + + *voc_pkt = ((prtd->rate_type & 0x0F) << 2) | + (buf_node->frame.frm_hdr.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + pkt_len = buf_node->frame.pktlen + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + voc_pkt = voc_pkt + buf_node->frame.pktlen; + + list_add_tail(&buf_node->list, &prtd->free_in_queue); + + if (!list_empty(&prtd->in_queue)) { + /* Get the second buffer. */ + buf_node = list_first_entry(&prtd->in_queue, + struct voip_buf_node, + list); + list_del(&buf_node->list); + + /* Add the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + *voc_pkt = ((prtd->rate_type & 0x0F) << 2) | + (buf_node->frame.frm_hdr.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + pkt_len = pkt_len + buf_node->frame.pktlen + + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, + &prtd->free_in_queue); + } else { + /* Only 10ms worth of data is available, signal + * erasure frame. + */ + *voc_pkt = ((prtd->rate_type & 0x0F) << 2) | + (MVS_G711A_ERASURE & 0x03); + + pkt_len = pkt_len + DSP_FRAME_HDR_LEN; + pr_debug("%s, Only 10ms read, erase 2nd frame\n", + __func__); + } + *((uint32_t *)voc_addr) = pkt_len; + break; + } + default: { + *((uint32_t *)voc_pkt) = buf_node->frame.pktlen; + voc_pkt = voc_pkt + sizeof(uint32_t); + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + list_add_tail(&buf_node->list, &prtd->free_in_queue); + } + } + pr_debug("%s: frame.pktlen=%d\n", __func__, + buf_node->frame.pktlen); + + if (prtd->mode == MODE_PCM) + prtd->pcm_playback_irq_pos += buf_node->frame.pktlen; + else + prtd->pcm_playback_irq_pos += prtd->pcm_count; + + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(prtd->playback_substream); + } else { + *((uint32_t *)voc_pkt) = 0; + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + pr_err_ratelimited("DL data not available\n"); + } + wake_up(&prtd->in_wait); +} + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + prtd->play_samp_rate = runtime->rate; + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_playback_irq_pos = 0; + prtd->pcm_playback_buf_pos = 0; + prtd->playback_prepare = 1; + + return 0; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + int ret = 0; + + prtd->cap_samp_rate = runtime->rate; + prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_capture_irq_pos = 0; + prtd->pcm_capture_buf_pos = 0; + prtd->capture_prepare = 1; + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + pr_debug("%s: Trigger start\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_start = 1; + else + prtd->playback_start = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->playback_start = 0; + else + prtd->capture_start = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = &voip_info; + int ret = 0; + + pr_debug("%s, VoIP\n", __func__); + mutex_lock(&prtd->lock); + + runtime->hw = msm_pcm_hardware; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_debug("snd_pcm_hw_constraint_list failed\n"); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + pr_debug("snd_pcm_hw_constraint_integer failed\n"); + goto err; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->playback_substream = substream; + else + prtd->capture_substream = substream; + + runtime->private_data = prtd; +err: + mutex_unlock(&prtd->lock); + + return ret; +} + +static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + struct voip_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + + int count = frames_to_bytes(runtime, frames); + + pr_debug("%s: count = %d, frames=%d\n", __func__, count, (int)frames); + + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + ret = wait_event_interruptible_timeout(prtd->in_wait, + (!list_empty(&prtd->free_in_queue) || + prtd->state == VOIP_STOPPED), + 1 * HZ); + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + if (ret > 0) { + if (count <= VOIP_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + buf_node = + list_first_entry(&prtd->free_in_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + if (prtd->mode == MODE_PCM) { + ret = copy_from_user(&buf_node->frame.voc_pkt, + buf, count); + buf_node->frame.pktlen = count; + } else { + ret = copy_from_user(&buf_node->frame, + buf, count); + if (buf_node->frame.pktlen >= count) + buf_node->frame.pktlen = count - + (sizeof(buf_node->frame.frm_hdr) + + sizeof(buf_node->frame.pktlen)); + } + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, &prtd->in_queue); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + } else { + pr_err("%s: Write cnt %d is > VOIP_MAX_VOC_PKT_SIZE\n", + __func__, count); + ret = -ENOMEM; + } + + } else if (ret == 0) { + pr_err("%s: No free DL buffs\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: playback copy was interrupted %d\n", __func__, ret); + } + + return ret; +} +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t hwoff, void __user *buf, + snd_pcm_uframes_t frames) +{ + int ret = 0; + int count = 0; + struct voip_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + int size; + + count = frames_to_bytes(runtime, frames); + + pr_debug("%s: count = %d\n", __func__, count); + + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + ret = wait_event_interruptible_timeout(prtd->out_wait, + (!list_empty(&prtd->out_queue) || + prtd->state == VOIP_STOPPED), + 1 * HZ); + + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + if (ret > 0) { + + if (count <= VOIP_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + buf_node = list_first_entry(&prtd->out_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + if (prtd->mode == MODE_PCM) { + ret = copy_to_user(buf, + &buf_node->frame.voc_pkt, + buf_node->frame.pktlen); + } else { + size = sizeof(buf_node->frame.frm_hdr) + + sizeof(buf_node->frame.pktlen) + + buf_node->frame.pktlen; + + ret = copy_to_user(buf, + &buf_node->frame, + size); + } + if (ret) { + pr_err("%s: Copy to user returned %d\n", + __func__, ret); + ret = -EFAULT; + } + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + list_add_tail(&buf_node->list, + &prtd->free_out_queue); + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + } else { + pr_err("%s: Read count %d > VOIP_MAX_VOC_PKT_SIZE\n", + __func__, count); + ret = -ENOMEM; + } + + + } else if (ret == 0) { + pr_err_ratelimited("%s: No UL data available\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + ret = -ERESTARTSYS; + } + return ret; +} +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames); + + return ret; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct voip_buf_node *buf_node = NULL; + struct snd_dma_buffer *p_dma_buf, *c_dma_buf; + struct snd_pcm_substream *p_substream, *c_substream; + struct snd_pcm_runtime *runtime; + struct voip_drv_info *prtd; + unsigned long dsp_flags; + + if (substream == NULL) { + pr_err("substream is NULL\n"); + return -EINVAL; + } + runtime = substream->runtime; + prtd = runtime->private_data; + + wake_up(&prtd->out_wait); + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->playback_prepare = 0; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_prepare = 0; + + if (!prtd->playback_prepare && !prtd->capture_prepare) { + if (prtd->state == VOIP_STARTED) { + prtd->voip_reset = false; + prtd->state = VOIP_STOPPED; + voc_end_voice_call( + voc_get_session_id(VOIP_SESSION_NAME)); + voc_register_mvs_cb(NULL, NULL, NULL, prtd); + } + /* release all buffer */ + /* release in_queue and free_in_queue */ + pr_debug("release all buffer\n"); + p_substream = prtd->playback_substream; + if (p_substream == NULL) { + pr_debug("p_substream is NULL\n"); + goto capt; + } + p_dma_buf = &p_substream->dma_buffer; + if (p_dma_buf == NULL) { + pr_debug("p_dma_buf is NULL\n"); + goto capt; + } + if (p_dma_buf->area != NULL) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_for_each_safe(ptr, next, &prtd->in_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + list_for_each_safe(ptr, next, &prtd->free_in_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + dma_free_coherent(p_substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, p_dma_buf->area, + p_dma_buf->addr); + p_dma_buf->area = NULL; + } + /* release out_queue and free_out_queue */ +capt: c_substream = prtd->capture_substream; + if (c_substream == NULL) { + pr_debug("c_substream is NULL\n"); + goto done; + } + c_dma_buf = &c_substream->dma_buffer; + if (c_substream == NULL) { + pr_debug("c_dma_buf is NULL.\n"); + goto done; + } + if (c_dma_buf->area != NULL) { + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + list_for_each_safe(ptr, next, &prtd->out_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + list_for_each_safe(ptr, next, &prtd->free_out_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + dma_free_coherent(c_substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, c_dma_buf->area, + c_dma_buf->addr); + c_dma_buf->area = NULL; + } +done: + prtd->capture_substream = NULL; + prtd->playback_substream = NULL; + } + mutex_unlock(&prtd->lock); + + return ret; +} + +static int voip_config_vocoder(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + uint32_t media_type = 0; + uint32_t rate_type = 0; + uint32_t evrc_min_rate_type = 0; + uint32_t evrc_max_rate_type = 0; + + pr_debug("%s(): mode=%d, playback rate=%d, capture rate=%d\n", + __func__, prtd->mode, prtd->play_samp_rate, + prtd->cap_samp_rate); + + if ((runtime->format != FORMAT_S16_LE && + runtime->format != FORMAT_SPECIAL) && + ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) || + (prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) || + (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW) || + (prtd->mode == MODE_G711) || (prtd->mode == MODE_G711A))) { + pr_err("%s(): mode:%d and format:%u are not matched\n", + __func__, prtd->mode, (uint32_t)runtime->format); + + ret = -EINVAL; + goto done; + } + + if (runtime->format != FORMAT_S16_LE && (prtd->mode == MODE_PCM)) { + pr_err("%s(): mode:%d and format:%u are not matched\n", + __func__, prtd->mode, runtime->format); + + ret = -EINVAL; + goto done; + } + + if ((prtd->mode == MODE_PCM) || + (prtd->mode == MODE_AMR) || + (prtd->mode == MODE_AMR_WB) || + (prtd->mode == MODE_G711) || + (prtd->mode == MODE_G711A)) { + ret = voip_get_rate_type(prtd->mode, + prtd->rate, + &rate_type); + if (ret < 0) { + pr_err("%s(): fail at getting rate_type, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + prtd->rate_type = rate_type; + pr_debug("rate_type=%d\n", rate_type); + + } else if ((prtd->mode == MODE_IS127) || + (prtd->mode == MODE_4GV_NB) || + (prtd->mode == MODE_4GV_WB) || + (prtd->mode == MODE_4GV_NW)) { + ret = voip_get_rate_type(prtd->mode, + prtd->evrc_min_rate, + &evrc_min_rate_type); + if (ret < 0) { + pr_err("%s(): fail at getting min rate, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + if (evrc_min_rate_type == VOC_0_RATE) + evrc_min_rate_type = VOC_8_RATE; + + ret = voip_get_rate_type(prtd->mode, + prtd->evrc_max_rate, + &evrc_max_rate_type); + if (ret < 0) { + pr_err("%s(): fail at getting max rate, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + if (evrc_max_rate_type == VOC_0_RATE) + evrc_max_rate_type = VOC_1_RATE; + + if (evrc_max_rate_type < evrc_min_rate_type) { + pr_err("%s(): Invalid EVRC min max rates: %d, %d\n", + __func__, evrc_min_rate_type, + evrc_max_rate_type); + + ret = -EINVAL; + goto done; + } + pr_debug("%s(): min rate=%d, max rate=%d\n", + __func__, evrc_min_rate_type, evrc_max_rate_type); + } + ret = voip_get_media_type(prtd->mode, + prtd->rate_type, + prtd->play_samp_rate, + &media_type); + if (ret < 0) { + pr_err("%s(): fail at getting media_type, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + pr_debug("%s(): media_type=%d\n", __func__, media_type); + + if ((prtd->play_samp_rate == 8000 && prtd->cap_samp_rate == 8000) || + (prtd->play_samp_rate == 16000 && prtd->cap_samp_rate == 16000) || + (prtd->play_samp_rate == 32000 && prtd->cap_samp_rate == 32000) || + (prtd->play_samp_rate == 48000 && prtd->cap_samp_rate == 48000)) { + voc_config_vocoder(media_type, rate_type, + VSS_NETWORK_ID_VOIP, + voip_info.dtx_mode, + evrc_min_rate_type, + evrc_max_rate_type); + } else { + pr_debug("%s: Invalid rate playback %d, capture %d\n", + __func__, prtd->play_samp_rate, + prtd->cap_samp_rate); + + ret = -EINVAL; + } +done: + + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + + if (prtd->playback_prepare && prtd->capture_prepare + && (prtd->state != VOIP_STARTED)) { + ret = voip_config_vocoder(substream); + if (ret < 0) { + pr_err("%s(): fail at configuring vocoder for voip, ret=%d\n", + __func__, ret); + + goto done; + } + + /* Initialaizing cb variables */ + voc_register_mvs_cb(voip_process_ul_pkt, + voip_process_dl_pkt, + voip_ssr_cb_fn, prtd); + + ret = voc_start_voice_call( + voc_get_session_id(VOIP_SESSION_NAME)); + + if (ret < 0) { + pr_err("%s: voc_start_voice_call() failed err %d", + __func__, ret); + + goto done; + } + prtd->state = VOIP_STARTED; + } +done: + mutex_unlock(&prtd->lock); + + return ret; +} + +static snd_pcm_uframes_t +msm_pcm_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + if (prtd->pcm_playback_irq_pos >= prtd->pcm_size) + prtd->pcm_playback_irq_pos = 0; + return bytes_to_frames(runtime, (prtd->pcm_playback_irq_pos)); +} + +static snd_pcm_uframes_t +msm_pcm_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size) + prtd->pcm_capture_irq_pos = 0; + return bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos)); +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t ret = 0; + + pr_debug("%s\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_pointer(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_pointer(substream); + return ret; +} + +static int msm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + pr_debug("%s\n", __func__); + dma_mmap_coherent(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + return 0; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct voip_buf_node *buf_node = NULL; + int i = 0, offset = 0; + + pr_debug("%s: voip\n", __func__); + + mutex_lock(&voip_info.lock); + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + + dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + &dma_buf->addr, GFP_KERNEL); + if (!dma_buf->area) { + pr_err("%s:MSM VOIP dma_alloc failed\n", __func__); + mutex_unlock(&voip_info.lock); + return -ENOMEM; + } + + dma_buf->bytes = runtime->hw.buffer_bytes_max; + memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < VOIP_MAX_Q_LEN; i++) { + buf_node = (void *)dma_buf->area + offset; + + list_add_tail(&buf_node->list, + &voip_info.free_in_queue); + offset = offset + sizeof(struct voip_buf_node); + } + } else { + for (i = 0; i < VOIP_MAX_Q_LEN; i++) { + buf_node = (void *) dma_buf->area + offset; + list_add_tail(&buf_node->list, + &voip_info.free_out_queue); + offset = offset + sizeof(struct voip_buf_node); + } + } + + mutex_unlock(&voip_info.lock); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + ucontrol->value.integer.value[0] = voip_info.mode; + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + voip_info.mode = ucontrol->value.integer.value[0]; + + pr_debug("%s: mode=%d\n", __func__, voip_info.mode); + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int rate = ucontrol->value.integer.value[0]; + + mutex_lock(&voip_info.lock); + + if (voip_info.rate != rate) { + voip_info.rate = rate; + pr_debug("%s: rate=%d\n", __func__, voip_info.rate); + + if (voip_info.state == VOIP_STARTED && + (voip_info.mode == MODE_AMR || + voip_info.mode == MODE_AMR_WB)) { + ret = voip_config_vocoder( + voip_info.capture_substream); + if (ret) { + pr_err("%s:Failed to configure vocoder, ret=%d\n", + __func__, ret); + + goto done; + } + + ret = voc_update_amr_vocoder_rate( + voc_get_session_id(VOIP_SESSION_NAME)); + if (ret) { + pr_err("%s:Failed to update AMR rate, ret=%d\n", + __func__, ret); + } + } + } + +done: + mutex_unlock(&voip_info.lock); + + return ret; +} + +static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + ucontrol->value.integer.value[0] = voip_info.evrc_min_rate; + ucontrol->value.integer.value[1] = voip_info.evrc_max_rate; + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + voip_info.evrc_min_rate = ucontrol->value.integer.value[0]; + voip_info.evrc_max_rate = ucontrol->value.integer.value[1]; + + pr_debug("%s(): evrc_min_rate=%d,evrc_max_rate=%d\n", __func__, + voip_info.evrc_min_rate, voip_info.evrc_max_rate); + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int voip_get_rate_type(uint32_t mode, uint32_t rate, + uint32_t *rate_type) +{ + int ret = 0; + + switch (mode) { + case MODE_AMR: { + switch (rate) { + case 4750: + *rate_type = AMR_RATE_4750; + break; + case 5150: + *rate_type = AMR_RATE_5150; + break; + case 5900: + *rate_type = AMR_RATE_5900; + break; + case 6700: + *rate_type = AMR_RATE_6700; + break; + case 7400: + *rate_type = AMR_RATE_7400; + break; + case 7950: + *rate_type = AMR_RATE_7950; + break; + case 10200: + *rate_type = AMR_RATE_10200; + break; + case 12200: + *rate_type = AMR_RATE_12200; + break; + default: + pr_err("wrong rate for AMR NB.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_AMR_WB: { + switch (rate) { + case 6600: + *rate_type = AMR_RATE_6600 - AMR_RATE_6600; + break; + case 8850: + *rate_type = AMR_RATE_8850 - AMR_RATE_6600; + break; + case 12650: + *rate_type = AMR_RATE_12650 - AMR_RATE_6600; + break; + case 14250: + *rate_type = AMR_RATE_14250 - AMR_RATE_6600; + break; + case 15850: + *rate_type = AMR_RATE_15850 - AMR_RATE_6600; + break; + case 18250: + *rate_type = AMR_RATE_18250 - AMR_RATE_6600; + break; + case 19850: + *rate_type = AMR_RATE_19850 - AMR_RATE_6600; + break; + case 23050: + *rate_type = AMR_RATE_23050 - AMR_RATE_6600; + break; + case 23850: + *rate_type = AMR_RATE_23850 - AMR_RATE_6600; + break; + default: + pr_err("wrong rate for AMR_WB.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_PCM: { + *rate_type = 0; + break; + } + case MODE_IS127: + case MODE_4GV_NB: + case MODE_4GV_WB: { + switch (rate) { + case VOC_0_RATE: + case VOC_8_RATE: + case VOC_4_RATE: + case VOC_2_RATE: + case VOC_1_RATE: + *rate_type = rate; + break; + default: + pr_err("wrong rate for IS127/4GV_NB/WB.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_4GV_NW: { + switch (rate) { + case VOC_0_RATE: + case VOC_8_RATE: + case VOC_4_RATE: + case VOC_2_RATE: + case VOC_1_RATE: + case VOC_8_RATE_NC: + *rate_type = rate; + break; + default: + pr_err("wrong rate for 4GV_NW.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_G711: + case MODE_G711A: + *rate_type = rate; + break; + default: + pr_err("wrong mode type.\n"); + ret = -EINVAL; + } + pr_debug("%s, mode=%d, rate=%u, rate_type=%d\n", + __func__, mode, rate, *rate_type); + return ret; +} + +static int voip_get_media_type(uint32_t mode, uint32_t rate_type, + unsigned int samp_rate, + unsigned int *media_type) +{ + int ret = 0; + + pr_debug("%s: mode=%d, samp_rate=%d\n", __func__, + mode, samp_rate); + switch (mode) { + case MODE_AMR: + *media_type = VSS_MEDIA_ID_AMR_NB_MODEM; + break; + case MODE_AMR_WB: + *media_type = VSS_MEDIA_ID_AMR_WB_MODEM; + break; + case MODE_PCM: + if (samp_rate == 8000) + *media_type = VSS_MEDIA_ID_PCM_8_KHZ; + else if (samp_rate == 16000) + *media_type = VSS_MEDIA_ID_PCM_16_KHZ; + else if (samp_rate == 32000) + *media_type = VSS_MEDIA_ID_PCM_32_KHZ; + else + *media_type = VSS_MEDIA_ID_PCM_48_KHZ; + break; + case MODE_IS127: /* EVRC-A */ + *media_type = VSS_MEDIA_ID_EVRC_MODEM; + break; + case MODE_4GV_NB: /* EVRC-B */ + *media_type = VSS_MEDIA_ID_4GV_NB_MODEM; + break; + case MODE_4GV_WB: /* EVRC-WB */ + *media_type = VSS_MEDIA_ID_4GV_WB_MODEM; + break; + case MODE_4GV_NW: /* EVRC-NW */ + *media_type = VSS_MEDIA_ID_4GV_NW_MODEM; + break; + case MODE_G711: + case MODE_G711A: + if (rate_type == MVS_G711A_MODE_MULAW) + *media_type = VSS_MEDIA_ID_G711_MULAW; + else + *media_type = VSS_MEDIA_ID_G711_ALAW; + break; + default: + pr_debug(" input mode is not supported\n"); + ret = -EINVAL; + } + + pr_debug("%s: media_type is 0x%x\n", __func__, *media_type); + + return ret; +} + + +static struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .copy = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .mmap = msm_pcm_mmap, +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + pr_debug("msm_asoc_pcm_new\n"); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_voip_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + + if (!is_voc_initialized()) { + pr_debug("%s: voice module not initialized yet, deferring probe()\n", + __func__); + + rc = -EPROBE_DEFER; + goto done; + } + + rc = voc_alloc_cal_shared_memory(); + if (rc == -EPROBE_DEFER) { + pr_debug("%s: memory allocation for calibration deferred %d\n", + __func__, rc); + + goto done; + } else if (rc < 0) { + pr_err("%s: memory allocation for calibration failed %d\n", + __func__, rc); + } + + rc = voc_alloc_voip_shared_memory(); + if (rc < 0) { + pr_err("%s: error allocating shared mem err %d\n", + __func__, rc); + } + + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + rc = snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); + +done: + return rc; +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_voip_dt_match[] = { + {.compatible = "qcom,msm-voip-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_voip_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-voip-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_voip_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + memset(&voip_info, 0, sizeof(voip_info)); + voip_info.mode = MODE_PCM; + mutex_init(&voip_info.lock); + + spin_lock_init(&voip_info.dsp_lock); + spin_lock_init(&voip_info.dsp_ul_lock); + + init_waitqueue_head(&voip_info.out_wait); + init_waitqueue_head(&voip_info.in_wait); + + INIT_LIST_HEAD(&voip_info.in_queue); + INIT_LIST_HEAD(&voip_info.free_in_queue); + INIT_LIST_HEAD(&voip_info.out_queue); + INIT_LIST_HEAD(&voip_info.free_out_queue); + + return platform_driver_register(&msm_pcm_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c new file mode 100644 index 000000000000..9276231a642c --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c @@ -0,0 +1,1035 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-qti-pp-config.h" +#include "msm-pcm-routing-v2.h" + +/* EQUALIZER */ +/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */ +#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE + +enum { + EQ_BAND1 = 0, + EQ_BAND2, + EQ_BAND3, + EQ_BAND4, + EQ_BAND5, + EQ_BAND6, + EQ_BAND7, + EQ_BAND8, + EQ_BAND9, + EQ_BAND10, + EQ_BAND11, + EQ_BAND12, + EQ_BAND_MAX, +}; + +struct msm_audio_eq_band { + uint16_t band_idx; /* The band index, 0 .. 11 */ + uint32_t filter_type; /* Filter band type */ + uint32_t center_freq_hz; /* Filter band center frequency */ + uint32_t filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + uint32_t q_factor; +} __packed; + +struct msm_audio_eq_stream_config { + uint32_t enable; /* Number of consequtive bands specified */ + uint32_t num_bands; + struct msm_audio_eq_band eq_bands[EQ_BAND_MAX]; +} __packed; + +/* Audio Sphere data structures */ +struct msm_audio_pp_asphere_state_s { + uint32_t enabled; + uint32_t strength; + uint32_t mode; + uint32_t version; + int port_id[AFE_MAX_PORTS]; + int copp_idx[AFE_MAX_PORTS]; + bool initialized; + uint32_t enabled_prev; + uint32_t strength_prev; +}; + +static struct msm_audio_pp_asphere_state_s asphere_state; + +struct msm_audio_eq_stream_config eq_data[MAX_EQ_SESSIONS]; + +static int msm_route_hfp_vol_control; +static const DECLARE_TLV_DB_LINEAR(hfp_rx_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static int msm_route_pri_auxpcm_lb_vol_ctrl; +static const DECLARE_TLV_DB_LINEAR(pri_auxpcm_lb_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static int msm_route_sec_auxpcm_lb_vol_ctrl; +static const DECLARE_TLV_DB_LINEAR(sec_auxpcm_lb_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static void msm_qti_pp_send_eq_values_(int eq_idx) +{ + int result; + struct msm_pcm_routing_fdai_data fe_dai; + struct audio_client *ac = NULL; + + msm_pcm_routing_get_fedai_info(eq_idx, SESSION_TYPE_RX, &fe_dai); + ac = q6asm_get_audio_client(fe_dai.strm_id); + + if (ac == NULL) { + pr_err("%s: Could not get audio client for session: %d\n", + __func__, fe_dai.strm_id); + goto done; + } + + result = q6asm_equalizer(ac, &eq_data[eq_idx]); + + if (result < 0) + pr_err("%s: Call to ASM equalizer failed, returned = %d\n", + __func__, result); +done: + return; +} + +static int msm_qti_pp_get_eq_enable_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + + ucontrol->value.integer.value[0] = eq_data[eq_idx].enable; + + pr_debug("%s: EQ #%d enable %d\n", __func__, + eq_idx, eq_data[eq_idx].enable); + return 0; +} + +static int msm_qti_pp_put_eq_enable_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int value = ucontrol->value.integer.value[0]; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + pr_debug("%s: EQ #%d enable %d\n", __func__, + eq_idx, value); + eq_data[eq_idx].enable = value; + msm_pcm_routing_acquire_lock(); + msm_qti_pp_send_eq_values_(eq_idx); + msm_pcm_routing_release_lock(); + return 0; +} + +static int msm_qti_pp_get_eq_band_count_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands; + + pr_debug("%s: EQ #%d bands %d\n", __func__, + eq_idx, eq_data[eq_idx].num_bands); + return eq_data[eq_idx].num_bands; +} + +static int msm_qti_pp_put_eq_band_count_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int value = ucontrol->value.integer.value[0]; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + + pr_debug("%s: EQ #%d bands %d\n", __func__, + eq_idx, value); + eq_data[eq_idx].num_bands = value; + return 0; +} + +static int msm_qti_pp_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) || + (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX)) + return -EINVAL; + + ucontrol->value.integer.value[0] = + eq_data[eq_idx].eq_bands[band_idx].band_idx; + ucontrol->value.integer.value[1] = + eq_data[eq_idx].eq_bands[band_idx].filter_type; + ucontrol->value.integer.value[2] = + eq_data[eq_idx].eq_bands[band_idx].center_freq_hz; + ucontrol->value.integer.value[3] = + eq_data[eq_idx].eq_bands[band_idx].filter_gain; + ucontrol->value.integer.value[4] = + eq_data[eq_idx].eq_bands[band_idx].q_factor; + + pr_debug("%s: band_idx = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].band_idx); + pr_debug("%s: filter_type = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].filter_type); + pr_debug("%s: center_freq_hz = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].center_freq_hz); + pr_debug("%s: filter_gain = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].filter_gain); + pr_debug("%s: q_factor = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].q_factor); + return 0; +} + +static int msm_qti_pp_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) || + (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX)) + return -EINVAL; + + eq_data[eq_idx].eq_bands[band_idx].band_idx = + ucontrol->value.integer.value[0]; + eq_data[eq_idx].eq_bands[band_idx].filter_type = + ucontrol->value.integer.value[1]; + eq_data[eq_idx].eq_bands[band_idx].center_freq_hz = + ucontrol->value.integer.value[2]; + eq_data[eq_idx].eq_bands[band_idx].filter_gain = + ucontrol->value.integer.value[3]; + eq_data[eq_idx].eq_bands[band_idx].q_factor = + ucontrol->value.integer.value[4]; + return 0; +} + +#ifdef CONFIG_QTI_PP +void msm_qti_pp_send_eq_values(int fedai_id) +{ + if (eq_data[fedai_id].enable) + msm_qti_pp_send_eq_values_(fedai_id); +} + +/* CUSTOM MIXING */ +int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx, + unsigned int session_id, + uint16_t op_FL_ip_FL_weight, + uint16_t op_FL_ip_FR_weight, + uint16_t op_FR_ip_FL_weight, + uint16_t op_FR_ip_FR_weight) +{ + char *params_value; + int *update_params_value32, rc = 0; + int16_t *update_params_value16 = 0; + uint32_t params_length = CUSTOM_STEREO_PAYLOAD_SIZE * sizeof(uint32_t); + uint32_t avail_length = params_length; + + pr_debug("%s: port_id - %d, session id - %d\n", __func__, port_id, + session_id); + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + update_params_value32 = (int *)params_value; + if (avail_length < 2 * sizeof(uint32_t)) + goto skip_send_cmd; + *update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER; + *update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF; + avail_length = avail_length - (2 * sizeof(uint32_t)); + + update_params_value16 = (int16_t *)update_params_value32; + if (avail_length < 10 * sizeof(uint16_t)) + goto skip_send_cmd; + *update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE; + /*for alignment only*/ + *update_params_value16++ = 0; + /*index is 32-bit param in little endian*/ + *update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM; + *update_params_value16++ = 0; + /*for stereo mixing num out ch*/ + *update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH; + /*for stereo mixing num in ch*/ + *update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH; + + /* Out ch map FL/FR*/ + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + + /* In ch map FL/FR*/ + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + avail_length = avail_length - (10 * sizeof(uint16_t)); + /* weighting coefficients as name suggests, + * mixing will be done according to these coefficients + */ + if (avail_length < 4 * sizeof(uint16_t)) + goto skip_send_cmd; + *update_params_value16++ = op_FL_ip_FL_weight; + *update_params_value16++ = op_FL_ip_FR_weight; + *update_params_value16++ = op_FR_ip_FL_weight; + *update_params_value16++ = op_FR_ip_FR_weight; + avail_length = avail_length - (4 * sizeof(uint16_t)); + if (params_length) { + rc = adm_set_stereo_to_custom_stereo(port_id, + copp_idx, + session_id, + params_value, + params_length); + if (rc) { + pr_err("%s: send params failed rc=%d\n", __func__, rc); + kfree(params_value); + return -EINVAL; + } + } + kfree(params_value); + return 0; +skip_send_cmd: + pr_err("%s: insufficient memory, send cmd failed\n", + __func__); + kfree(params_value); + return -ENOMEM; +} +#endif /* CONFIG_QTI_PP */ + +/* RMS */ +static int msm_qti_pp_get_rms_value_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int be_idx = 0, copp_idx; + char *param_value; + int *update_param_value; + uint32_t param_length = sizeof(uint32_t); + uint32_t param_payload_len = RMS_PAYLOAD_LEN * sizeof(uint32_t); + struct msm_pcm_routing_bdai_data msm_bedai; + + param_value = kzalloc(param_length, GFP_KERNEL); + if (!param_value) + return -ENOMEM; + + msm_pcm_routing_acquire_lock(); + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + msm_pcm_routing_get_bedai_info(be_idx, &msm_bedai); + if (msm_bedai.port_id == SLIMBUS_0_TX) + break; + } + if ((be_idx >= MSM_BACKEND_DAI_MAX) || !msm_bedai.active) { + pr_err("%s, back not active to query rms be_idx:%d\n", + __func__, be_idx); + rc = -EINVAL; + goto get_rms_value_err; + } + copp_idx = adm_get_default_copp_idx(SLIMBUS_0_TX); + if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) { + pr_err("%s, no active copp to query rms copp_idx:%d\n", + __func__, copp_idx); + rc = -EINVAL; + goto get_rms_value_err; + } + rc = adm_get_params(SLIMBUS_0_TX, copp_idx, + RMS_MODULEID_APPI_PASSTHRU, + RMS_PARAM_FIRST_SAMPLE, + param_length + param_payload_len, + param_value); + if (rc) { + pr_err("%s: get parameters failed rc=%d\n", __func__, rc); + rc = -EINVAL; + goto get_rms_value_err; + } + update_param_value = (int *)param_value; + ucontrol->value.integer.value[0] = update_param_value[0]; + + pr_debug("%s: FROM DSP value[0] 0x%x\n", + __func__, update_param_value[0]); +get_rms_value_err: + msm_pcm_routing_release_lock(); + kfree(param_value); + return rc; +} + +static int msm_qti_pp_put_rms_value_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* not used */ + return 0; +} + +/* VOLUME */ +static int msm_route_fm_vol_control; +static int msm_afe_lb_vol_ctrl; +static int msm_afe_sec_mi2s_lb_vol_ctrl; +static int msm_afe_tert_mi2s_lb_vol_ctrl; +static int msm_afe_quat_mi2s_lb_vol_ctrl; +static int msm_afe_slimbus_8_lb_vol_ctrl; +static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0, INT_RX_VOL_MAX_STEPS); +static const DECLARE_TLV_DB_LINEAR(afe_lb_vol_gain, 0, INT_RX_VOL_MAX_STEPS); + +static int msm_qti_pp_get_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_fm_vol_control; + return 0; +} + +static int msm_qti_pp_set_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(INT_FM_TX, ucontrol->value.integer.value[0]); + + msm_route_fm_vol_control = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_PRIMARY_MI2S_TX, + ucontrol->value.integer.value[0]); + + msm_afe_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_sec_mi2s_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_SECONDARY_MI2S_TX, + ucontrol->value.integer.value[0]); + msm_afe_sec_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_tert_mi2s_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_TERTIARY_MI2S_TX, + ucontrol->value.integer.value[0]); + msm_afe_tert_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0]; + return 0; +} + +static int msm_qti_pp_get_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_slimbus_8_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + + ret = afe_loopback_gain(SLIMBUS_8_TX, + ucontrol->value.integer.value[0]); + + if (ret) + pr_err("%s: failed to set LB vol for SLIMBUS_8_TX", __func__); + else + msm_afe_slimbus_8_lb_vol_ctrl = + ucontrol->value.integer.value[0]; + + return ret; +} + +static int msm_qti_pp_get_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_quat_mi2s_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_QUATERNARY_MI2S_TX, + ucontrol->value.integer.value[0]); + + msm_afe_quat_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_hfp_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_hfp_vol_control; + return 0; +} + +static int msm_qti_pp_set_hfp_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(INT_BT_SCO_TX, ucontrol->value.integer.value[0]); + + msm_route_hfp_vol_control = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_pri_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_pri_auxpcm_lb_vol_ctrl; + pr_debug("%s: Volume = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_qti_pp_set_pri_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]); + + msm_route_pri_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_sec_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_sec_auxpcm_lb_vol_ctrl; + pr_debug("%s: Volume = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_qti_pp_set_sec_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]); + + msm_route_sec_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_channel_map_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL]; + int i; + + adm_get_multi_ch_map(channel_map, ADM_PATH_PLAYBACK); + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = + (unsigned int) channel_map[i]; + return 0; +} + +static int msm_qti_pp_put_channel_map_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL]; + int i; + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + channel_map[i] = (char)(ucontrol->value.integer.value[i]); + adm_set_multi_ch_map(channel_map, ADM_PATH_PLAYBACK); + + return 0; +} + +/* Audio Sphere functions */ + +static void msm_qti_pp_asphere_init_state(void) +{ + int i; + + if (asphere_state.initialized) + return; + asphere_state.initialized = true; + for (i = 0; i < AFE_MAX_PORTS; i++) { + asphere_state.port_id[i] = -1; + asphere_state.copp_idx[i] = -1; + } + asphere_state.enabled = 0; + asphere_state.strength = 0; + asphere_state.mode = 0; + asphere_state.version = 0; + asphere_state.enabled_prev = 0; + asphere_state.strength_prev = 0; +} + +static int msm_qti_pp_asphere_send_params(int port_id, int copp_idx, bool force) +{ + char *params_value = NULL; + uint32_t *update_params_value = NULL; + uint32_t param_size = sizeof(uint32_t) + + sizeof(struct adm_param_data_v5); + int params_length = 0, param_count = 0, ret = 0; + bool set_enable = force || + (asphere_state.enabled != asphere_state.enabled_prev); + bool set_strength = asphere_state.enabled == 1 && (set_enable || + (asphere_state.strength != asphere_state.strength_prev)); + + if (set_enable) + param_count++; + if (set_strength) + param_count++; + params_length = param_count * param_size; + + pr_debug("%s: port_id %d, copp_id %d, forced %d, param_count %d\n", + __func__, port_id, copp_idx, force, param_count); + pr_debug("%s: enable prev:%u cur:%u, strength prev:%u cur:%u\n", + __func__, asphere_state.enabled_prev, asphere_state.enabled, + asphere_state.strength_prev, asphere_state.strength); + + if (params_length > 0) + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + update_params_value = (uint32_t *)params_value; + params_length = 0; + if (set_strength) { + /* add strength command */ + *update_params_value++ = AUDPROC_MODULE_ID_AUDIOSPHERE; + *update_params_value++ = AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH; + *update_params_value++ = sizeof(uint32_t); + *update_params_value++ = asphere_state.strength; + params_length += param_size; + } + if (set_enable) { + /* add enable command */ + *update_params_value++ = AUDPROC_MODULE_ID_AUDIOSPHERE; + *update_params_value++ = AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE; + *update_params_value++ = sizeof(uint32_t); + *update_params_value++ = asphere_state.enabled; + params_length += param_size; + } + pr_debug("%s, param length: %d\n", __func__, params_length); + if (params_length) { + ret = adm_send_params_v5(port_id, copp_idx, + params_value, params_length); + if (ret) { + pr_err("%s: setting param failed with err=%d\n", + __func__, ret); + kfree(params_value); + return -EINVAL; + } + } + kfree(params_value); + return 0; +} + +#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE) +int msm_qti_pp_asphere_init(int port_id, int copp_idx) +{ + int index = adm_validate_and_get_port_index(port_id); + + pr_debug("%s, port_id %d, copp_id %d\n", __func__, port_id, copp_idx); + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return -EINVAL; + } + msm_qti_pp_asphere_init_state(); + + asphere_state.port_id[index] = port_id; + asphere_state.copp_idx[index] = copp_idx; + + if (asphere_state.enabled) + msm_qti_pp_asphere_send_params(port_id, copp_idx, true); + + return 0; +} + +void msm_qti_pp_asphere_deinit(int port_id) +{ + int index = adm_validate_and_get_port_index(port_id); + + pr_debug("%s, port_id %d\n", __func__, port_id); + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return; + } + + if (asphere_state.port_id[index] == port_id) { + asphere_state.port_id[index] = -1; + asphere_state.copp_idx[index] = -1; + } +} +#endif + +static int msm_qti_pp_asphere_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (!asphere_state.initialized) + return -EAGAIN; + ucontrol->value.integer.value[0] = asphere_state.enabled; + ucontrol->value.integer.value[1] = asphere_state.strength; + pr_debug("%s, enable %u, strength %u\n", __func__, + asphere_state.enabled, asphere_state.strength); + return 0; +} + +static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t enable = ucontrol->value.integer.value[0]; + int32_t strength = ucontrol->value.integer.value[1]; + int i; + + pr_debug("%s, enable %u, strength %u\n", __func__, enable, strength); + + msm_qti_pp_asphere_init_state(); + + if (enable == 0 || enable == 1) { + asphere_state.enabled_prev = asphere_state.enabled; + asphere_state.enabled = enable; + } + + if (strength >= 0 && strength <= 1000) { + asphere_state.strength_prev = asphere_state.strength; + asphere_state.strength = strength; + } + + if (asphere_state.strength != asphere_state.strength_prev || + asphere_state.enabled != asphere_state.enabled_prev) { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (asphere_state.port_id[i] >= 0) + msm_qti_pp_asphere_send_params( + asphere_state.port_id[i], + asphere_state.copp_idx[i], + false); + } + } + return 0; +} + +static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_fm_vol_mixer, + msm_qti_pp_set_fm_vol_mixer, fm_rx_vol_gain), + SOC_SINGLE_EXT_TLV("Quat MI2S FM RX Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_quat_mi2s_fm_vol_mixer, + msm_qti_pp_set_quat_mi2s_fm_vol_mixer, fm_rx_vol_gain), +}; + +static const struct snd_kcontrol_new pri_mi2s_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("PRI MI2S LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_pri_mi2s_lb_vol_mixer, + msm_qti_pp_set_pri_mi2s_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new sec_mi2s_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SEC MI2S LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_sec_mi2s_lb_vol_mixer, + msm_qti_pp_set_sec_mi2s_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new tert_mi2s_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Tert MI2S LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_tert_mi2s_lb_vol_mixer, + msm_qti_pp_set_tert_mi2s_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new slimbus_8_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SLIMBUS_8 LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_slimbus_8_lb_vol_mixer, + msm_qti_pp_set_slimbus_8_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new int_hfp_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Internal HFP RX Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_hfp_vol_mixer, + msm_qti_pp_set_hfp_vol_mixer, hfp_rx_vol_gain), +}; + +static const struct snd_kcontrol_new pri_auxpcm_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("PRI AUXPCM LOOPBACK Volume", + AFE_PORT_ID_PRIMARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0, + msm_qti_pp_get_pri_auxpcm_lb_vol_mixer, + msm_qti_pp_set_pri_auxpcm_lb_vol_mixer, + pri_auxpcm_lb_vol_gain), +}; + +static const struct snd_kcontrol_new sec_auxpcm_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SEC AUXPCM LOOPBACK Volume", + AFE_PORT_ID_SECONDARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0, + msm_qti_pp_get_sec_auxpcm_lb_vol_mixer, + msm_qti_pp_set_sec_auxpcm_lb_vol_mixer, + sec_auxpcm_lb_vol_gain), +}; + +static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 16, + 0, 8, msm_qti_pp_get_channel_map_mixer, + msm_qti_pp_put_channel_map_mixer), +}; + + +static const struct snd_kcontrol_new get_rms_controls[] = { + SOC_SINGLE_EXT("Get RMS", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, msm_qti_pp_get_rms_value_control, msm_qti_pp_put_rms_value_control), +}; + +static const struct snd_kcontrol_new eq_enable_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_qti_pp_get_eq_enable_mixer, + msm_qti_pp_put_eq_enable_mixer), + SOC_SINGLE_EXT("MultiMedia2 EQ Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_qti_pp_get_eq_enable_mixer, + msm_qti_pp_put_eq_enable_mixer), + SOC_SINGLE_EXT("MultiMedia3 EQ Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_qti_pp_get_eq_enable_mixer, + msm_qti_pp_put_eq_enable_mixer), +}; + +static const struct snd_kcontrol_new eq_band_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 EQ Band Count", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 11, 0, + msm_qti_pp_get_eq_band_count_audio_mixer, + msm_qti_pp_put_eq_band_count_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2 EQ Band Count", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 11, 0, + msm_qti_pp_get_eq_band_count_audio_mixer, + msm_qti_pp_put_eq_band_count_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3 EQ Band Count", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 11, 0, + msm_qti_pp_get_eq_band_count_audio_mixer, + msm_qti_pp_put_eq_band_count_audio_mixer), +}; + +static const struct snd_kcontrol_new eq_coeff_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band1", EQ_BAND1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band2", EQ_BAND2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band3", EQ_BAND3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band4", EQ_BAND4, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band5", EQ_BAND5, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band6", EQ_BAND6, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band7", EQ_BAND7, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band8", EQ_BAND8, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band9", EQ_BAND9, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band10", EQ_BAND10, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band11", EQ_BAND11, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band12", EQ_BAND12, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band1", EQ_BAND1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band2", EQ_BAND2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band3", EQ_BAND3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band4", EQ_BAND4, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band5", EQ_BAND5, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band6", EQ_BAND6, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band7", EQ_BAND7, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band8", EQ_BAND8, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band9", EQ_BAND9, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band10", EQ_BAND10, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band11", EQ_BAND11, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band12", EQ_BAND12, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band1", EQ_BAND1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band2", EQ_BAND2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band3", EQ_BAND3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band4", EQ_BAND4, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band5", EQ_BAND5, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band6", EQ_BAND6, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band7", EQ_BAND7, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band8", EQ_BAND8, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band9", EQ_BAND9, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band10", EQ_BAND10, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band11", EQ_BAND11, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band12", EQ_BAND12, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), +}; + +static const struct snd_kcontrol_new asphere_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("MSM ASphere Set Param", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set), +}; + +#ifdef CONFIG_QTI_PP +void msm_qti_pp_add_controls(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, int_fm_vol_mixer_controls, + ARRAY_SIZE(int_fm_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, pri_mi2s_lb_vol_mixer_controls, + ARRAY_SIZE(pri_mi2s_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, sec_mi2s_lb_vol_mixer_controls, + ARRAY_SIZE(sec_mi2s_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, tert_mi2s_lb_vol_mixer_controls, + ARRAY_SIZE(tert_mi2s_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, slimbus_8_lb_vol_mixer_controls, + ARRAY_SIZE(slimbus_8_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, int_hfp_vol_mixer_controls, + ARRAY_SIZE(int_hfp_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, + pri_auxpcm_lb_vol_mixer_controls, + ARRAY_SIZE(pri_auxpcm_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, + sec_auxpcm_lb_vol_mixer_controls, + ARRAY_SIZE(sec_auxpcm_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, + multi_ch_channel_map_mixer_controls, + ARRAY_SIZE(multi_ch_channel_map_mixer_controls)); + + snd_soc_add_platform_controls(platform, get_rms_controls, + ARRAY_SIZE(get_rms_controls)); + + snd_soc_add_platform_controls(platform, eq_enable_mixer_controls, + ARRAY_SIZE(eq_enable_mixer_controls)); + + snd_soc_add_platform_controls(platform, eq_band_mixer_controls, + ARRAY_SIZE(eq_band_mixer_controls)); + + snd_soc_add_platform_controls(platform, eq_coeff_mixer_controls, + ARRAY_SIZE(eq_coeff_mixer_controls)); + + snd_soc_add_platform_controls(platform, asphere_mixer_controls, + ARRAY_SIZE(asphere_mixer_controls)); +} +#endif /* CONFIG_QTI_PP */ diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h new file mode 100644 index 000000000000..805fb3e8584e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_QTI_PP_H_ +#define _MSM_QTI_PP_H_ + +#include + +#ifdef CONFIG_QTI_PP +void msm_qti_pp_send_eq_values(int fedai_id); +int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx, + unsigned int session_id, + uint16_t op_FL_ip_FL_weight, + uint16_t op_FL_ip_FR_weight, + uint16_t op_FR_ip_FL_weight, + uint16_t op_FR_ip_FR_weight); +void msm_qti_pp_add_controls(struct snd_soc_platform *platform); +#else /* CONFIG_QTI_PP */ +#define msm_qti_pp_send_eq_values(fedai_id) do {} while (0) +#define msm_qti_pp_send_stereo_to_custom_stereo_cmd(port_id, copp_idx, \ + session_id, op_FL_ip_FL_weight, op_FL_ip_FR_weight, \ + op_FR_ip_FL_weight, op_FR_ip_FR_weight) (0) +#define msm_qti_pp_add_controls(platform) do {} while (0) +#endif /* CONFIG_QTI_PP */ + + +#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE) +int msm_qti_pp_asphere_init(int port_id, int copp_idx); +void msm_qti_pp_asphere_deinit(int port_id); +#else +#define msm_qti_pp_asphere_init(port_id, copp_idx) (0) +#define msm_qti_pp_asphere_deinit(port_id) do {} while (0) +#endif + +#endif /* _MSM_QTI_PP_H_ */ diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c new file mode 100644 index 000000000000..26d2b80ff7af --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -0,0 +1,4407 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-dts-srs-tm-config.h" +#include + +#define TIMEOUT_MS 1000 + +#define RESET_COPP_ID 99 +#define INVALID_COPP_ID 0xFF +/* Used for inband payload copy, max size is 4k */ +/* 2 is to account for module & param ID in payload */ +#define ADM_GET_PARAMETER_LENGTH (4096 - APR_HDR_SIZE - 2 * sizeof(uint32_t)) + +#define ULL_SUPPORTED_BITS_PER_SAMPLE 16 +#define ULL_SUPPORTED_SAMPLE_RATE 48000 + +/* ENUM for adm_status */ +enum adm_cal_status { + ADM_STATUS_CALIBRATION_REQUIRED = 0, + ADM_STATUS_MAX, +}; + +struct adm_copp { + + atomic_t id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t cnt[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t topology[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t mode[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t rate[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t bit_width[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t channels[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t app_type[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t acdb_id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + wait_queue_head_t wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + wait_queue_head_t adm_delay_wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t adm_delay_stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; +}; + +struct source_tracking_data { + struct ion_client *ion_client; + struct ion_handle *ion_handle; + struct param_outband memmap; + int apr_cmd_status; +}; + +struct adm_ctl { + void *apr; + + struct adm_copp copp; + + atomic_t matrix_map_stat; + wait_queue_head_t matrix_map_wait; + + atomic_t adm_stat; + wait_queue_head_t adm_wait; + + struct cal_type_data *cal_data[ADM_MAX_CAL_TYPES]; + + atomic_t mem_map_handles[ADM_MEM_MAP_INDEX_MAX]; + atomic_t mem_map_index; + + struct param_outband outband_memmap; + struct source_tracking_data sourceTrackingData; + + int set_custom_topology; + int ec_ref_rx; +}; + +static struct adm_ctl this_adm; + +struct adm_multi_ch_map { + bool set_channel_map; + char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; +}; + +#define ADM_MCH_MAP_IDX_PLAYBACK 0 +#define ADM_MCH_MAP_IDX_REC 1 +static struct adm_multi_ch_map multi_ch_maps[2] = { + { false, + {0, 0, 0, 0, 0, 0, 0, 0} + }, + { false, + {0, 0, 0, 0, 0, 0, 0, 0} + } +}; + +static int adm_get_parameters[MAX_COPPS_PER_PORT * ADM_GET_PARAMETER_LENGTH]; +static int adm_module_topo_list[ + MAX_COPPS_PER_PORT * ADM_GET_TOPO_MODULE_LIST_LENGTH]; + +int adm_validate_and_get_port_index(int port_id) +{ + int index; + int ret; + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port validation failed id 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + + index = afe_get_port_index(port_id); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port idx %d port_id 0x%x\n", + __func__, index, + port_id); + return -EINVAL; + } + pr_debug("%s: port_idx- %d\n", __func__, index); + return index; +} + +int adm_get_default_copp_idx(int port_id) +{ + int port_idx = adm_validate_and_get_port_index(port_id), idx; + + if (port_idx < 0) { + pr_err("%s: Invalid port id: 0x%x", __func__, port_id); + return -EINVAL; + } + pr_debug("%s: port_idx:%d\n", __func__, port_idx); + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + if (atomic_read(&this_adm.copp.id[port_idx][idx]) != + RESET_COPP_ID) + return idx; + } + return -EINVAL; +} + +int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id) +{ + int port_idx = adm_validate_and_get_port_index(port_id), idx; + + if (port_idx < 0) { + pr_err("%s: Invalid port id: 0x%x", __func__, port_id); + return 0; + } + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (atomic_read(&this_adm.copp.id[port_idx][idx]) == copp_id) + return atomic_read(&this_adm.copp.topology[port_idx] + [idx]); + pr_err("%s: Invalid copp_id %d port_id 0x%x\n", + __func__, copp_id, port_id); + return 0; +} + +int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx) +{ + int port_idx = adm_validate_and_get_port_index(port_id); + + if (port_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid port: 0x%x copp id: 0x%x", + __func__, port_id, copp_idx); + return 0; + } + return atomic_read(&this_adm.copp.topology[port_idx][copp_idx]); +} + +int adm_get_indexes_from_copp_id(int copp_id, int *copp_idx, int *port_idx) +{ + int p_idx, c_idx; + + for (p_idx = 0; p_idx < AFE_MAX_PORTS; p_idx++) { + for (c_idx = 0; c_idx < MAX_COPPS_PER_PORT; c_idx++) { + if (atomic_read(&this_adm.copp.id[p_idx][c_idx]) + == copp_id) { + if (copp_idx != NULL) + *copp_idx = c_idx; + if (port_idx != NULL) + *port_idx = p_idx; + return 0; + } + } + } + return -EINVAL; +} + +static int adm_get_copp_id(int port_idx, int copp_idx) +{ + pr_debug("%s: port_idx:%d copp_idx:%d\n", __func__, port_idx, copp_idx); + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + return atomic_read(&this_adm.copp.id[port_idx][copp_idx]); +} + +static int adm_get_idx_if_copp_exists(int port_idx, int topology, int mode, + int rate, int bit_width, int app_type) +{ + int idx; + + pr_debug("%s: port_idx-%d, topology-0x%x, mode-%d, rate-%d, bit_width-%d\n", + __func__, port_idx, topology, mode, rate, bit_width); + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if ((topology == + atomic_read(&this_adm.copp.topology[port_idx][idx])) && + (mode == atomic_read(&this_adm.copp.mode[port_idx][idx])) && + (rate == atomic_read(&this_adm.copp.rate[port_idx][idx])) && + (bit_width == + atomic_read(&this_adm.copp.bit_width[port_idx][idx])) && + (app_type == + atomic_read(&this_adm.copp.app_type[port_idx][idx]))) + return idx; + return -EINVAL; +} + +static int adm_get_next_available_copp(int port_idx) +{ + int idx; + + pr_debug("%s:\n", __func__); + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + pr_debug("%s: copp_id:0x%x port_idx:%d idx:%d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx][idx]), + port_idx, idx); + if (atomic_read(&this_adm.copp.id[port_idx][idx]) == + RESET_COPP_ID) + break; + } + return idx; +} + +int adm_dts_eagle_set(int port_id, int copp_idx, int param_id, + void *data, uint32_t size) +{ + struct adm_cmd_set_pp_params_v5 admp; + int p_idx, ret = 0, *ob_params; + + pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X size %u\n", + __func__, port_id, copp_idx, param_id, size); + + port_id = afe_convert_virtual_to_portid(port_id); + p_idx = adm_validate_and_get_port_index(port_id); + pr_debug("DTS_EAGLE_ADM: %s - after lookup, port id %i, port idx %i\n", + __func__, port_id, p_idx); + + if (p_idx < 0) { + pr_err("DTS_EAGLE_ADM: %s: invalid port index 0x%x, port id 0x%x\n", + __func__, p_idx, port_id); + return -EINVAL; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__, + copp_idx); + return -EINVAL; + } + + ob_params = (int *)this_adm.outband_memmap.kvaddr; + if (ob_params == NULL) { + pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + /* check for integer overflow */ + if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) + ret = -EINVAL; + if ((ret < 0) || + (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) { + pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n", + __func__, this_adm.outband_memmap.size, + size + APR_CMD_OB_HDR_SZ); + ret = -EINVAL; + goto fail_cmd; + } + *ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX; + *ob_params++ = param_id; + *ob_params++ = size; + memcpy(ob_params, data, size); + + admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + admp.hdr.pkt_size = sizeof(admp); + admp.hdr.src_svc = APR_SVC_ADM; + admp.hdr.src_domain = APR_DOMAIN_APPS; + admp.hdr.src_port = port_id; + admp.hdr.dest_svc = APR_SVC_ADM; + admp.hdr.dest_domain = APR_DOMAIN_ADSP; + admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]); + admp.hdr.token = p_idx << 16 | copp_idx; + admp.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + admp.payload_addr_lsw = lower_32_bits(this_adm.outband_memmap.paddr); + admp.payload_addr_msw = msm_audio_populate_upper_32_bits( + this_adm.outband_memmap.paddr); + admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[ + ADM_DTS_EAGLE]); + admp.payload_size = size + sizeof(struct adm_param_data_v5); + + pr_debug("DTS_EAGLE_ADM: %s - Command was sent now check Q6 - port id = %d, size %d, module id %x, param id %x.\n", + __func__, admp.hdr.dest_port, + admp.payload_size, AUDPROC_MODULE_ID_DTS_HPX_POSTMIX, + param_id); + atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp); + if (ret < 0) { + pr_err("DTS_EAGLE_ADM: %s - ADM enable for port %d failed\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("DTS_EAGLE_ADM: %s - set params timed out port = %d\n", + __func__, port_id); + ret = -EINVAL; + } else if (atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx])); + } else { + ret = 0; + } + +fail_cmd: + return ret; +} + +int adm_dts_eagle_get(int port_id, int copp_idx, int param_id, + void *data, uint32_t size) +{ + struct adm_cmd_get_pp_params_v5 admp; + int p_idx, ret = 0, *ob_params; + uint32_t orig_size = size; + + pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X\n", + __func__, port_id, copp_idx, param_id); + + port_id = afe_convert_virtual_to_portid(port_id); + p_idx = adm_validate_and_get_port_index(port_id); + if (p_idx < 0) { + pr_err("DTS_EAGLE_ADM: %s - invalid port index %i, port id %i, copp idx %i\n", + __func__, p_idx, port_id, copp_idx); + return -EINVAL; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__, + copp_idx); + return -EINVAL; + } + + if ((size == 0) || !data) { + pr_err("DTS_EAGLE_ADM: %s - invalid size %u or pointer %pK.\n", + __func__, size, data); + return -EINVAL; + } + + size = (size+3) & 0xFFFFFFFC; + + ob_params = (int *)(this_adm.outband_memmap.kvaddr); + if (ob_params == NULL) { + pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + /* check for integer overflow */ + if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) + ret = -EINVAL; + if ((ret < 0) || + (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) { + pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n", + __func__, this_adm.outband_memmap.size, + size + APR_CMD_OB_HDR_SZ); + ret = -EINVAL; + goto fail_cmd; + } + *ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX; + *ob_params++ = param_id; + *ob_params++ = size; + + admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + admp.hdr.pkt_size = sizeof(admp); + admp.hdr.src_svc = APR_SVC_ADM; + admp.hdr.src_domain = APR_DOMAIN_APPS; + admp.hdr.src_port = port_id; + admp.hdr.dest_svc = APR_SVC_ADM; + admp.hdr.dest_domain = APR_DOMAIN_ADSP; + admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]); + admp.hdr.token = p_idx << 16 | copp_idx; + admp.hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5; + admp.data_payload_addr_lsw = + lower_32_bits(this_adm.outband_memmap.paddr); + admp.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + this_adm.outband_memmap.paddr); + admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[ + ADM_DTS_EAGLE]); + admp.module_id = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX; + admp.param_id = param_id; + admp.param_max_size = size + sizeof(struct adm_param_data_v5); + admp.reserved = 0; + + atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp); + if (ret < 0) { + pr_err("DTS_EAGLE_ADM: %s - Failed to get EAGLE Params on port %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("DTS_EAGLE_ADM: %s - EAGLE get params timed out port = %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx])); + goto fail_cmd; + } + + memcpy(data, ob_params, orig_size); + ret = 0; +fail_cmd: + return ret; +} + +int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, + void *srs_params) +{ + struct adm_cmd_set_pp_params_inband_v5 *adm_params = NULL; + struct adm_cmd_set_pp_params_v5 *adm_params_ = NULL; + __s32 sz = 0, param_id, module_id = SRS_TRUMEDIA_MODULE_ID, outband = 0; + int ret = 0, port_idx; + + pr_debug("SRS - %s", __func__); + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + return -EINVAL; + } + switch (srs_tech_id) { + case SRS_ID_GLOBAL: { + struct srs_trumedia_params_GLOBAL *glb_params = NULL; + + sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) + + sizeof(struct srs_trumedia_params_GLOBAL); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + adm_params->payload_size = + sizeof(struct srs_trumedia_params_GLOBAL) + + sizeof(struct adm_param_data_v5); + param_id = SRS_TRUMEDIA_PARAMS; + adm_params->params.param_size = + sizeof(struct srs_trumedia_params_GLOBAL); + glb_params = (struct srs_trumedia_params_GLOBAL *) + ((u8 *)adm_params + + sizeof(struct adm_cmd_set_pp_params_inband_v5)); + memcpy(glb_params, srs_params, + sizeof(struct srs_trumedia_params_GLOBAL)); + break; + } + case SRS_ID_WOWHD: { + struct srs_trumedia_params_WOWHD *whd_params = NULL; + + sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) + + sizeof(struct srs_trumedia_params_WOWHD); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + adm_params->payload_size = + sizeof(struct srs_trumedia_params_WOWHD) + + sizeof(struct adm_param_data_v5); + param_id = SRS_TRUMEDIA_PARAMS_WOWHD; + adm_params->params.param_size = + sizeof(struct srs_trumedia_params_WOWHD); + whd_params = (struct srs_trumedia_params_WOWHD *) + ((u8 *)adm_params + + sizeof(struct adm_cmd_set_pp_params_inband_v5)); + memcpy(whd_params, srs_params, + sizeof(struct srs_trumedia_params_WOWHD)); + break; + } + case SRS_ID_CSHP: { + struct srs_trumedia_params_CSHP *chp_params = NULL; + + sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) + + sizeof(struct srs_trumedia_params_CSHP); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + adm_params->payload_size = + sizeof(struct srs_trumedia_params_CSHP) + + sizeof(struct adm_param_data_v5); + param_id = SRS_TRUMEDIA_PARAMS_CSHP; + adm_params->params.param_size = + sizeof(struct srs_trumedia_params_CSHP); + chp_params = (struct srs_trumedia_params_CSHP *) + ((u8 *)adm_params + + sizeof(struct adm_cmd_set_pp_params_inband_v5)); + memcpy(chp_params, srs_params, + sizeof(struct srs_trumedia_params_CSHP)); + break; + } + case SRS_ID_HPF: { + struct srs_trumedia_params_HPF *hpf_params = NULL; + + sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) + + sizeof(struct srs_trumedia_params_HPF); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + adm_params->payload_size = + sizeof(struct srs_trumedia_params_HPF) + + sizeof(struct adm_param_data_v5); + param_id = SRS_TRUMEDIA_PARAMS_HPF; + adm_params->params.param_size = + sizeof(struct srs_trumedia_params_HPF); + hpf_params = (struct srs_trumedia_params_HPF *) + ((u8 *)adm_params + + sizeof(struct adm_cmd_set_pp_params_inband_v5)); + memcpy(hpf_params, srs_params, + sizeof(struct srs_trumedia_params_HPF)); + break; + } + case SRS_ID_AEQ: { + int *update_params_ptr = (int *)this_adm.outband_memmap.kvaddr; + + outband = 1; + adm_params = kzalloc(sizeof(struct adm_cmd_set_pp_params_v5), + GFP_KERNEL); + adm_params_ = (struct adm_cmd_set_pp_params_v5 *)adm_params; + if (!adm_params_) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + + sz = sizeof(struct srs_trumedia_params_AEQ); + if (update_params_ptr == NULL) { + pr_err("ADM_SRS_TRUMEDIA - %s: null memmap for AEQ params\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + param_id = SRS_TRUMEDIA_PARAMS_AEQ; + *update_params_ptr++ = module_id; + *update_params_ptr++ = param_id; + *update_params_ptr++ = sz; + memcpy(update_params_ptr, srs_params, sz); + + adm_params_->payload_size = sz + 12; + + break; + } + case SRS_ID_HL: { + struct srs_trumedia_params_HL *hl_params = NULL; + + sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) + + sizeof(struct srs_trumedia_params_HL); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + adm_params->payload_size = + sizeof(struct srs_trumedia_params_HL) + + sizeof(struct adm_param_data_v5); + param_id = SRS_TRUMEDIA_PARAMS_HL; + adm_params->params.param_size = + sizeof(struct srs_trumedia_params_HL); + hl_params = (struct srs_trumedia_params_HL *) + ((u8 *)adm_params + + sizeof(struct adm_cmd_set_pp_params_inband_v5)); + memcpy(hl_params, srs_params, + sizeof(struct srs_trumedia_params_HL)); + break; + } + case SRS_ID_GEQ: { + struct srs_trumedia_params_GEQ *geq_params = NULL; + + sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) + + sizeof(struct srs_trumedia_params_GEQ); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", + __func__); + return -ENOMEM; + } + adm_params->payload_size = + sizeof(struct srs_trumedia_params_GEQ) + + sizeof(struct adm_param_data_v5); + param_id = SRS_TRUMEDIA_PARAMS_GEQ; + adm_params->params.param_size = + sizeof(struct srs_trumedia_params_GEQ); + geq_params = (struct srs_trumedia_params_GEQ *) + ((u8 *)adm_params + + sizeof(struct adm_cmd_set_pp_params_inband_v5)); + memcpy(geq_params, srs_params, + sizeof(struct srs_trumedia_params_GEQ)); + pr_debug("SRS - %s: GEQ params prepared\n", __func__); + break; + } + default: + goto fail_cmd; + } + + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + if (outband && this_adm.outband_memmap.paddr) { + adm_params->hdr.pkt_size = + sizeof(struct adm_cmd_set_pp_params_v5); + adm_params->payload_addr_lsw = lower_32_bits( + this_adm.outband_memmap.paddr); + adm_params->payload_addr_msw = msm_audio_populate_upper_32_bits( + this_adm.outband_memmap.paddr); + adm_params->mem_map_handle = atomic_read(&this_adm. + mem_map_handles[ADM_SRS_TRUMEDIA]); + } else { + adm_params->hdr.pkt_size = sz; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + + adm_params->params.module_id = module_id; + adm_params->params.param_id = param_id; + adm_params->params.reserved = 0; + } + + pr_debug("SRS - %s: Command was sent now check Q6 - port id = %d, size %d, module id %x, param id %x.\n", + __func__, adm_params->hdr.dest_port, + adm_params->payload_size, module_id, param_id); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (ret < 0) { + pr_err("SRS - %s: ADM enable for port %d failed\n", __func__, + port_id); + ret = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: SRS set params timed out port = %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto fail_cmd; + } + +fail_cmd: + kfree(adm_params); + return ret; +} + +int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx, + unsigned int session_id, char *params, + uint32_t params_length) +{ + struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL; + int sz, rc = 0, port_idx; + + pr_debug("%s:\n", __func__); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = 0; /* Ignored */; + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->payload_size = params_length; + /* direction RX as 0 */ + adm_params->direction = ADM_MATRIX_ID_AUDIO_RX; + /* session id for this cmd to be applied on */ + adm_params->sessionid = session_id; + adm_params->deviceid = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->reserved = 0; + pr_debug("%s: deviceid %d, session_id %d, src_port %d, dest_port %d\n", + __func__, adm_params->deviceid, adm_params->sessionid, + adm_params->hdr.src_port, adm_params->hdr.dest_port); + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = 0x%x rc %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto set_stereo_to_custom_stereo_return; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = 0x%x\n", __func__, + port_id); + rc = -EINVAL; + goto set_stereo_to_custom_stereo_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto set_stereo_to_custom_stereo_return; + } + rc = 0; +set_stereo_to_custom_stereo_return: + kfree(adm_params); + return rc; +} + +int adm_dolby_dap_send_params(int port_id, int copp_idx, char *params, + uint32_t params_length) +{ + struct adm_cmd_set_pp_params_v5 *adm_params = NULL; + int sz, rc = 0; + int port_idx; + + pr_debug("%s:\n", __func__); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_set_pp_params_v5) + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->payload_size = params_length; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = 0x%x rc %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto dolby_dap_send_param_return; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = 0x%x\n", + __func__, port_id); + rc = -EINVAL; + goto dolby_dap_send_param_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto dolby_dap_send_param_return; + } + rc = 0; +dolby_dap_send_param_return: + kfree(adm_params); + return rc; +} + +int adm_send_params_v5(int port_id, int copp_idx, char *params, + uint32_t params_length) +{ + struct adm_cmd_set_pp_params_v5 *adm_params = NULL; + int rc = 0; + int sz, port_idx; + + pr_debug("%s:\n", __func__); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_set_pp_params_v5) + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->payload_size = params_length; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = 0x%x rc %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto send_param_return; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = 0x%x\n", + __func__, port_id); + rc = -EINVAL; + goto send_param_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto send_param_return; + } + rc = 0; +send_param_return: + kfree(adm_params); + return rc; +} + +int adm_get_params_v2(int port_id, int copp_idx, uint32_t module_id, + uint32_t param_id, uint32_t params_length, + char *params, uint32_t client_id) +{ + struct adm_cmd_get_pp_params_v5 *adm_params = NULL; + int sz, rc = 0, i = 0; + int port_idx, idx; + int *params_data = (int *)params; + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_get_pp_params_v5) + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s: adm params memory alloc failed", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_get_pp_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | client_id << 8 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5; + adm_params->data_payload_addr_lsw = 0; + adm_params->data_payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->module_id = module_id; + adm_params->param_id = param_id; + adm_params->param_max_size = params_length; + adm_params->reserved = 0; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Failed to Get Params on port_id 0x%x %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto adm_get_param_return; + } + /* Wait for the callback with copp id */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: get params timed out port_id = 0x%x\n", __func__, + port_id); + rc = -EINVAL; + goto adm_get_param_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto adm_get_param_return; + } + idx = ADM_GET_PARAMETER_LENGTH * copp_idx; + + if (adm_get_parameters[idx] < 0) { + pr_err("%s: Size is invalid %d\n", __func__, + adm_get_parameters[idx]); + rc = -EINVAL; + goto adm_get_param_return; + } + if ((params_data) && + (ARRAY_SIZE(adm_get_parameters) > + idx) && + (ARRAY_SIZE(adm_get_parameters) >= + 1+adm_get_parameters[idx]+idx) && + (params_length/sizeof(uint32_t) >= + adm_get_parameters[idx])) { + for (i = 0; i < adm_get_parameters[idx]; i++) + params_data[i] = adm_get_parameters[1+i+idx]; + + } else { + pr_err("%s: Get param data not copied! get_param array size %zd, index %d, params array size %zd, index %d\n", + __func__, ARRAY_SIZE(adm_get_parameters), + (1+adm_get_parameters[idx]+idx), + params_length/sizeof(int), + adm_get_parameters[idx]); + } + rc = 0; +adm_get_param_return: + kfree(adm_params); + + return rc; +} + +int adm_get_params(int port_id, int copp_idx, uint32_t module_id, + uint32_t param_id, uint32_t params_length, char *params) +{ + return adm_get_params_v2(port_id, copp_idx, module_id, param_id, + params_length, params, 0); +} + +int adm_get_pp_topo_module_list(int port_id, int copp_idx, int32_t param_length, + char *params) +{ + struct adm_cmd_get_pp_topo_module_list_t *adm_pp_module_list = NULL; + int sz, rc = 0, i = 0; + int port_idx, idx; + int32_t *params_data = (int32_t *)params; + int *topo_list; + + pr_debug("%s : port_id %x", __func__, port_id); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_get_pp_topo_module_list_t) + param_length; + adm_pp_module_list = kzalloc(sz, GFP_KERNEL); + if (!adm_pp_module_list) { + pr_err("%s, adm params memory alloc failed", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_pp_module_list + + sizeof(struct adm_cmd_get_pp_topo_module_list_t)), + params, param_length); + adm_pp_module_list->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_pp_module_list->hdr.pkt_size = sz; + adm_pp_module_list->hdr.src_svc = APR_SVC_ADM; + adm_pp_module_list->hdr.src_domain = APR_DOMAIN_APPS; + adm_pp_module_list->hdr.src_port = port_id; + adm_pp_module_list->hdr.dest_svc = APR_SVC_ADM; + adm_pp_module_list->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_pp_module_list->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_pp_module_list->hdr.token = port_idx << 16 | copp_idx; + adm_pp_module_list->hdr.opcode = ADM_CMD_GET_PP_TOPO_MODULE_LIST; + adm_pp_module_list->param_max_size = param_length; + /* Payload address and mmap handle set to zero by kzalloc */ + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_pp_module_list); + if (rc < 0) { + pr_err("%s: Failed to Get Params on port %d\n", __func__, + port_id); + rc = -EINVAL; + goto adm_pp_module_list_l; + } + /* Wait for the callback with copp id */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: get params timed out port = %d\n", __func__, + port_id); + rc = -EINVAL; + goto adm_pp_module_list_l; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto adm_pp_module_list_l; + } + if (params_data) { + idx = ADM_GET_TOPO_MODULE_LIST_LENGTH * copp_idx; + topo_list = (int *)(adm_module_topo_list + idx); + if (param_length <= ADM_GET_TOPO_MODULE_LIST_LENGTH && + idx < + (MAX_COPPS_PER_PORT * ADM_GET_TOPO_MODULE_LIST_LENGTH)) + memcpy(params_data, topo_list, param_length); + else + pr_debug("%s: i/p size:%d > MAX param size:%d\n", + __func__, param_length, + (int)ADM_GET_TOPO_MODULE_LIST_LENGTH); + for (i = 1; i <= params_data[0]; i++) + pr_debug("module = 0x%x\n", params_data[i]); + } + rc = 0; +adm_pp_module_list_l: + kfree(adm_pp_module_list); + pr_debug("%s : rc = %d ", __func__, rc); + return rc; +} +static void adm_callback_debug_print(struct apr_client_data *data) +{ + uint32_t *payload; + + payload = data->payload; + + if (data->payload_size >= 8) + pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n", + __func__, data->opcode, payload[0], payload[1], + data->payload_size); + else if (data->payload_size >= 4) + pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n", + __func__, data->opcode, payload[0], + data->payload_size); + else + pr_debug("%s: code = 0x%x, size = %d\n", + __func__, data->opcode, data->payload_size); +} + +int adm_set_multi_ch_map(char *channel_map, int path) +{ + int idx; + + if (path == ADM_PATH_PLAYBACK) { + idx = ADM_MCH_MAP_IDX_PLAYBACK; + } else if (path == ADM_PATH_LIVE_REC) { + idx = ADM_MCH_MAP_IDX_REC; + } else { + pr_err("%s: invalid attempt to set path %d\n", __func__, path); + return -EINVAL; + } + + memcpy(multi_ch_maps[idx].channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + multi_ch_maps[idx].set_channel_map = true; + + return 0; +} + +int adm_get_multi_ch_map(char *channel_map, int path) +{ + int idx; + + if (path == ADM_PATH_PLAYBACK) { + idx = ADM_MCH_MAP_IDX_PLAYBACK; + } else if (path == ADM_PATH_LIVE_REC) { + idx = ADM_MCH_MAP_IDX_REC; + } else { + pr_err("%s: invalid attempt to get path %d\n", __func__, path); + return -EINVAL; + } + + if (multi_ch_maps[idx].set_channel_map) { + memcpy(channel_map, multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + return 0; +} + +static int32_t adm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *payload; + int i, j, port_idx, copp_idx, idx, client_id; + + if (data == NULL) { + pr_err("%s: data parameter is null\n", __func__); + return -EINVAL; + } + + payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", + __func__, + data->reset_event, data->reset_proc, this_adm.apr); + if (this_adm.apr) { + apr_reset(this_adm.apr); + for (i = 0; i < AFE_MAX_PORTS; i++) { + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + atomic_set(&this_adm.copp.id[i][j], + RESET_COPP_ID); + atomic_set(&this_adm.copp.cnt[i][j], 0); + atomic_set( + &this_adm.copp.topology[i][j], 0); + atomic_set(&this_adm.copp.mode[i][j], + 0); + atomic_set(&this_adm.copp.stat[i][j], + 0); + atomic_set(&this_adm.copp.rate[i][j], + 0); + atomic_set( + &this_adm.copp.channels[i][j], + 0); + atomic_set( + &this_adm.copp.bit_width[i][j], 0); + atomic_set( + &this_adm.copp.app_type[i][j], 0); + atomic_set( + &this_adm.copp.acdb_id[i][j], 0); + this_adm.copp.adm_status[i][j] = + ADM_STATUS_CALIBRATION_REQUIRED; + } + } + this_adm.apr = NULL; + cal_utils_clear_cal_block_q6maps(ADM_MAX_CAL_TYPES, + this_adm.cal_data); + mutex_lock(&this_adm.cal_data + [ADM_CUSTOM_TOP_CAL]->lock); + this_adm.set_custom_topology = 1; + mutex_unlock(&this_adm.cal_data[ + ADM_CUSTOM_TOP_CAL]->lock); + rtac_clear_mapping(ADM_RTAC_CAL); + /* + * Free the ION memory and clear the map handles + * for Source Tracking + */ + if (this_adm.sourceTrackingData.memmap.paddr != 0) { + msm_audio_ion_free( + this_adm.sourceTrackingData.ion_client, + this_adm.sourceTrackingData.ion_handle); + this_adm.sourceTrackingData.ion_client = NULL; + this_adm.sourceTrackingData.ion_handle = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = + NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles[ + ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0); + } + } + return 0; + } + + adm_callback_debug_print(data); + if (data->payload_size) { + copp_idx = (data->token) & 0XFF; + port_idx = ((data->token) >> 16) & 0xFF; + client_id = ((data->token) >> 8) & 0xFF; + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port idx %d token %d\n", + __func__, port_idx, data->token); + return 0; + } + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp idx %d token %d\n", + __func__, copp_idx, data->token); + return 0; + } + if (client_id < 0 || client_id >= ADM_CLIENT_ID_MAX) { + pr_err("%s: Invalid client id %d\n", __func__, + client_id); + return 0; + } + if (data->opcode == APR_BASIC_RSP_RESULT) { + pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n", + __func__, payload[0]); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + } + switch (payload[0]) { + case ADM_CMD_SET_PP_PARAMS_V5: + pr_debug("%s: ADM_CMD_SET_PP_PARAMS_V5\n", + __func__); + if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING) + this_adm.sourceTrackingData. + apr_cmd_status = payload[1]; + else if (rtac_make_adm_callback(payload, + data->payload_size)) + break; + /* + * if soft volume is called and already + * interrupted break out of the sequence here + */ + case ADM_CMD_DEVICE_OPEN_V5: + case ADM_CMD_DEVICE_CLOSE_V5: + pr_debug("%s: Basic callback received, wake up.\n", + __func__); + atomic_set(&this_adm.copp.stat[port_idx] + [copp_idx], payload[1]); + wake_up( + &this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMD_ADD_TOPOLOGIES: + pr_debug("%s: callback received, ADM_CMD_ADD_TOPOLOGIES.\n", + __func__); + atomic_set(&this_adm.adm_stat, payload[1]); + wake_up(&this_adm.adm_wait); + break; + case ADM_CMD_MATRIX_MAP_ROUTINGS_V5: + case ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5: + pr_debug("%s: Basic callback received, wake up.\n", + __func__); + atomic_set(&this_adm.matrix_map_stat, + payload[1]); + wake_up(&this_adm.matrix_map_wait); + break; + case ADM_CMD_SHARED_MEM_UNMAP_REGIONS: + pr_debug("%s: ADM_CMD_SHARED_MEM_UNMAP_REGIONS\n", + __func__); + atomic_set(&this_adm.adm_stat, payload[1]); + wake_up(&this_adm.adm_wait); + break; + case ADM_CMD_SHARED_MEM_MAP_REGIONS: + pr_debug("%s: ADM_CMD_SHARED_MEM_MAP_REGIONS\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + if (payload[1] != 0) { + pr_err("%s: ADM map error, resuming\n", + __func__); + atomic_set(&this_adm.adm_stat, + payload[1]); + wake_up(&this_adm.adm_wait); + } + break; + case ADM_CMD_GET_PP_PARAMS_V5: + pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* ADM_CMDRSP_GET_PP_PARAMS_V5 */ + if (client_id == + ADM_CLIENT_ID_SOURCE_TRACKING) { + this_adm.sourceTrackingData. + apr_cmd_status = payload[1]; + if (payload[1] != 0) + pr_err("%s: ADM get param error = %d\n", + __func__, payload[1]); + + atomic_set(&this_adm.copp.stat + [port_idx][copp_idx], + payload[1]); + wake_up(&this_adm.copp.wait + [port_idx][copp_idx]); + } else { + if (payload[1] != 0) { + pr_err("%s: ADM get param error = %d, resuming\n", + __func__, payload[1]); + + rtac_make_adm_callback(payload, + data->payload_size); + } + } + break; + case ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5: + pr_debug("%s: ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5\n", + __func__); + atomic_set(&this_adm.copp.stat[port_idx] + [copp_idx], payload[1]); + wake_up( + &this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMD_GET_PP_TOPO_MODULE_LIST: + pr_debug("%s:ADM_CMD_GET_PP_TOPO_MODULE_LIST\n", + __func__); + if (payload[1] != 0) + pr_err("%s: ADM get topo list error = %d,\n", + __func__, payload[1]); + break; + default: + pr_err("%s: Unknown Cmd: 0x%x\n", __func__, + payload[0]); + break; + } + return 0; + } + + switch (data->opcode) { + case ADM_CMDRSP_DEVICE_OPEN_V5: { + struct adm_cmd_rsp_device_open_v5 *open = + (struct adm_cmd_rsp_device_open_v5 *)data->payload; + + if (open->copp_id == INVALID_COPP_ID) { + pr_err("%s: invalid coppid rxed %d\n", + __func__, open->copp_id); + atomic_set(&this_adm.copp.stat[port_idx] + [copp_idx], ADSP_EBADPARAM); + wake_up( + &this_adm.copp.wait[port_idx][copp_idx]); + break; + } + atomic_set(&this_adm.copp.stat + [port_idx][copp_idx], payload[0]); + atomic_set(&this_adm.copp.id[port_idx][copp_idx], + open->copp_id); + pr_debug("%s: coppid rxed=%d\n", __func__, + open->copp_id); + wake_up(&this_adm.copp.wait[port_idx][copp_idx]); + } + break; + case ADM_CMDRSP_GET_PP_PARAMS_V5: + pr_debug("%s: ADM_CMDRSP_GET_PP_PARAMS_V5\n", __func__); + if (payload[0] != 0) + pr_err("%s: ADM_CMDRSP_GET_PP_PARAMS_V5 returned error = 0x%x\n", + __func__, payload[0]); + if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING) + this_adm.sourceTrackingData.apr_cmd_status = + payload[0]; + else if (rtac_make_adm_callback(payload, + data->payload_size)) + break; + + idx = ADM_GET_PARAMETER_LENGTH * copp_idx; + if ((payload[0] == 0) && (data->payload_size > + (4 * sizeof(*payload))) && + (data->payload_size - 4 >= + payload[3]) && + (ARRAY_SIZE(adm_get_parameters) > + idx) && + (ARRAY_SIZE(adm_get_parameters)-idx-1 >= + payload[3])) { + adm_get_parameters[idx] = payload[3] / + sizeof(uint32_t); + /* + * payload[3] is param_size which is + * expressed in number of bytes + */ + pr_debug("%s: GET_PP PARAM:received parameter length: 0x%x\n", + __func__, adm_get_parameters[idx]); + /* storing param size then params */ + for (i = 0; i < payload[3] / + sizeof(uint32_t); i++) + adm_get_parameters[idx+1+i] = + payload[4+i]; + } else if (payload[0] == 0) { + adm_get_parameters[idx] = -1; + pr_err("%s: Out of band case, setting size to %d\n", + __func__, adm_get_parameters[idx]); + } else { + adm_get_parameters[idx] = -1; + pr_err("%s: GET_PP_PARAMS failed, setting size to %d\n", + __func__, adm_get_parameters[idx]); + } + atomic_set(&this_adm.copp.stat + [port_idx][copp_idx], payload[0]); + wake_up(&this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST: + pr_debug("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST\n", + __func__); + if (payload[0] != 0) { + pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST", + __func__); + pr_err(":err = 0x%x\n", payload[0]); + } else if (payload[1] > + ((ADM_GET_TOPO_MODULE_LIST_LENGTH / + sizeof(uint32_t)) - 1)) { + pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST", + __func__); + pr_err(":size = %d\n", payload[1]); + } else { + idx = ADM_GET_TOPO_MODULE_LIST_LENGTH * + copp_idx; + pr_debug("%s:Num modules payload[1] %d\n", + __func__, payload[1]); + adm_module_topo_list[idx] = payload[1]; + for (i = 1; i <= payload[1]; i++) { + adm_module_topo_list[idx+i] = + payload[1+i]; + pr_debug("%s:payload[%d] = %x\n", + __func__, (i+1), payload[1+i]); + } + } + atomic_set(&this_adm.copp.stat + [port_idx][copp_idx], payload[0]); + wake_up(&this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMDRSP_SHARED_MEM_MAP_REGIONS: + pr_debug("%s: ADM_CMDRSP_SHARED_MEM_MAP_REGIONS\n", + __func__); + atomic_set(&this_adm.mem_map_handles[ + atomic_read(&this_adm.mem_map_index)], + *payload); + atomic_set(&this_adm.adm_stat, 0); + wake_up(&this_adm.adm_wait); + break; + default: + pr_err("%s: Unknown cmd:0x%x\n", __func__, + data->opcode); + break; + } + } + return 0; +} + +static int adm_memory_map_regions(phys_addr_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + pr_debug("%s:\n", __func__); + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_adm_handle(this_adm.apr); + } + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload) + * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; + mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mmap_regions->hdr.pkt_size = cmd_size; + mmap_regions->hdr.src_port = 0; + + mmap_regions->hdr.dest_port = 0; + mmap_regions->hdr.token = 0; + mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff; + mmap_regions->num_regions = bufcnt & 0x00ff; + mmap_regions->property_flag = 0x00; + + pr_debug("%s: map_regions->num_regions = %d\n", __func__, + mmap_regions->num_regions); + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->shm_addr_lsw = lower_32_bits(buf_add[i]); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add[i]); + mregions->mem_size_bytes = bufsz[i]; + ++mregions; + } + + atomic_set(&this_adm.adm_stat, -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd); + if (ret < 0) { + pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.adm_wait, + atomic_read(&this_adm.adm_stat) >= 0, + 5 * HZ); + if (!ret) { + pr_err("%s: timeout. waited for memory_map\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.adm_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.adm_stat))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.adm_stat)); + goto fail_cmd; + } +fail_cmd: + kfree(mmap_region_cmd); + return ret; +} + +static int adm_memory_unmap_regions(void) +{ + struct avs_cmd_shared_mem_unmap_regions unmap_regions; + int ret = 0; + + pr_debug("%s:\n", __func__); + if (this_adm.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + unmap_regions.hdr.pkt_size = sizeof(unmap_regions); + unmap_regions.hdr.src_port = 0; + unmap_regions.hdr.dest_port = 0; + unmap_regions.hdr.token = 0; + unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS; + unmap_regions.mem_map_handle = atomic_read(&this_adm. + mem_map_handles[atomic_read(&this_adm.mem_map_index)]); + atomic_set(&this_adm.adm_stat, -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions); + if (ret < 0) { + pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + unmap_regions.hdr.opcode, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.adm_wait, + atomic_read(&this_adm.adm_stat) >= 0, + 5 * HZ); + if (!ret) { + pr_err("%s: timeout. waited for memory_unmap\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.adm_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.adm_stat))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.adm_stat)); + goto fail_cmd; + } else { + pr_debug("%s: Unmap handle 0x%x succeeded\n", __func__, + unmap_regions.mem_map_handle); + } +fail_cmd: + return ret; +} + +static int remap_cal_data(struct cal_block_data *cal_block, int cal_index) +{ + int ret = 0; + + if (cal_block->map_data.ion_client == NULL) { + pr_err("%s: No ION allocation for cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + atomic_set(&this_adm.mem_map_index, cal_index); + ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0, + (uint32_t *)&cal_block->map_data.map_size, 1); + if (ret < 0) { + pr_err("%s: ADM mmap did not work! size = %zd ret %d\n", + __func__, + cal_block->map_data.map_size, ret); + pr_debug("%s: ADM mmap did not work! addr = 0x%pK, size = %zd ret %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size, ret); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_adm. + mem_map_handles[cal_index]); + } +done: + return ret; +} + +static void send_adm_custom_topology(void) +{ + struct cal_block_data *cal_block = NULL; + struct cmd_set_topologies adm_top; + int cal_index = ADM_CUSTOM_TOP_CAL; + int result; + + if (this_adm.cal_data[cal_index] == NULL) + goto done; + + mutex_lock(&this_adm.cal_data[cal_index]->lock); + if (!this_adm.set_custom_topology) + goto unlock; + this_adm.set_custom_topology = 0; + + cal_block = cal_utils_get_only_cal_block(this_adm.cal_data[cal_index]); + if (cal_block == NULL) + goto unlock; + + pr_debug("%s: Sending cal_index %d\n", __func__, cal_index); + + result = remap_cal_data(cal_block, cal_index); + if (result) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto unlock; + } + atomic_set(&this_adm.mem_map_index, cal_index); + atomic_set(&this_adm.mem_map_handles[cal_index], + cal_block->map_data.q6map_handle); + + if (cal_block->cal_data.size == 0) { + pr_debug("%s: No ADM cal to send\n", __func__); + goto unlock; + } + + adm_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_top.hdr.pkt_size = sizeof(adm_top); + adm_top.hdr.src_svc = APR_SVC_ADM; + adm_top.hdr.src_domain = APR_DOMAIN_APPS; + adm_top.hdr.src_port = 0; + adm_top.hdr.dest_svc = APR_SVC_ADM; + adm_top.hdr.dest_domain = APR_DOMAIN_ADSP; + adm_top.hdr.dest_port = 0; + adm_top.hdr.token = 0; + adm_top.hdr.opcode = ADM_CMD_ADD_TOPOLOGIES; + adm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr); + adm_top.payload_addr_msw = msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + adm_top.mem_map_handle = cal_block->map_data.q6map_handle; + adm_top.payload_size = cal_block->cal_data.size; + + atomic_set(&this_adm.adm_stat, -1); + pr_debug("%s: Sending ADM_CMD_ADD_TOPOLOGIES payload = 0x%pK, size = %d\n", + __func__, &cal_block->cal_data.paddr, + adm_top.payload_size); + result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_top); + if (result < 0) { + pr_err("%s: Set topologies failed payload size = %zd result %d\n", + __func__, cal_block->cal_data.size, result); + goto unlock; + } + /* Wait for the callback */ + result = wait_event_timeout(this_adm.adm_wait, + atomic_read(&this_adm.adm_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set topologies timed out payload size = %zd\n", + __func__, cal_block->cal_data.size); + goto unlock; + } else if (atomic_read(&this_adm.adm_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.adm_stat))); + result = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.adm_stat)); + goto unlock; + } +unlock: + mutex_unlock(&this_adm.cal_data[cal_index]->lock); +done: + return; +} + +static int send_adm_cal_block(int port_id, int copp_idx, + struct cal_block_data *cal_block, int perf_mode, + int app_type, int acdb_id, int sample_rate) +{ + s32 result = 0; + struct adm_cmd_set_pp_params_v5 adm_params; + int port_idx; + + pr_debug("%s: Port id 0x%x sample_rate %d ,\n", __func__, + port_id, sample_rate); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + if (!cal_block) { + pr_debug("%s: No ADM cal to send for port_id = 0x%x!\n", + __func__, port_id); + result = -EINVAL; + goto done; + } + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: No ADM cal send for port_id = 0x%x!\n", + __func__, port_id); + result = -EINVAL; + goto done; + } + + if (perf_mode == LEGACY_PCM_MODE && + ((atomic_read(&this_adm.copp.topology[port_idx][copp_idx])) == + DS2_ADM_COPP_TOPOLOGY_ID)) { + pr_err("%s: perf_mode %d, topology 0x%x\n", __func__, perf_mode, + atomic_read( + &this_adm.copp.topology[port_idx][copp_idx])); + goto done; + } + + adm_params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_params.hdr.pkt_size = sizeof(adm_params); + adm_params.hdr.src_svc = APR_SVC_ADM; + adm_params.hdr.src_domain = APR_DOMAIN_APPS; + adm_params.hdr.src_port = port_id; + adm_params.hdr.dest_svc = APR_SVC_ADM; + adm_params.hdr.dest_domain = APR_DOMAIN_ADSP; + + adm_params.hdr.token = port_idx << 16 | copp_idx; + adm_params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + adm_params.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr); + adm_params.payload_addr_msw = msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + adm_params.mem_map_handle = cal_block->map_data.q6map_handle; + adm_params.payload_size = cal_block->cal_data.size; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + pr_debug("%s: Sending SET_PARAMS payload = 0x%pK, size = %d\n", + __func__, &cal_block->cal_data.paddr, + adm_params.payload_size); + result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_params); + if (result < 0) { + pr_err("%s: Set params failed port 0x%x result %d\n", + __func__, port_id, result); + pr_debug("%s: Set params failed port = 0x%x payload = 0x%pK result %d\n", + __func__, port_id, &cal_block->cal_data.paddr, result); + result = -EINVAL; + goto done; + } + /* Wait for the callback */ + result = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set params timed out port = 0x%x\n", + __func__, port_id); + pr_debug("%s: Set params timed out port = 0x%x, payload = 0x%pK\n", + __func__, port_id, &cal_block->cal_data.paddr); + result = -EINVAL; + goto done; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + result = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto done; + } + +done: + return result; +} + +static struct cal_block_data *adm_find_cal_by_path(int cal_index, int path) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audproc *audproc_cal_info = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + + pr_debug("%s:\n", __func__); + + list_for_each_safe(ptr, next, + &this_adm.cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_index == ADM_AUDPROC_CAL) { + audproc_cal_info = cal_block->cal_info; + if ((audproc_cal_info->path == path) && + (cal_block->cal_data.size > 0)) + return cal_block; + } else if (cal_index == ADM_AUDVOL_CAL) { + audvol_cal_info = cal_block->cal_info; + if ((audvol_cal_info->path == path) && + (cal_block->cal_data.size > 0)) + return cal_block; + } + } + pr_debug("%s: Can't find ADM cal for cal_index %d, path %d\n", + __func__, cal_index, path); + return NULL; +} + +static struct cal_block_data *adm_find_cal_by_app_type(int cal_index, int path, + int app_type) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audproc *audproc_cal_info = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &this_adm.cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_index == ADM_AUDPROC_CAL) { + audproc_cal_info = cal_block->cal_info; + if ((audproc_cal_info->path == path) && + (audproc_cal_info->app_type == app_type) && + (cal_block->cal_data.size > 0)) + return cal_block; + } else if (cal_index == ADM_AUDVOL_CAL) { + audvol_cal_info = cal_block->cal_info; + if ((audvol_cal_info->path == path) && + (audvol_cal_info->app_type == app_type) && + (cal_block->cal_data.size > 0)) + return cal_block; + } + } + pr_debug("%s: Can't find ADM cali for cal_index %d, path %d, app %d, defaulting to search by path\n", + __func__, cal_index, path, app_type); + return adm_find_cal_by_path(cal_index, path); +} + + +static struct cal_block_data *adm_find_cal(int cal_index, int path, + int app_type, int acdb_id, + int sample_rate) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audproc *audproc_cal_info = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + + pr_debug("%s:\n", __func__); + + list_for_each_safe(ptr, next, + &this_adm.cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_index == ADM_AUDPROC_CAL) { + audproc_cal_info = cal_block->cal_info; + if ((audproc_cal_info->path == path) && + (audproc_cal_info->app_type == app_type) && + (audproc_cal_info->acdb_id == acdb_id) && + (audproc_cal_info->sample_rate == sample_rate) && + (cal_block->cal_data.size > 0)) + return cal_block; + } else if (cal_index == ADM_AUDVOL_CAL) { + audvol_cal_info = cal_block->cal_info; + if ((audvol_cal_info->path == path) && + (audvol_cal_info->app_type == app_type) && + (audvol_cal_info->acdb_id == acdb_id) && + (cal_block->cal_data.size > 0)) + return cal_block; + } + } + pr_debug("%s: Can't find ADM cal for cal_index %d, path %d, app %d, acdb_id %d sample_rate %d defaulting to search by app type\n", + __func__, cal_index, path, app_type, acdb_id, sample_rate); + return adm_find_cal_by_app_type(cal_index, path, app_type); +} + +static int adm_remap_and_send_cal_block(int cal_index, int port_id, + int copp_idx, struct cal_block_data *cal_block, int perf_mode, + int app_type, int acdb_id, int sample_rate) +{ + int ret = 0; + + pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index); + ret = remap_cal_data(cal_block, cal_index); + if (ret) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto done; + } + ret = send_adm_cal_block(port_id, copp_idx, cal_block, perf_mode, + app_type, acdb_id, sample_rate); + if (ret < 0) + pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d sample_rate %d\n", + __func__, cal_index, port_id, ret, sample_rate); +done: + return ret; +} + +static void send_adm_cal_type(int cal_index, int path, int port_id, + int copp_idx, int perf_mode, int app_type, + int acdb_id, int sample_rate) +{ + struct cal_block_data *cal_block = NULL; + int ret; + + pr_debug("%s: cal index %d\n", __func__, cal_index); + + if (this_adm.cal_data[cal_index] == NULL) { + pr_debug("%s: cal_index %d not allocated!\n", + __func__, cal_index); + goto done; + } + + mutex_lock(&this_adm.cal_data[cal_index]->lock); + cal_block = adm_find_cal(cal_index, path, app_type, acdb_id, + sample_rate); + if (cal_block == NULL) + goto unlock; + + ret = adm_remap_and_send_cal_block(cal_index, port_id, copp_idx, + cal_block, perf_mode, app_type, acdb_id, sample_rate); +unlock: + mutex_unlock(&this_adm.cal_data[cal_index]->lock); +done: + return; +} + +static int get_cal_path(int path) +{ + if (path == 0x1) + return RX_DEVICE; + else + return TX_DEVICE; +} + +static void send_adm_cal(int port_id, int copp_idx, int path, int perf_mode, + int app_type, int acdb_id, int sample_rate) +{ + pr_debug("%s: port id 0x%x copp_idx %d\n", __func__, port_id, copp_idx); + + send_adm_cal_type(ADM_AUDPROC_CAL, path, port_id, copp_idx, perf_mode, + app_type, acdb_id, sample_rate); + send_adm_cal_type(ADM_AUDVOL_CAL, path, port_id, copp_idx, perf_mode, + app_type, acdb_id, sample_rate); +} + +int adm_connect_afe_port(int mode, int session_id, int port_id) +{ + struct adm_cmd_connect_afe_port_v5 cmd; + int ret = 0; + int port_idx, copp_idx = 0; + + pr_debug("%s: port_id: 0x%x session id:%d mode:%d\n", __func__, + port_id, session_id, mode); + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_adm_handle(this_adm.apr); + } + pr_debug("%s: Port ID 0x%x, index %d\n", __func__, port_id, port_idx); + + cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd.hdr.pkt_size = sizeof(cmd); + cmd.hdr.src_svc = APR_SVC_ADM; + cmd.hdr.src_domain = APR_DOMAIN_APPS; + cmd.hdr.src_port = port_id; + cmd.hdr.dest_svc = APR_SVC_ADM; + cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cmd.hdr.dest_port = 0; /* Ignored */ + cmd.hdr.token = port_idx << 16 | copp_idx; + cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V5; + + cmd.mode = mode; + cmd.session_id = session_id; + cmd.afe_port_id = port_id; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd); + if (ret < 0) { + pr_err("%s: ADM enable for port_id: 0x%x failed ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM connect timedout for port_id: 0x%x\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto fail_cmd; + } + atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]); + return 0; + +fail_cmd: + + return ret; +} + +int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path, + int channel_mode) +{ + int rc = 0, idx; + + memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + switch (path) { + case ADM_PATH_PLAYBACK: + idx = ADM_MCH_MAP_IDX_PLAYBACK; + break; + case ADM_PATH_LIVE_REC: + case ADM_PATH_NONLIVE_REC: + idx = ADM_MCH_MAP_IDX_REC; + break; + default: + goto non_mch_path; + }; + if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) { + memcpy(open->dev_channel_mapping, + multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL); + } else { + if (channel_mode == 1) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LS; + open->dev_channel_mapping[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LS; + open->dev_channel_mapping[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 7) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[4] = PCM_CHANNEL_LB; + open->dev_channel_mapping[5] = PCM_CHANNEL_RB; + open->dev_channel_mapping[6] = PCM_CHANNEL_CS; + } else if (channel_mode == 8) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + open->dev_channel_mapping[6] = PCM_CHANNEL_LB; + open->dev_channel_mapping[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + goto inval_ch_mod; + } + } + +non_mch_path: +inval_ch_mod: + return rc; +} + +int adm_open(int port_id, int path, int rate, int channel_mode, int topology, + int perf_mode, uint16_t bit_width, int app_type, int acdb_id) +{ + struct adm_cmd_device_open_v5 open; + int ret = 0; + int port_idx, copp_idx, flags; + int tmp_port = q6audio_get_port_id(port_id); + + pr_debug("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n", + __func__, port_id, path, rate, channel_mode, perf_mode, + topology); + + /* For DTS EAGLE only, force 24 bit */ + if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) && + (perf_mode == LEGACY_PCM_MODE)) { + bit_width = 24; + pr_debug("%s: Force open adm in 24-bit for DTS HPX topology 0x%x\n", + __func__, topology); + } + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + return -ENODEV; + } + rtac_set_adm_handle(this_adm.apr); + } + + if (perf_mode == ULL_POST_PROCESSING_PCM_MODE) { + flags = ADM_ULL_POST_PROCESSING_DEVICE_SESSION; + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) || + (topology == DS2_ADM_COPP_TOPOLOGY_ID) || + (topology == SRS_TRUMEDIA_TOPOLOGY_ID) || + (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) + topology = DEFAULT_COPP_TOPOLOGY; + } else if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { + flags = ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION; + topology = NULL_COPP_TOPOLOGY; + rate = ULL_SUPPORTED_SAMPLE_RATE; + bit_width = ULL_SUPPORTED_BITS_PER_SAMPLE; + } else if (perf_mode == LOW_LATENCY_PCM_MODE) { + flags = ADM_LOW_LATENCY_DEVICE_SESSION; + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) || + (topology == DS2_ADM_COPP_TOPOLOGY_ID) || + (topology == SRS_TRUMEDIA_TOPOLOGY_ID) || + (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) + topology = DEFAULT_COPP_TOPOLOGY; + } else { + if (path == ADM_PATH_COMPRESSED_RX) + flags = 0; + else + flags = ADM_LEGACY_DEVICE_SESSION; + } + + if ((topology == VPM_TX_SM_ECNS_COPP_TOPOLOGY) || + (topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) || + (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY)) + rate = 16000; + + copp_idx = adm_get_idx_if_copp_exists(port_idx, topology, perf_mode, + rate, bit_width, app_type); + if (copp_idx < 0) { + copp_idx = adm_get_next_available_copp(port_idx); + if (copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: exceeded copp id %d\n", + __func__, copp_idx); + return -EINVAL; + } + atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.topology[port_idx][copp_idx], + topology); + atomic_set(&this_adm.copp.mode[port_idx][copp_idx], + perf_mode); + atomic_set(&this_adm.copp.rate[port_idx][copp_idx], + rate); + atomic_set(&this_adm.copp.channels[port_idx][copp_idx], + channel_mode); + atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], + bit_width); + atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], + app_type); + atomic_set(&this_adm.copp.acdb_id[port_idx][copp_idx], + acdb_id); + set_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp.adm_status[port_idx][copp_idx]); + if (path != ADM_PATH_COMPRESSED_RX) + send_adm_custom_topology(); + } + + if (this_adm.copp.adm_delay[port_idx][copp_idx] && + perf_mode == LEGACY_PCM_MODE) { + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], + 1); + this_adm.copp.adm_delay[port_idx][copp_idx] = 0; + wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]); + } + + /* Create a COPP if port id are not enabled */ + if (atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]) == 0) { + pr_debug("%s: open ADM: port_idx: %d, copp_idx: %d\n", __func__, + port_idx, copp_idx); + if ((topology == SRS_TRUMEDIA_TOPOLOGY_ID) && + perf_mode == LEGACY_PCM_MODE) { + int res; + + atomic_set(&this_adm.mem_map_index, ADM_SRS_TRUMEDIA); + msm_dts_srs_tm_ion_memmap(&this_adm.outband_memmap); + res = adm_memory_map_regions(&this_adm.outband_memmap.paddr, 0, + (uint32_t *)&this_adm.outband_memmap.size, 1); + if (res < 0) { + pr_err("%s: SRS adm_memory_map_regions failed ! addr = 0x%pK, size = %d\n", + __func__, (void *)this_adm.outband_memmap.paddr, + (uint32_t)this_adm.outband_memmap.size); + } + } + if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) && + (perf_mode == LEGACY_PCM_MODE)) { + int res = 0; + + atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE); + msm_dts_ion_memmap(&this_adm.outband_memmap); + res = adm_memory_map_regions( + &this_adm.outband_memmap.paddr, + 0, + (uint32_t *)&this_adm.outband_memmap.size, + 1); + if (res < 0) + pr_err("%s: DTS_EAGLE mmap did not work!", + __func__); + } + open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + open.hdr.pkt_size = sizeof(open); + open.hdr.src_svc = APR_SVC_ADM; + open.hdr.src_domain = APR_DOMAIN_APPS; + open.hdr.src_port = tmp_port; + open.hdr.dest_svc = APR_SVC_ADM; + open.hdr.dest_domain = APR_DOMAIN_ADSP; + open.hdr.dest_port = tmp_port; + open.hdr.token = port_idx << 16 | copp_idx; + open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5; + open.flags = flags; + open.mode_of_operation = path; + open.endpoint_id_1 = tmp_port; + + if (this_adm.ec_ref_rx == -1) { + open.endpoint_id_2 = 0xFFFF; + } else if (this_adm.ec_ref_rx && (path != 1)) { + open.endpoint_id_2 = this_adm.ec_ref_rx; + this_adm.ec_ref_rx = -1; + } + + open.topology_id = topology; + + open.dev_num_channel = channel_mode & 0x00FF; + open.bit_width = bit_width; + WARN_ON((perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) && + (rate != ULL_SUPPORTED_SAMPLE_RATE)); + open.sample_rate = rate; + + ret = adm_arrange_mch_map(&open, path, channel_mode); + + if (ret) + return ret; + + pr_debug("%s: port_id=0x%x rate=%d topology_id=0x%X\n", + __func__, open.endpoint_id_1, open.sample_rate, + open.topology_id); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open); + if (ret < 0) { + pr_err("%s: port_id: 0x%x for[0x%x] failed %d\n", + __func__, tmp_port, port_id, ret); + return -EINVAL; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM open timedout for port_id: 0x%x for [0x%x]\n", + __func__, tmp_port, port_id); + return -EINVAL; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + return adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + } + } + atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]); + return copp_idx; +} + + +void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate) +{ + struct audproc_mfc_output_media_fmt mfc_cfg; + struct adm_cmd_device_open_v5 open; + int port_idx; + int sz = 0; + int rc = 0; + int i = 0; + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + goto fail_cmd; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + goto fail_cmd; + } + + sz = sizeof(struct audproc_mfc_output_media_fmt); + + mfc_cfg.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mfc_cfg.params.hdr.pkt_size = sz; + mfc_cfg.params.hdr.src_svc = APR_SVC_ADM; + mfc_cfg.params.hdr.src_domain = APR_DOMAIN_APPS; + mfc_cfg.params.hdr.src_port = port_id; + mfc_cfg.params.hdr.dest_svc = APR_SVC_ADM; + mfc_cfg.params.hdr.dest_domain = APR_DOMAIN_ADSP; + mfc_cfg.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + mfc_cfg.params.hdr.token = port_idx << 16 | copp_idx; + mfc_cfg.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + mfc_cfg.params.payload_addr_lsw = 0; + mfc_cfg.params.payload_addr_msw = 0; + mfc_cfg.params.mem_map_handle = 0; + mfc_cfg.params.payload_size = sizeof(mfc_cfg) - + sizeof(mfc_cfg.params); + mfc_cfg.data.module_id = AUDPROC_MODULE_ID_MFC; + mfc_cfg.data.param_id = + AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + mfc_cfg.data.param_size = mfc_cfg.params.payload_size - + sizeof(mfc_cfg.data); + mfc_cfg.data.reserved = 0; + mfc_cfg.sampling_rate = dst_sample_rate; + mfc_cfg.bits_per_sample = + atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); + open.dev_num_channel = mfc_cfg.num_channels = + atomic_read(&this_adm.copp.channels[port_idx][copp_idx]); + + rc = adm_arrange_mch_map(&open, ADM_PATH_PLAYBACK, + mfc_cfg.num_channels); + if (rc < 0) { + pr_err("%s: unable to get channal map\n", __func__); + goto fail_cmd; + } + + for (i = 0; i < mfc_cfg.num_channels; i++) + mfc_cfg.channel_type[i] = + (uint16_t) open.dev_channel_mapping[i]; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + pr_debug("%s: mfc config: port_idx %d copp_idx %d copp SR %d copp BW %d copp chan %d o/p SR %d\n", + __func__, port_idx, copp_idx, + atomic_read(&this_adm.copp.rate[port_idx][copp_idx]), + mfc_cfg.bits_per_sample, mfc_cfg.num_channels, + mfc_cfg.sampling_rate); + + rc = apr_send_pkt(this_adm.apr, (uint32_t *)&mfc_cfg); + + if (rc < 0) { + pr_err("%s: port_id: for[0x%x] failed %d\n", + __func__, port_id, rc); + goto fail_cmd; + } + /* Wait for the callback with copp id */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: mfc_cfg Set params timed out for port_id: for [0x%x]\n", + __func__, port_id); + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return; +} + + +int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode) +{ + struct adm_cmd_matrix_map_routings_v5 *route; + struct adm_session_map_node_v5 *node; + uint16_t *copps_list; + int cmd_size = 0; + int ret = 0, i = 0; + void *payload = NULL; + void *matrix_map = NULL; + int port_idx, copp_idx; + + /* Assumes port_ids have already been validated during adm_open */ + cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) + + sizeof(struct adm_session_map_node_v5) + + (sizeof(uint32_t) * payload_map.num_copps)); + matrix_map = kzalloc(cmd_size, GFP_KERNEL); + if (matrix_map == NULL) { + pr_err("%s: Mem alloc failed\n", __func__); + ret = -EINVAL; + return ret; + } + route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map; + + route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + route->hdr.pkt_size = cmd_size; + route->hdr.src_svc = 0; + route->hdr.src_domain = APR_DOMAIN_APPS; + route->hdr.src_port = 0; /* Ignored */; + route->hdr.dest_svc = APR_SVC_ADM; + route->hdr.dest_domain = APR_DOMAIN_ADSP; + route->hdr.dest_port = 0; /* Ignored */; + route->hdr.token = 0; + if (path == ADM_PATH_COMPRESSED_RX) { + pr_debug("%s: ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5 0x%x\n", + __func__, ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5); + route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + } else { + pr_debug("%s: DM_CMD_MATRIX_MAP_ROUTINGS_V5 0x%x\n", + __func__, ADM_CMD_MATRIX_MAP_ROUTINGS_V5); + route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + } + route->num_sessions = 1; + + switch (path) { + case ADM_PATH_PLAYBACK: + route->matrix_id = ADM_MATRIX_ID_AUDIO_RX; + break; + case ADM_PATH_LIVE_REC: + case ADM_PATH_NONLIVE_REC: + route->matrix_id = ADM_MATRIX_ID_AUDIO_TX; + break; + case ADM_PATH_COMPRESSED_RX: + route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_RX; + break; + default: + pr_err("%s: Wrong path set[%d]\n", __func__, path); + break; + } + payload = ((u8 *)matrix_map + + sizeof(struct adm_cmd_matrix_map_routings_v5)); + node = (struct adm_session_map_node_v5 *)payload; + + node->session_id = payload_map.session_id; + node->num_copps = payload_map.num_copps; + payload = (u8 *)node + sizeof(struct adm_session_map_node_v5); + copps_list = (uint16_t *)payload; + for (i = 0; i < payload_map.num_copps; i++) { + port_idx = + adm_validate_and_get_port_index(payload_map.port_id[i]); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, + payload_map.port_id[i]); + ret = -EINVAL; + goto fail_cmd; + } + copp_idx = payload_map.copp_idx[i]; + copps_list[i] = atomic_read(&this_adm.copp.id[port_idx] + [copp_idx]); + } + atomic_set(&this_adm.matrix_map_stat, -1); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map); + if (ret < 0) { + pr_err("%s: routing for syream %d failed ret %d\n", + __func__, payload_map.session_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_adm.matrix_map_wait, + atomic_read(&this_adm.matrix_map_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: routing for syream %d failed\n", __func__, + payload_map.session_id); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.matrix_map_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_adm.matrix_map_stat))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.matrix_map_stat)); + goto fail_cmd; + } + + if ((perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) && + (path != ADM_PATH_COMPRESSED_RX)) { + for (i = 0; i < payload_map.num_copps; i++) { + port_idx = afe_get_port_index(payload_map.port_id[i]); + copp_idx = payload_map.copp_idx[i]; + if (port_idx < 0 || copp_idx < 0 || + (copp_idx > MAX_COPPS_PER_PORT - 1)) { + pr_err("%s: Invalid idx port_idx %d copp_idx %d\n", + __func__, port_idx, copp_idx); + continue; + } + if (atomic_read( + &this_adm.copp.topology[port_idx][copp_idx]) == + ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) + continue; + rtac_add_adm_device(payload_map.port_id[i], + atomic_read(&this_adm.copp.id + [port_idx][copp_idx]), + get_cal_path(path), + payload_map.session_id, + payload_map.app_type, + payload_map.acdb_dev_id); + + if (!test_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp.adm_status[port_idx] + [copp_idx])) { + pr_debug("%s: adm copp[0x%x][%d] already sent", + __func__, port_idx, copp_idx); + continue; + } + send_adm_cal(payload_map.port_id[i], copp_idx, + get_cal_path(path), perf_mode, + payload_map.app_type, + payload_map.acdb_dev_id, + payload_map.sample_rate); + /* ADM COPP calibration is already sent */ + clear_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp. + adm_status[port_idx][copp_idx]); + pr_debug("%s: copp_id: %d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx] + [copp_idx])); + } + } + +fail_cmd: + kfree(matrix_map); + return ret; +} + +void adm_ec_ref_rx_id(int port_id) +{ + this_adm.ec_ref_rx = port_id; + pr_debug("%s: ec_ref_rx:%d", __func__, this_adm.ec_ref_rx); +} + +int adm_close(int port_id, int perf_mode, int copp_idx) +{ + struct apr_hdr close; + + int ret = 0, port_idx; + int copp_id = RESET_COPP_ID; + + pr_debug("%s: port_id=0x%x perf_mode: %d copp_idx: %d\n", __func__, + port_id, perf_mode, copp_idx); + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", + __func__, port_id); + return -EINVAL; + } + + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp idx: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (this_adm.copp.adm_delay[port_idx][copp_idx] && perf_mode + == LEGACY_PCM_MODE) { + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], + 1); + this_adm.copp.adm_delay[port_idx][copp_idx] = 0; + wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]); + } + + atomic_dec(&this_adm.copp.cnt[port_idx][copp_idx]); + if (!(atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]))) { + copp_id = adm_get_copp_id(port_idx, copp_idx); + pr_debug("%s: Closing ADM port_idx:%d copp_idx:%d copp_id:0x%x\n", + __func__, port_idx, copp_idx, copp_id); + if ((!perf_mode) && (this_adm.outband_memmap.paddr != 0) && + (atomic_read(&this_adm.copp.topology[port_idx][copp_idx]) == + SRS_TRUMEDIA_TOPOLOGY_ID)) { + atomic_set(&this_adm.mem_map_index, + ADM_SRS_TRUMEDIA); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: adm mem unmmap err %d", + __func__, ret); + } else { + atomic_set(&this_adm.mem_map_handles + [ADM_SRS_TRUMEDIA], 0); + } + } + + if ((perf_mode == LEGACY_PCM_MODE) && + (this_adm.outband_memmap.paddr != 0) && + (atomic_read( + &this_adm.copp.topology[port_idx][copp_idx]) == + ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) { + atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: adm mem unmmap err %d", + __func__, ret); + } else { + atomic_set(&this_adm.mem_map_handles + [ADM_DTS_EAGLE], 0); + } + } + + if ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) && + this_adm.sourceTrackingData.memmap.paddr) { + atomic_set(&this_adm.mem_map_index, + ADM_MEM_MAP_INDEX_SOURCE_TRACKING); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: adm mem unmmap err %d", + __func__, ret); + } + msm_audio_ion_free( + this_adm.sourceTrackingData.ion_client, + this_adm.sourceTrackingData.ion_handle); + this_adm.sourceTrackingData.ion_client = NULL; + this_adm.sourceTrackingData.ion_handle = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles[ + ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0); + } + + close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + close.pkt_size = sizeof(close); + close.src_svc = APR_SVC_ADM; + close.src_domain = APR_DOMAIN_APPS; + close.src_port = port_id; + close.dest_svc = APR_SVC_ADM; + close.dest_domain = APR_DOMAIN_ADSP; + close.dest_port = copp_id; + close.token = port_idx << 16 | copp_idx; + close.opcode = ADM_CMD_DEVICE_CLOSE_V5; + + atomic_set(&this_adm.copp.id[port_idx][copp_idx], + RESET_COPP_ID); + atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.topology[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.mode[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + atomic_set(&this_adm.copp.rate[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.channels[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], 0); + + clear_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp.adm_status[port_idx][copp_idx]); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close); + if (ret < 0) { + pr_err("%s: ADM close failed %d\n", __func__, ret); + return -EINVAL; + } + + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM cmd Route timedout for port 0x%x\n", + __func__, port_id); + return -EINVAL; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + return adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + } + } + + if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) { + pr_debug("%s: remove adm device from rtac\n", __func__); + rtac_remove_adm_device(port_id, copp_id); + } + return 0; +} + +int send_rtac_audvol_cal(void) +{ + int ret = 0; + int ret2 = 0; + int i = 0; + int copp_idx, port_idx, acdb_id, app_id, path; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + struct rtac_adm rtac_adm_data; + + mutex_lock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock); + + cal_block = cal_utils_get_only_cal_block( + this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]); + if (cal_block == NULL) { + pr_err("%s: can't find cal block!\n", __func__); + goto unlock; + } + + audvol_cal_info = cal_block->cal_info; + if (audvol_cal_info == NULL) { + pr_err("%s: audvol_cal_info is NULL!\n", __func__); + goto unlock; + } + + get_rtac_adm_data(&rtac_adm_data); + for (; i < rtac_adm_data.num_of_dev; i++) { + + acdb_id = rtac_adm_data.device[i].acdb_dev_id; + if (acdb_id == 0) + acdb_id = audvol_cal_info->acdb_id; + + app_id = rtac_adm_data.device[i].app_type; + if (app_id == 0) + app_id = audvol_cal_info->app_type; + + path = afe_get_port_type(rtac_adm_data.device[i].afe_port); + if ((acdb_id == audvol_cal_info->acdb_id) && + (app_id == audvol_cal_info->app_type) && + (path == audvol_cal_info->path)) { + + if (adm_get_indexes_from_copp_id(rtac_adm_data. + device[i].copp, &copp_idx, &port_idx) != 0) { + pr_debug("%s: Copp Id %d is not active\n", + __func__, + rtac_adm_data.device[i].copp); + continue; + } + + ret2 = adm_remap_and_send_cal_block(ADM_RTAC_AUDVOL_CAL, + rtac_adm_data.device[i].afe_port, + copp_idx, cal_block, + atomic_read(&this_adm.copp. + mode[port_idx][copp_idx]), + audvol_cal_info->app_type, + audvol_cal_info->acdb_id, + atomic_read(&this_adm.copp. + rate[port_idx][copp_idx])); + if (ret2 < 0) { + pr_debug("%s: remap and send failed for copp Id %d, acdb id %d, app type %d, path %d\n", + __func__, rtac_adm_data.device[i].copp, + audvol_cal_info->acdb_id, + audvol_cal_info->app_type, + audvol_cal_info->path); + ret = ret2; + } + } + } +unlock: + mutex_unlock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock); + return ret; +} + +int adm_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + result = -EINVAL; + goto done; + } + + /* valid port ID needed for callback use primary I2S */ + atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL); + result = adm_memory_map_regions(&cal_block->cal_data.paddr, 0, + &cal_block->map_data.map_size, 1); + if (result < 0) { + pr_err("%s: RTAC mmap did not work! size = %d result %d\n", + __func__, + cal_block->map_data.map_size, result); + pr_debug("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + + cal_block->map_data.map_handle = atomic_read( + &this_adm.mem_map_handles[ADM_RTAC_APR_CAL]); +done: + return result; +} + +int adm_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (mem_map_handle == NULL) { + pr_debug("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle != atomic_read( + &this_adm.mem_map_handles[ADM_RTAC_APR_CAL])) { + pr_err("%s: Map handles do not match! Unmapping RTAC, RTAC map 0x%x, ADM map 0x%x\n", + __func__, *mem_map_handle, atomic_read( + &this_adm.mem_map_handles[ADM_RTAC_APR_CAL])); + + /* if mismatch use handle passed in to unmap */ + atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL], + *mem_map_handle); + } + + /* valid port ID needed for callback use primary I2S */ + atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL); + result = adm_memory_unmap_regions(); + if (result < 0) { + pr_debug("%s: adm_memory_unmap_regions failed, error %d\n", + __func__, result); + } else { + atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL], 0); + *mem_map_handle = 0; + } +done: + return result; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case ADM_AUDPROC_CAL_TYPE: + ret = ADM_AUDPROC_CAL; + break; + case ADM_AUDVOL_CAL_TYPE: + ret = ADM_AUDVOL_CAL; + break; + case ADM_CUST_TOPOLOGY_CAL_TYPE: + ret = ADM_CUSTOM_TOP_CAL; + break; + case ADM_RTAC_INFO_CAL_TYPE: + ret = ADM_RTAC_INFO_CAL; + break; + case ADM_RTAC_APR_CAL_TYPE: + ret = ADM_RTAC_APR_CAL; + break; + case ADM_RTAC_AUDVOL_CAL_TYPE: + ret = ADM_RTAC_AUDVOL_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int adm_alloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + this_adm.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int adm_dealloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + this_adm.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int adm_set_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + this_adm.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == ADM_CUSTOM_TOP_CAL) { + mutex_lock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock); + this_adm.set_custom_topology = 1; + mutex_unlock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock); + } else if (cal_index == ADM_RTAC_AUDVOL_CAL) { + send_rtac_audvol_cal(); + } +done: + return ret; +} + +static int adm_map_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + atomic_set(&this_adm.mem_map_index, cal_index); + ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0, + (uint32_t *)&cal_block->map_data.map_size, 1); + if (ret < 0) { + pr_err("%s: map did not work! cal_type %i ret %d\n", + __func__, cal_index, ret); + ret = -ENODEV; + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_adm. + mem_map_handles[cal_index]); +done: + return ret; +} + +static int adm_unmap_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", + __func__); + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_err("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + atomic_set(&this_adm.mem_map_handles[cal_index], + cal_block->map_data.q6map_handle); + atomic_set(&this_adm.mem_map_index, cal_index); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: unmap did not work! cal_type %i ret %d\n", + __func__, cal_index, ret); + ret = -ENODEV; + goto done; + } + cal_block->map_data.q6map_handle = 0; +done: + return ret; +} + +static void adm_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data); +} + +static int adm_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{ADM_CUST_TOPOLOGY_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_AUDPROC_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_AUDVOL_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_RTAC_INFO_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ADM_RTAC_APR_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{DTS_EAGLE_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{SRS_TRUMEDIA_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ADM_RTAC_AUDVOL_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type! ret %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + return ret; +err: + adm_delete_cal_data(); + return ret; +} + +int adm_set_volume(int port_id, int copp_idx, int volume) +{ + struct audproc_volume_ctrl_master_gain audproc_vol; + int sz = 0; + int rc = 0; + int port_idx; + + pr_debug("%s: port_id %d, volume %d\n", __func__, port_id, volume); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + sz = sizeof(struct audproc_volume_ctrl_master_gain); + audproc_vol.params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + audproc_vol.params.hdr.pkt_size = sz; + audproc_vol.params.hdr.src_svc = APR_SVC_ADM; + audproc_vol.params.hdr.src_domain = APR_DOMAIN_APPS; + audproc_vol.params.hdr.src_port = port_id; + audproc_vol.params.hdr.dest_svc = APR_SVC_ADM; + audproc_vol.params.hdr.dest_domain = APR_DOMAIN_ADSP; + audproc_vol.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + audproc_vol.params.hdr.token = port_idx << 16 | copp_idx; + audproc_vol.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + audproc_vol.params.payload_addr_lsw = 0; + audproc_vol.params.payload_addr_msw = 0; + audproc_vol.params.mem_map_handle = 0; + audproc_vol.params.payload_size = sizeof(audproc_vol) - + sizeof(audproc_vol.params); + audproc_vol.data.module_id = AUDPROC_MODULE_ID_VOL_CTRL; + audproc_vol.data.param_id = AUDPROC_PARAM_ID_VOL_CTRL_MASTER_GAIN; + audproc_vol.data.param_size = audproc_vol.params.payload_size - + sizeof(audproc_vol.data); + audproc_vol.data.reserved = 0; + audproc_vol.master_gain = volume; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)&audproc_vol); + if (rc < 0) { + pr_err("%s: Set params failed port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Vol cntrl Set params timed out port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +int adm_set_softvolume(int port_id, int copp_idx, + struct audproc_softvolume_params *softvol_param) +{ + struct audproc_soft_step_volume_params audproc_softvol; + int sz = 0; + int rc = 0; + int port_idx; + + pr_debug("%s: period %d step %d curve %d\n", __func__, + softvol_param->period, softvol_param->step, + softvol_param->rampingcurve); + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + sz = sizeof(struct audproc_soft_step_volume_params); + + audproc_softvol.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + audproc_softvol.params.hdr.pkt_size = sz; + audproc_softvol.params.hdr.src_svc = APR_SVC_ADM; + audproc_softvol.params.hdr.src_domain = APR_DOMAIN_APPS; + audproc_softvol.params.hdr.src_port = port_id; + audproc_softvol.params.hdr.dest_svc = APR_SVC_ADM; + audproc_softvol.params.hdr.dest_domain = APR_DOMAIN_ADSP; + audproc_softvol.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + audproc_softvol.params.hdr.token = port_idx << 16 | copp_idx; + audproc_softvol.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + audproc_softvol.params.payload_addr_lsw = 0; + audproc_softvol.params.payload_addr_msw = 0; + audproc_softvol.params.mem_map_handle = 0; + audproc_softvol.params.payload_size = sizeof(audproc_softvol) - + sizeof(audproc_softvol.params); + audproc_softvol.data.module_id = AUDPROC_MODULE_ID_VOL_CTRL; + audproc_softvol.data.param_id = + AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS; + audproc_softvol.data.param_size = audproc_softvol.params.payload_size - + sizeof(audproc_softvol.data); + audproc_softvol.data.reserved = 0; + audproc_softvol.period = softvol_param->period; + audproc_softvol.step = softvol_param->step; + audproc_softvol.ramping_curve = softvol_param->rampingcurve; + + pr_debug("%s: period %d, step %d, curve %d\n", __func__, + audproc_softvol.period, audproc_softvol.step, + audproc_softvol.ramping_curve); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)&audproc_softvol); + if (rc < 0) { + pr_err("%s: Set params failed port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Soft volume Set params timed out port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +int adm_param_enable(int port_id, int copp_idx, int module_id, int enable) +{ + struct audproc_enable_param_t adm_mod_enable; + int sz = 0; + int rc = 0; + int port_idx; + + pr_debug("%s port_id %d, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + sz = sizeof(struct audproc_enable_param_t); + + adm_mod_enable.pp_params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_mod_enable.pp_params.hdr.pkt_size = sz; + adm_mod_enable.pp_params.hdr.src_svc = APR_SVC_ADM; + adm_mod_enable.pp_params.hdr.src_domain = APR_DOMAIN_APPS; + adm_mod_enable.pp_params.hdr.src_port = port_id; + adm_mod_enable.pp_params.hdr.dest_svc = APR_SVC_ADM; + adm_mod_enable.pp_params.hdr.dest_domain = APR_DOMAIN_ADSP; + adm_mod_enable.pp_params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_mod_enable.pp_params.hdr.token = port_idx << 16 | copp_idx; + adm_mod_enable.pp_params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + adm_mod_enable.pp_params.payload_addr_lsw = 0; + adm_mod_enable.pp_params.payload_addr_msw = 0; + adm_mod_enable.pp_params.mem_map_handle = 0; + adm_mod_enable.pp_params.payload_size = sizeof(adm_mod_enable) - + sizeof(adm_mod_enable.pp_params) + + sizeof(adm_mod_enable.pp_params.params); + adm_mod_enable.pp_params.params.module_id = module_id; + adm_mod_enable.pp_params.params.param_id = AUDPROC_PARAM_ID_ENABLE; + adm_mod_enable.pp_params.params.param_size = + adm_mod_enable.pp_params.payload_size - + sizeof(adm_mod_enable.pp_params.params); + adm_mod_enable.pp_params.params.reserved = 0; + adm_mod_enable.enable = enable; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + rc = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_mod_enable); + if (rc < 0) { + pr_err("%s: Set params failed port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: module %x enable %d timed out on port = %#x\n", + __func__, module_id, enable, port_id); + rc = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; + +} + +int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode, + int cal_type, char *params, int size) +{ + + struct adm_cmd_set_pp_params_v5 *adm_params = NULL; + int sz, rc = 0; + int port_idx; + + pr_debug("%s:port_id %d, path %d, perf_mode %d, cal_type %d, size %d\n", + __func__, port_id, path, perf_mode, cal_type, size); + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + rc = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + /* Maps audio_dev_ctrl path definition to ACDB definition */ + if (get_cal_path(path) != RX_DEVICE) { + pr_err("%s: acdb_path %d\n", __func__, path); + rc = -EINVAL; + goto end; + } + + sz = sizeof(struct adm_cmd_set_pp_params_v5) + size; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed", __func__); + rc = -ENOMEM; + goto end; + } + + memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)), + params, size); + + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + /* payload address and mmap handle initialized to zero by kzalloc */ + adm_params->payload_size = size; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto end; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = %#x\n", + __func__, port_id); + rc = -EINVAL; + goto end; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto end; + } + rc = 0; + +end: + kfree(adm_params); + return rc; +} + +/* + * adm_update_wait_parameters must be called with routing driver locks. + * adm_reset_wait_parameters must be called with routing driver locks. + * set and reset parmeters are separated to make sure it is always called + * under routing driver lock. + * adm_wait_timeout is to block until timeout or interrupted. Timeout is + * not a an error. + */ +int adm_set_wait_parameters(int port_id, int copp_idx) +{ + + int ret = 0, port_idx; + + pr_debug("%s: port_id 0x%x, copp_idx %d\n", __func__, port_id, + copp_idx); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + this_adm.copp.adm_delay[port_idx][copp_idx] = 1; + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 0); + +end: + return ret; + +} + +int adm_reset_wait_parameters(int port_id, int copp_idx) +{ + int ret = 0, port_idx; + + pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id, + copp_idx); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 1); + this_adm.copp.adm_delay[port_idx][copp_idx] = 0; + +end: + return ret; +} + +int adm_wait_timeout(int port_id, int copp_idx, int wait_time) +{ + int ret = 0, port_idx; + + pr_debug("%s: port_id 0x%x, copp_idx %d, wait_time %d\n", __func__, + port_id, copp_idx, wait_time); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + ret = wait_event_timeout( + this_adm.copp.adm_delay_wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.adm_delay_stat[port_idx][copp_idx]), + msecs_to_jiffies(wait_time)); + pr_debug("%s: return %d\n", __func__, ret); + if (ret != 0) + ret = -EINTR; +end: + pr_debug("%s: return %d--\n", __func__, ret); + return ret; +} + +int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode, + int cal_index, char *params, int *size) +{ + int rc = 0; + struct cal_block_data *cal_block = NULL; + int app_type, acdb_id, port_idx, sample_rate; + + if (this_adm.cal_data[cal_index] == NULL) { + pr_debug("%s: cal_index %d not allocated!\n", + __func__, cal_index); + goto end; + } + + if (get_cal_path(path) != RX_DEVICE) { + pr_debug("%s: Invalid path to store calibration %d\n", + __func__, path); + rc = -EINVAL; + goto end; + } + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + rc = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + acdb_id = atomic_read(&this_adm.copp.acdb_id[port_idx][copp_idx]); + app_type = atomic_read(&this_adm.copp.app_type[port_idx][copp_idx]); + sample_rate = atomic_read(&this_adm.copp.rate[port_idx][copp_idx]); + + mutex_lock(&this_adm.cal_data[cal_index]->lock); + cal_block = adm_find_cal(cal_index, get_cal_path(path), app_type, + acdb_id, sample_rate); + if (cal_block == NULL) + goto unlock; + + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: No ADM cal send for port_id = 0x%x!\n", + __func__, port_id); + rc = -EINVAL; + goto unlock; + } + + if (cal_index == ADM_AUDPROC_CAL) { + if (cal_block->cal_data.size > AUD_PROC_BLOCK_SIZE) { + pr_err("%s:audproc:invalid size exp/actual[%zd, %d]\n", + __func__, cal_block->cal_data.size, *size); + rc = -ENOMEM; + goto unlock; + } + } else if (cal_index == ADM_AUDVOL_CAL) { + if (cal_block->cal_data.size > AUD_VOL_BLOCK_SIZE) { + pr_err("%s:aud_vol:invalid size exp/actual[%zd, %d]\n", + __func__, cal_block->cal_data.size, *size); + rc = -ENOMEM; + goto unlock; + } + } else { + pr_debug("%s: Not valid calibration for dolby topolgy\n", + __func__); + rc = -EINVAL; + goto unlock; + } + memcpy(params, cal_block->cal_data.kvaddr, cal_block->cal_data.size); + *size = cal_block->cal_data.size; + + pr_debug("%s:port_id %d, copp_idx %d, path %d", + __func__, port_id, copp_idx, path); + pr_debug("perf_mode %d, cal_type %d, size %d\n", + perf_mode, cal_index, *size); + +unlock: + mutex_unlock(&this_adm.cal_data[cal_index]->lock); +end: + return rc; +} + +int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on) +{ + struct adm_set_compressed_device_mute mute_params; + int ret = 0; + int port_idx; + + pr_debug("%s port_id: 0x%x, copp_idx %d, mute_on: %d\n", + __func__, port_id, copp_idx, mute_on); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_id %#x copp_idx %d\n", + __func__, port_id, copp_idx); + ret = -EINVAL; + goto end; + } + + mute_params.command.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mute_params.command.hdr.pkt_size = + sizeof(struct adm_set_compressed_device_mute); + mute_params.command.hdr.src_svc = APR_SVC_ADM; + mute_params.command.hdr.src_domain = APR_DOMAIN_APPS; + mute_params.command.hdr.src_port = port_id; + mute_params.command.hdr.dest_svc = APR_SVC_ADM; + mute_params.command.hdr.dest_domain = APR_DOMAIN_ADSP; + mute_params.command.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + mute_params.command.hdr.token = port_idx << 16 | copp_idx; + mute_params.command.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + mute_params.command.payload_addr_lsw = 0; + mute_params.command.payload_addr_msw = 0; + mute_params.command.mem_map_handle = 0; + mute_params.command.payload_size = sizeof(mute_params) - + sizeof(mute_params.command); + mute_params.params.module_id = AUDPROC_MODULE_ID_COMPRESSED_MUTE; + mute_params.params.param_id = AUDPROC_PARAM_ID_COMPRESSED_MUTE; + mute_params.params.param_size = mute_params.command.payload_size - + sizeof(mute_params.params); + mute_params.params.reserved = 0; + mute_params.mute_on = mute_on; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&mute_params); + if (ret < 0) { + pr_err("%s: device mute for port %d copp %d failed, ret %d\n", + __func__, port_id, copp_idx, ret); + ret = -EINVAL; + goto end; + } + + /* Wait for the callback */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: send device mute for port %d copp %d failed\n", + __func__, port_id, copp_idx); + ret = -EINVAL; + goto end; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto end; + } + ret = 0; +end: + return ret; +} + +int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency) +{ + struct adm_set_compressed_device_latency latency_params; + int port_idx; + int ret = 0; + + pr_debug("%s port_id: 0x%x, copp_idx %d latency: %d\n", __func__, + port_id, copp_idx, latency); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_id %#x copp_idx %d\n", + __func__, port_id, copp_idx); + ret = -EINVAL; + goto end; + } + + latency_params.command.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + latency_params.command.hdr.pkt_size = + sizeof(struct adm_set_compressed_device_latency); + latency_params.command.hdr.src_svc = APR_SVC_ADM; + latency_params.command.hdr.src_domain = APR_DOMAIN_APPS; + latency_params.command.hdr.src_port = port_id; + latency_params.command.hdr.dest_svc = APR_SVC_ADM; + latency_params.command.hdr.dest_domain = APR_DOMAIN_ADSP; + latency_params.command.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + latency_params.command.hdr.token = port_idx << 16 | copp_idx; + latency_params.command.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + latency_params.command.payload_addr_lsw = 0; + latency_params.command.payload_addr_msw = 0; + latency_params.command.mem_map_handle = 0; + latency_params.command.payload_size = sizeof(latency_params) - + sizeof(latency_params.command); + latency_params.params.module_id = AUDPROC_MODULE_ID_COMPRESSED_LATENCY; + latency_params.params.param_id = AUDPROC_PARAM_ID_COMPRESSED_LATENCY; + latency_params.params.param_size = latency_params.command.payload_size - + sizeof(latency_params.params); + latency_params.params.reserved = 0; + latency_params.latency = latency; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&latency_params); + if (ret < 0) { + pr_err("%s: send device latency err %d for port %d copp %d\n", + __func__, port_id, copp_idx, ret); + ret = -EINVAL; + goto end; + } + + /* Wait for the callback */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: send device latency for port %d failed\n", __func__, + port_id); + ret = -EINVAL; + goto end; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto end; + } + ret = 0; +end: + return ret; +} + +int adm_set_sound_focus(int port_id, int copp_idx, + struct sound_focus_param soundFocusData) +{ + struct adm_set_fluence_soundfocus_param soundfocus_params; + int sz = 0; + int ret = 0; + int port_idx; + int i; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + + ret = -EINVAL; + goto done; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + + ret = -EINVAL; + goto done; + } + + sz = sizeof(struct adm_set_fluence_soundfocus_param); + soundfocus_params.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + soundfocus_params.params.hdr.pkt_size = sz; + soundfocus_params.params.hdr.src_svc = APR_SVC_ADM; + soundfocus_params.params.hdr.src_domain = APR_DOMAIN_APPS; + soundfocus_params.params.hdr.src_port = port_id; + soundfocus_params.params.hdr.dest_svc = APR_SVC_ADM; + soundfocus_params.params.hdr.dest_domain = APR_DOMAIN_ADSP; + soundfocus_params.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + soundfocus_params.params.hdr.token = port_idx << 16 | + ADM_CLIENT_ID_SOURCE_TRACKING << 8 | copp_idx; + soundfocus_params.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + soundfocus_params.params.payload_addr_lsw = 0; + soundfocus_params.params.payload_addr_msw = 0; + soundfocus_params.params.mem_map_handle = 0; + soundfocus_params.params.payload_size = sizeof(soundfocus_params) - + sizeof(soundfocus_params.params); + soundfocus_params.data.module_id = VOICEPROC_MODULE_ID_GENERIC_TX; + soundfocus_params.data.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS; + soundfocus_params.data.param_size = + soundfocus_params.params.payload_size - + sizeof(soundfocus_params.data); + soundfocus_params.data.reserved = 0; + + memset(&(soundfocus_params.soundfocus_data), 0xFF, + sizeof(struct adm_param_fluence_soundfocus_t)); + for (i = 0; i < MAX_SECTORS; i++) { + soundfocus_params.soundfocus_data.start_angles[i] = + soundFocusData.start_angle[i]; + soundfocus_params.soundfocus_data.enables[i] = + soundFocusData.enable[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData.start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData.enable[i]); + } + soundfocus_params.soundfocus_data.gain_step = + soundFocusData.gain_step; + pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step); + + soundfocus_params.soundfocus_data.reserved = 0; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&soundfocus_params); + if (ret < 0) { + pr_err("%s: Set params failed\n", __func__); + + ret = -EINVAL; + goto done; + } + /* Wait for the callback */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Set params timed out\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (this_adm.sourceTrackingData.apr_cmd_status != 0) { + pr_err("%s - set params returned error [%s]\n", + __func__, adsp_err_get_err_str( + this_adm.sourceTrackingData.apr_cmd_status)); + + ret = adsp_err_get_lnx_err_code( + this_adm.sourceTrackingData.apr_cmd_status); + goto done; + } + + ret = 0; + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +int adm_get_sound_focus(int port_id, int copp_idx, + struct sound_focus_param *soundFocusData) +{ + int ret = 0, i; + char *params_value; + uint32_t param_payload_len = sizeof(struct adm_param_data_v5) + + sizeof(struct adm_param_fluence_soundfocus_t); + struct adm_param_fluence_soundfocus_t *soundfocus_params; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + params_value = kzalloc(param_payload_len, GFP_KERNEL); + if (!params_value) { + ret = -ENOMEM; + goto done; + } + ret = adm_get_params_v2(port_id, copp_idx, + VOICEPROC_MODULE_ID_GENERIC_TX, + VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS, + param_payload_len, + params_value, + ADM_CLIENT_ID_SOURCE_TRACKING); + if (ret) { + pr_err("%s: get parameters failed ret:%d\n", __func__, ret); + + kfree(params_value); + ret = -EINVAL; + goto done; + } + + if (this_adm.sourceTrackingData.apr_cmd_status != 0) { + pr_err("%s - get params returned error [%s]\n", + __func__, adsp_err_get_err_str( + this_adm.sourceTrackingData.apr_cmd_status)); + + kfree(params_value); + ret = adsp_err_get_lnx_err_code( + this_adm.sourceTrackingData.apr_cmd_status); + goto done; + } + + soundfocus_params = (struct adm_param_fluence_soundfocus_t *) + params_value; + for (i = 0; i < MAX_SECTORS; i++) { + soundFocusData->start_angle[i] = + soundfocus_params->start_angles[i]; + soundFocusData->enable[i] = soundfocus_params->enables[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData->start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData->enable[i]); + } + soundFocusData->gain_step = soundfocus_params->gain_step; + pr_debug("%s: gain_step = %d\n", __func__, soundFocusData->gain_step); + + kfree(params_value); + +done: + pr_debug("%s: Exit, ret = %d\n", __func__, ret); + + return ret; +} + +static int adm_source_tracking_alloc_map_memory(void) +{ + int ret; + + pr_debug("%s: Enter\n", __func__); + + ret = msm_audio_ion_alloc("SOURCE_TRACKING", + &this_adm.sourceTrackingData.ion_client, + &this_adm.sourceTrackingData.ion_handle, + AUD_PROC_BLOCK_SIZE, + &this_adm.sourceTrackingData.memmap.paddr, + &this_adm.sourceTrackingData.memmap.size, + &this_adm.sourceTrackingData.memmap.kvaddr); + if (ret) { + pr_err("%s: failed to allocate memory\n", __func__); + + ret = -EINVAL; + goto done; + } + + atomic_set(&this_adm.mem_map_index, ADM_MEM_MAP_INDEX_SOURCE_TRACKING); + ret = adm_memory_map_regions(&this_adm.sourceTrackingData.memmap.paddr, + 0, + (uint32_t *)&this_adm.sourceTrackingData.memmap.size, + 1); + if (ret < 0) { + pr_err("%s: failed to map memory, paddr = 0x%pK, size = %d\n", + __func__, + (void *)this_adm.sourceTrackingData.memmap.paddr, + (uint32_t)this_adm.sourceTrackingData.memmap.size); + + msm_audio_ion_free(this_adm.sourceTrackingData.ion_client, + this_adm.sourceTrackingData.ion_handle); + this_adm.sourceTrackingData.ion_client = NULL; + this_adm.sourceTrackingData.ion_handle = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles + [ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0); + + ret = -EINVAL; + goto done; + } + ret = 0; + pr_debug("%s: paddr = 0x%pK, size = %d, mem_map_handle = 0x%x\n", + __func__, (void *)this_adm.sourceTrackingData.memmap.paddr, + (uint32_t)this_adm.sourceTrackingData.memmap.size, + atomic_read(&this_adm.mem_map_handles + [ADM_MEM_MAP_INDEX_SOURCE_TRACKING])); + +done: + pr_debug("%s: Exit, ret = %d\n", __func__, ret); + + return ret; +} + +int adm_get_source_tracking(int port_id, int copp_idx, + struct source_tracking_param *sourceTrackingData) +{ + struct adm_cmd_get_pp_params_v5 admp; + int p_idx, ret = 0, i; + struct adm_param_fluence_sourcetracking_t *source_tracking_params; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + if (!this_adm.sourceTrackingData.memmap.paddr) { + /* Allocate and map shared memory for out of band usage */ + ret = adm_source_tracking_alloc_map_memory(); + if (ret != 0) { + ret = -EINVAL; + goto done; + } + } + + port_id = afe_convert_virtual_to_portid(port_id); + p_idx = adm_validate_and_get_port_index(port_id); + if (p_idx < 0) { + pr_err("%s - invalid port index %i, port id %i, copp idx %i\n", + __func__, p_idx, port_id, copp_idx); + + ret = -EINVAL; + goto done; + } + + admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + admp.hdr.pkt_size = sizeof(admp); + admp.hdr.src_svc = APR_SVC_ADM; + admp.hdr.src_domain = APR_DOMAIN_APPS; + admp.hdr.src_port = port_id; + admp.hdr.dest_svc = APR_SVC_ADM; + admp.hdr.dest_domain = APR_DOMAIN_ADSP; + admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]); + admp.hdr.token = p_idx << 16 | ADM_CLIENT_ID_SOURCE_TRACKING << 8 | + copp_idx; + admp.hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5; + admp.data_payload_addr_lsw = + lower_32_bits(this_adm.sourceTrackingData.memmap.paddr); + admp.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + this_adm.sourceTrackingData.memmap.paddr); + admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[ + ADM_MEM_MAP_INDEX_SOURCE_TRACKING]); + admp.module_id = VOICEPROC_MODULE_ID_GENERIC_TX; + admp.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING; + admp.param_max_size = sizeof(struct adm_param_fluence_sourcetracking_t) + + sizeof(struct adm_param_data_v5); + admp.reserved = 0; + + atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp); + if (ret < 0) { + pr_err("%s - failed to get Source Tracking Params\n", + __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx], + atomic_read(&this_adm.copp.stat[p_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s - get params timed out\n", __func__); + + ret = -EINVAL; + goto done; + } else if (atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [p_idx][copp_idx])); + goto done; + } + + if (this_adm.sourceTrackingData.apr_cmd_status != 0) { + pr_err("%s - get params returned error [%s]\n", + __func__, adsp_err_get_err_str( + this_adm.sourceTrackingData.apr_cmd_status)); + + ret = adsp_err_get_lnx_err_code( + this_adm.sourceTrackingData.apr_cmd_status); + goto done; + } + + source_tracking_params = (struct adm_param_fluence_sourcetracking_t *) + (this_adm.sourceTrackingData.memmap.kvaddr + + sizeof(struct adm_param_data_v5)); + for (i = 0; i < MAX_SECTORS; i++) { + sourceTrackingData->vad[i] = source_tracking_params->vad[i]; + pr_debug("%s: vad[%d] = %d\n", + __func__, i, sourceTrackingData->vad[i]); + } + sourceTrackingData->doa_speech = source_tracking_params->doa_speech; + pr_debug("%s: doa_speech = %d\n", + __func__, sourceTrackingData->doa_speech); + + for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) { + sourceTrackingData->doa_noise[i] = + source_tracking_params->doa_noise[i]; + pr_debug("%s: doa_noise[%d] = %d\n", + __func__, i, sourceTrackingData->doa_noise[i]); + } + for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) { + sourceTrackingData->polar_activity[i] = + source_tracking_params->polar_activity[i]; + pr_debug("%s: polar_activity[%d] = %d\n", + __func__, i, sourceTrackingData->polar_activity[i]); + } + + ret = 0; + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int __init adm_init(void) +{ + int i = 0, j; + + this_adm.apr = NULL; + this_adm.ec_ref_rx = -1; + atomic_set(&this_adm.matrix_map_stat, 0); + init_waitqueue_head(&this_adm.matrix_map_wait); + atomic_set(&this_adm.adm_stat, 0); + init_waitqueue_head(&this_adm.adm_wait); + + for (i = 0; i < AFE_MAX_PORTS; i++) { + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + atomic_set(&this_adm.copp.id[i][j], RESET_COPP_ID); + atomic_set(&this_adm.copp.cnt[i][j], 0); + atomic_set(&this_adm.copp.topology[i][j], 0); + atomic_set(&this_adm.copp.mode[i][j], 0); + atomic_set(&this_adm.copp.stat[i][j], 0); + atomic_set(&this_adm.copp.rate[i][j], 0); + atomic_set(&this_adm.copp.channels[i][j], 0); + atomic_set(&this_adm.copp.bit_width[i][j], 0); + atomic_set(&this_adm.copp.app_type[i][j], 0); + atomic_set(&this_adm.copp.acdb_id[i][j], 0); + init_waitqueue_head(&this_adm.copp.wait[i][j]); + atomic_set(&this_adm.copp.adm_delay_stat[i][j], 0); + init_waitqueue_head( + &this_adm.copp.adm_delay_wait[i][j]); + atomic_set(&this_adm.copp.topology[i][j], 0); + this_adm.copp.adm_delay[i][j] = 0; + this_adm.copp.adm_status[i][j] = + ADM_STATUS_CALIBRATION_REQUIRED; + } + } + + if (adm_init_cal_data()) + pr_err("%s: could not init cal data!\n", __func__); + + this_adm.sourceTrackingData.ion_client = NULL; + this_adm.sourceTrackingData.ion_handle = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles[ADM_MEM_MAP_INDEX_SOURCE_TRACKING], + 0); + + return 0; +} + +static void __exit adm_exit(void) +{ + adm_delete_cal_data(); +} + +device_initcall(adm_init); +module_exit(adm_exit); diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c new file mode 100644 index 000000000000..43c39d3954ee --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -0,0 +1,6626 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include +#include +#include + +#define WAKELOCK_TIMEOUT 5000 +enum { + AFE_COMMON_RX_CAL = 0, + AFE_COMMON_TX_CAL, + AFE_AANC_CAL, + AFE_FB_SPKR_PROT_CAL, + AFE_HW_DELAY_CAL, + AFE_SIDETONE_CAL, + AFE_TOPOLOGY_CAL, + AFE_CUST_TOPOLOGY_CAL, + AFE_FB_SPKR_PROT_TH_VI_CAL, + AFE_FB_SPKR_PROT_EX_VI_CAL, + MAX_AFE_CAL_TYPES +}; + +enum fbsp_state { + FBSP_INCORRECT_OP_MODE, + FBSP_INACTIVE, + FBSP_WARMUP, + FBSP_IN_PROGRESS, + FBSP_SUCCESS, + FBSP_FAILED, + MAX_FBSP_STATE +}; + +static char fbsp_state[MAX_FBSP_STATE][50] = { + [FBSP_INCORRECT_OP_MODE] = "incorrect operation mode", + [FBSP_INACTIVE] = "port not started", + [FBSP_WARMUP] = "waiting for warmup", + [FBSP_IN_PROGRESS] = "in progress state", + [FBSP_SUCCESS] = "success", + [FBSP_FAILED] = "failed" +}; + +enum { + USE_CALIBRATED_R0TO, + USE_SAFE_R0TO +}; + +enum { + QUICK_CALIB_DISABLE, + QUICK_CALIB_ENABLE +}; + +enum { + Q6AFE_MSM_SPKR_PROCESSING = 0, + Q6AFE_MSM_SPKR_CALIBRATION, + Q6AFE_MSM_SPKR_FTM_MODE +}; + +struct wlock { + struct wakeup_source ws; +}; + +static struct wlock wl; + +struct afe_ctl { + void *apr; + atomic_t state; + atomic_t status; + wait_queue_head_t wait[AFE_MAX_PORTS]; + struct task_struct *task; + void (*tx_cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv); + void (*rx_cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv); + void *tx_private_data; + void *rx_private_data; + uint32_t mmap_handle; + + int topology[AFE_MAX_PORTS]; + struct cal_type_data *cal_data[MAX_AFE_CAL_TYPES]; + + atomic_t mem_map_cal_handles[MAX_AFE_CAL_TYPES]; + atomic_t mem_map_cal_index; + u32 afe_cal_mode[AFE_MAX_PORTS]; + + u16 dtmf_gen_rx_portid; + struct audio_cal_info_spk_prot_cfg prot_cfg; + struct afe_spkr_prot_calib_get_resp calib_data; + struct audio_cal_info_sp_th_vi_ftm_cfg th_ftm_cfg; + struct audio_cal_info_sp_ex_vi_ftm_cfg ex_ftm_cfg; + struct afe_sp_th_vi_get_param_resp th_vi_resp; + struct afe_sp_ex_vi_get_param_resp ex_vi_resp; + int vi_tx_port; + int vi_rx_port; + uint32_t afe_sample_rates[AFE_MAX_PORTS]; + struct aanc_data aanc_info; + struct mutex afe_cmd_lock; + int set_custom_topology; +}; + +static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX]; +static unsigned long afe_configured_cmd; + +static struct afe_ctl this_afe; + +#define TIMEOUT_MS 1000 +#define Q6AFE_MAX_VOLUME 0x3FFF + +static int pcm_afe_instance[2]; +static int proxy_afe_instance[2]; +bool afe_close_done[2] = {true, true}; + +#define SIZEOF_CFG_CMD(y) \ + (sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y))) + +static int afe_get_cal_hw_delay(int32_t path, + struct audio_cal_hw_delay_entry *entry); +static int remap_cal_data(struct cal_block_data *cal_block, int cal_index); + +int afe_get_topology(int port_id) +{ + int topology; + int port_index = afe_get_port_index(port_id); + + if ((port_index < 0) || (port_index > AFE_MAX_PORTS)) { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + topology = -EINVAL; + goto done; + } + + topology = this_afe.topology[port_index]; +done: + return topology; +} + +void afe_set_aanc_info(struct aanc_data *q6_aanc_info) +{ + this_afe.aanc_info.aanc_active = q6_aanc_info->aanc_active; + this_afe.aanc_info.aanc_rx_port = q6_aanc_info->aanc_rx_port; + this_afe.aanc_info.aanc_tx_port = q6_aanc_info->aanc_tx_port; + + pr_debug("%s: aanc active is %d rx port is 0x%x, tx port is 0x%x\n", + __func__, + this_afe.aanc_info.aanc_active, + this_afe.aanc_info.aanc_rx_port, + this_afe.aanc_info.aanc_tx_port); +} + +static void afe_callback_debug_print(struct apr_client_data *data) +{ + uint32_t *payload; + + payload = data->payload; + + if (data->payload_size >= 8) + pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n", + __func__, data->opcode, payload[0], payload[1], + data->payload_size); + else if (data->payload_size >= 4) + pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n", + __func__, data->opcode, payload[0], + data->payload_size); + else + pr_debug("%s: code = 0x%x, size = %d\n", + __func__, data->opcode, data->payload_size); +} + +static int32_t sp_make_afe_callback(uint32_t *payload, uint32_t payload_size) +{ + u32 param_id; + struct afe_spkr_prot_calib_get_resp *resp = + (struct afe_spkr_prot_calib_get_resp *) payload; + + if (!(&(resp->pdata))) { + pr_err("%s: Error: resp pdata is NULL\n", __func__); + return -EINVAL; + } + + param_id = resp->pdata.param_id; + if (param_id == AFE_PARAM_ID_CALIB_RES_CFG_V2) { + if (payload_size < sizeof(this_afe.calib_data)) { + pr_err("%s: Error: received size %d, calib_data size %zu\n", + __func__, payload_size, + sizeof(this_afe.calib_data)); + return -EINVAL; + } + memcpy(&this_afe.calib_data, payload, + sizeof(this_afe.calib_data)); + if (!this_afe.calib_data.status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: calib resp status: %d", __func__, + this_afe.calib_data.status); + atomic_set(&this_afe.state, -1); + } + } + if (param_id == AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS) { + if (payload_size < sizeof(this_afe.th_vi_resp)) { + pr_err("%s: Error: received size %d, th_vi_resp size %zu\n", + __func__, payload_size, + sizeof(this_afe.th_vi_resp)); + return -EINVAL; + } + memcpy(&this_afe.th_vi_resp, payload, + sizeof(this_afe.th_vi_resp)); + if (!this_afe.th_vi_resp.status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: th vi resp status: %d", __func__, + this_afe.th_vi_resp.status); + atomic_set(&this_afe.state, -1); + } + } + if (param_id == AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS) { + if (payload_size < sizeof(this_afe.ex_vi_resp)) { + pr_err("%s: Error: received size %d, ex_vi_resp size %zu\n", + __func__, payload_size, + sizeof(this_afe.ex_vi_resp)); + return -EINVAL; + } + memcpy(&this_afe.ex_vi_resp, payload, + sizeof(this_afe.ex_vi_resp)); + if (!this_afe.ex_vi_resp.status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: ex vi resp status: %d", __func__, + this_afe.ex_vi_resp.status); + atomic_set(&this_afe.state, -1); + } + } + + return 0; +} + +static int32_t afe_callback(struct apr_client_data *data, void *priv) +{ + if (!data) { + pr_err("%s: Invalid param data\n", __func__); + return -EINVAL; + } + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: reset event = %d %d apr[%pK]\n", + __func__, + data->reset_event, data->reset_proc, this_afe.apr); + + cal_utils_clear_cal_block_q6maps(MAX_AFE_CAL_TYPES, + this_afe.cal_data); + + /* Reset the custom topology mode: to resend again to AFE. */ + mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + this_afe.set_custom_topology = 1; + mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + + if (this_afe.apr) { + apr_reset(this_afe.apr); + atomic_set(&this_afe.state, 0); + this_afe.apr = NULL; + rtac_set_afe_handle(this_afe.apr); + } + /* send info to user */ + if (this_afe.task == NULL) + this_afe.task = current; + pr_debug("%s: task_name = %s pid = %d\n", + __func__, + this_afe.task->comm, this_afe.task->pid); + + /* + * Pass reset events to proxy driver, if cb is registered + */ + if (this_afe.tx_cb) { + this_afe.tx_cb(data->opcode, data->token, + data->payload, + this_afe.tx_private_data); + this_afe.tx_cb = NULL; + } + if (this_afe.rx_cb) { + this_afe.rx_cb(data->opcode, data->token, + data->payload, + this_afe.rx_private_data); + this_afe.rx_cb = NULL; + } + + return 0; + } + afe_callback_debug_print(data); + if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) { + u8 *payload = data->payload; + + if (rtac_make_afe_callback(data->payload, data->payload_size)) + return 0; + + if (!payload || (data->token >= AFE_MAX_PORTS)) { + pr_err("%s: Error: size %d payload %pK token %d\n", + __func__, data->payload_size, + payload, data->token); + return -EINVAL; + } + if (sp_make_afe_callback(data->payload, data->payload_size)) + return -EINVAL; + + wake_up(&this_afe.wait[data->token]); + } else if (data->payload_size) { + uint32_t *payload; + uint16_t port_id = 0; + + payload = data->payload; + if (data->opcode == APR_BASIC_RSP_RESULT) { + pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x token=%d\n", + __func__, data->opcode, + payload[0], payload[1], data->token); + /* payload[1] contains the error status for response */ + if (payload[1] != 0) { + atomic_set(&this_afe.status, payload[1]); + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + } + switch (payload[0]) { + case AFE_PORT_CMD_SET_PARAM_V2: + if (rtac_make_afe_callback(payload, + data->payload_size)) + return 0; + case AFE_PORT_CMD_DEVICE_STOP: + case AFE_PORT_CMD_DEVICE_START: + case AFE_PSEUDOPORT_CMD_START: + case AFE_PSEUDOPORT_CMD_STOP: + case AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS: + case AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS: + case AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER: + case AFE_PORTS_CMD_DTMF_CTL: + case AFE_SVC_CMD_SET_PARAM: + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + break; + case AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER: + break; + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2: + port_id = RT_PROXY_PORT_001_TX; + break; + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2: + port_id = RT_PROXY_PORT_001_RX; + break; + case AFE_CMD_ADD_TOPOLOGIES: + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + pr_debug("%s: AFE_CMD_ADD_TOPOLOGIES cmd 0x%x\n", + __func__, payload[1]); + break; + default: + pr_err("%s: Unknown cmd 0x%x\n", __func__, + payload[0]); + break; + } + } else if (data->opcode == + AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS) { + pr_debug("%s: mmap_handle: 0x%x, cal index %d\n", + __func__, payload[0], + atomic_read(&this_afe.mem_map_cal_index)); + if (atomic_read(&this_afe.mem_map_cal_index) != -1) + atomic_set(&this_afe.mem_map_cal_handles[ + atomic_read( + &this_afe.mem_map_cal_index)], + (uint32_t)payload[0]); + else + this_afe.mmap_handle = payload[0]; + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + } else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) { + port_id = (uint16_t)(0x0000FFFF & payload[0]); + } + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + switch (port_id) { + case RT_PROXY_PORT_001_TX: { + if (this_afe.tx_cb) { + this_afe.tx_cb(data->opcode, data->token, + data->payload, + this_afe.tx_private_data); + } + break; + } + case RT_PROXY_PORT_001_RX: { + if (this_afe.rx_cb) { + this_afe.rx_cb(data->opcode, data->token, + data->payload, + this_afe.rx_private_data); + } + break; + } + default: + pr_debug("%s: default case 0x%x\n", __func__, port_id); + break; + } + } + return 0; +} + +int afe_get_port_type(u16 port_id) +{ + int ret; + + switch (port_id) { + case PRIMARY_I2S_RX: + case SECONDARY_I2S_RX: + case MI2S_RX: + case HDMI_RX: + case DISPLAY_PORT_RX: + case AFE_PORT_ID_SPDIF_RX: + case SLIMBUS_0_RX: + case SLIMBUS_1_RX: + case SLIMBUS_2_RX: + case SLIMBUS_3_RX: + case SLIMBUS_4_RX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_7_RX: + case SLIMBUS_8_RX: + case INT_BT_SCO_RX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case RT_PROXY_PORT_001_RX: + case AUDIO_PORT_ID_I2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_RX: + ret = MSM_AFE_PORT_TYPE_RX; + break; + + case PRIMARY_I2S_TX: + case SECONDARY_I2S_TX: + case MI2S_TX: + case DIGI_MIC_TX: + case VOICE_RECORD_TX: + case SLIMBUS_0_TX: + case SLIMBUS_1_TX: + case SLIMBUS_2_TX: + case SLIMBUS_3_TX: + case SLIMBUS_4_TX: + case SLIMBUS_5_TX: + case SLIMBUS_6_TX: + case SLIMBUS_7_TX: + case SLIMBUS_8_TX: + case INT_FM_TX: + case VOICE_RECORD_RX: + case INT_BT_SCO_TX: + case RT_PROXY_PORT_001_TX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_USB_TX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_TX: + ret = MSM_AFE_PORT_TYPE_TX; + break; + + default: + WARN_ON(1); + pr_err("%s: Invalid port id = 0x%x\n", + __func__, port_id); + ret = -EINVAL; + } + + return ret; +} + +int afe_sizeof_cfg_cmd(u16 port_id) +{ + int ret_size; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_i2s_cfg); + break; + case HDMI_RX: + case DISPLAY_PORT_RX: + ret_size = + SIZEOF_CFG_CMD(afe_param_id_hdmi_multi_chan_audio_cfg); + break; + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_5_TX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_slimbus_cfg); + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_pseudo_port_cfg); + break; + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_rt_proxy_port_cfg); + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_usb_audio_cfg); + break; + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + default: + pr_debug("%s: default case 0x%x\n", __func__, port_id); + ret_size = SIZEOF_CFG_CMD(afe_param_id_pcm_cfg); + break; + } + return ret_size; +} + +int afe_q6_interface_prepare(void) +{ + int ret = 0; + + pr_debug("%s:\n", __func__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + } + rtac_set_afe_handle(this_afe.apr); + } + return ret; +} + +/* + * afe_apr_send_pkt : returns 0 on success, negative otherwise. + */ +static int afe_apr_send_pkt(void *data, wait_queue_head_t *wait) +{ + int ret; + + if (wait) + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, data); + if (ret > 0) { + if (wait) { + ret = wait_event_timeout(*wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + ret = -ETIMEDOUT; + } else if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + } else { + ret = 0; + } + } else { + ret = 0; + } + } else if (ret == 0) { + pr_err("%s: packet not transmitted\n", __func__); + /* apr_send_pkt can return 0 when nothing is transmitted */ + ret = -EINVAL; + } + + pr_debug("%s: leave %d\n", __func__, ret); + return ret; +} + +static int afe_send_cal_block(u16 port_id, struct cal_block_data *cal_block) +{ + int result = 0; + int index = 0; + struct afe_audioif_config_command_no_payload afe_cal; + + if (!cal_block) { + pr_debug("%s: No AFE cal to send!\n", __func__); + result = -EINVAL; + goto done; + } + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: AFE cal has invalid size!\n", __func__); + result = -EINVAL; + goto done; + } + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + result = -EINVAL; + goto done; + } + + afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afe_cal.hdr.pkt_size = sizeof(afe_cal); + afe_cal.hdr.src_port = 0; + afe_cal.hdr.dest_port = 0; + afe_cal.hdr.token = index; + afe_cal.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + afe_cal.param.port_id = port_id; + afe_cal.param.payload_size = cal_block->cal_data.size; + afe_cal.param.payload_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + afe_cal.param.payload_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + afe_cal.param.mem_map_handle = cal_block->map_data.q6map_handle; + + pr_debug("%s: AFE cal sent for device port = 0x%x, cal size = %zd, cal addr = 0x%pK\n", + __func__, port_id, + cal_block->cal_data.size, &cal_block->cal_data.paddr); + + result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]); + if (result) + pr_err("%s: AFE cal for port 0x%x failed %d\n", + __func__, port_id, result); + +done: + return result; +} + + +static int afe_send_custom_topology_block(struct cal_block_data *cal_block) +{ + int result = 0; + int index = 0; + struct cmd_set_topologies afe_cal; + + if (!cal_block) { + pr_err("%s: No AFE SVC cal to send!\n", __func__); + return -EINVAL; + } + if (cal_block->cal_data.size <= 0) { + pr_err("%s: AFE SVC cal has invalid size: %zd!\n", + __func__, cal_block->cal_data.size); + return -EINVAL; + } + + afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afe_cal.hdr.pkt_size = sizeof(afe_cal); + afe_cal.hdr.src_port = 0; + afe_cal.hdr.dest_port = 0; + afe_cal.hdr.token = index; + afe_cal.hdr.opcode = AFE_CMD_ADD_TOPOLOGIES; + + afe_cal.payload_size = cal_block->cal_data.size; + afe_cal.payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + afe_cal.payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + afe_cal.mem_map_handle = cal_block->map_data.q6map_handle; + + pr_debug("%s:cmd_id:0x%x calsize:%zd memmap_hdl:0x%x caladdr:0x%pK", + __func__, AFE_CMD_ADD_TOPOLOGIES, cal_block->cal_data.size, + afe_cal.mem_map_handle, &cal_block->cal_data.paddr); + + result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]); + if (result) + pr_err("%s: AFE send topology for command 0x%x failed %d\n", + __func__, AFE_CMD_ADD_TOPOLOGIES, result); + + return result; +} + +static void afe_send_custom_topology(void) +{ + struct cal_block_data *cal_block = NULL; + int cal_index = AFE_CUST_TOPOLOGY_CAL; + int ret; + + if (this_afe.cal_data[cal_index] == NULL) { + pr_err("%s: cal_index %d not allocated!\n", + __func__, cal_index); + return; + } + mutex_lock(&this_afe.cal_data[cal_index]->lock); + + if (!this_afe.set_custom_topology) + goto unlock; + this_afe.set_custom_topology = 0; + cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]); + if (cal_block == NULL) { + pr_err("%s cal_block not found!!\n", __func__); + goto unlock; + } + + pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index); + + ret = remap_cal_data(cal_block, cal_index); + if (ret) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto unlock; + } + ret = afe_send_custom_topology_block(cal_block); + if (ret < 0) { + pr_err("%s: No cal sent for cal_index %d! ret %d\n", + __func__, cal_index, ret); + goto unlock; + } + pr_debug("%s:sent custom topology for AFE\n", __func__); +unlock: + mutex_unlock(&this_afe.cal_data[cal_index]->lock); +} + +static int afe_spk_ramp_dn_cfg(int port) +{ + int ret = -EINVAL; + int index = 0; + struct afe_spkr_prot_config_command config; + + if (afe_get_port_type(port) != MSM_AFE_PORT_TYPE_RX) { + pr_debug("%s: port doesn't match 0x%x\n", __func__, port); + return 0; + } + if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_DISABLED || + (this_afe.vi_rx_port != port)) { + pr_debug("%s: spkr protection disabled port 0x%x %d 0x%x\n", + __func__, port, ret, this_afe.vi_rx_port); + return 0; + } + memset(&config, 0, sizeof(config)); + ret = q6audio_validate_port(port); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", __func__, port, ret); + ret = -EINVAL; + goto fail_cmd; + } + index = q6audio_get_port_index(port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port); + config.param.payload_size = + sizeof(config) - sizeof(config.hdr) - sizeof(config.param) + - sizeof(config.prot_config); + config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX; + config.pdata.param_id = AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG; + config.pdata.param_size = 0; + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config); + if (ret < 0) { + pr_err("%s: port = 0x%x param = 0x%x failed %d\n", + __func__, port, config.pdata.param_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + /* dsp needs atleast 15ms to ramp down pilot tone*/ + usleep_range(15000, 15010); + ret = 0; +fail_cmd: + pr_debug("%s: config.pdata.param_id 0x%x status %d\n", + __func__, config.pdata.param_id, ret); +return ret; +} + +static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, + union afe_spkr_prot_config *prot_config) +{ + int ret = -EINVAL; + int index = 0; + struct afe_spkr_prot_config_command config; + + memset(&config, 0, sizeof(config)); + if (!prot_config) { + pr_err("%s: Invalid params\n", __func__); + goto fail_cmd; + } + ret = q6audio_validate_port(src_port); + if (ret < 0) { + pr_err("%s: Invalid src port 0x%x ret %d", + __func__, src_port, ret); + ret = -EINVAL; + goto fail_cmd; + } + ret = q6audio_validate_port(dst_port); + if (ret < 0) { + pr_err("%s: Invalid dst port 0x%x ret %d", __func__, + dst_port, ret); + ret = -EINVAL; + goto fail_cmd; + } + index = q6audio_get_port_index(src_port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + switch (param_id) { + case AFE_PARAM_ID_FBSP_MODE_RX_CFG: + config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX; + break; + case AFE_PARAM_ID_FEEDBACK_PATH_CFG: + this_afe.vi_tx_port = src_port; + this_afe.vi_rx_port = dst_port; + config.pdata.module_id = AFE_MODULE_FEEDBACK; + break; + /* + * AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2 is same as + * AFE_PARAM_ID_SP_V2_TH_VI_MODE_CFG + */ + case AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2: + case AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG: + config.pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI; + break; + case AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG: + case AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG: + config.pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI; + break; + default: + pr_err("%s: default case 0x%x\n", __func__, param_id); + goto fail_cmd; + } + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(src_port); + config.param.payload_size = sizeof(config) - sizeof(config.hdr) + - sizeof(config.param); + config.pdata.param_id = param_id; + config.pdata.param_size = sizeof(config.prot_config); + config.prot_config = *prot_config; + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config); + if (ret < 0) { + pr_err("%s: port = 0x%x param = 0x%x failed %d\n", + __func__, src_port, param_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + ret = 0; +fail_cmd: + pr_debug("%s: config.pdata.param_id 0x%x status %d 0x%x\n", + __func__, config.pdata.param_id, ret, src_port); + return ret; +} + +static void afe_send_cal_spkr_prot_tx(int port_id) +{ + union afe_spkr_prot_config afe_spk_config; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL || + this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL || + this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL) + return; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + if ((this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED) && + (this_afe.vi_tx_port == port_id)) { + if (this_afe.prot_cfg.mode == + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) { + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_CALIBRATION; + afe_spk_config.vi_proc_cfg.quick_calib_flag = + this_afe.prot_cfg.quick_calib_flag; + } else { + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_PROCESSING; + } + + if (this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_FTM_MODE; + afe_spk_config.vi_proc_cfg.minor_version = 1; + afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_1] = + (uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_1]; + afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_2] = + (uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_2]; + afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_1] = + (uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_1]; + afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_2] = + (uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_2]; + if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_NOT_CALIBRATED) { + struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg; + + vi_proc_cfg = &afe_spk_config.vi_proc_cfg; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] = + USE_CALIBRATED_R0TO; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] = + USE_CALIBRATED_R0TO; + } else { + struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg; + + vi_proc_cfg = &afe_spk_config.vi_proc_cfg; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] = + USE_SAFE_R0TO; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] = + USE_SAFE_R0TO; + } + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2, + &afe_spk_config)) + pr_err("%s: SPKR_CALIB_VI_PROC_CFG failed\n", + __func__); + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + if ((this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) && + (this_afe.vi_tx_port == port_id)) { + afe_spk_config.th_vi_ftm_cfg.minor_version = 1; + afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] = + this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_1]; + afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] = + this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_2]; + afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] = + this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_1]; + afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] = + this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_2]; + + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG, + &afe_spk_config)) + pr_err("%s: th vi ftm cfg failed\n", __func__); + this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + if ((this_afe.ex_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) && + (this_afe.vi_tx_port == port_id)) { + afe_spk_config.ex_vi_mode_cfg.minor_version = 1; + afe_spk_config.ex_vi_mode_cfg.operation_mode = + Q6AFE_MSM_SPKR_FTM_MODE; + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG, + &afe_spk_config)) + pr_err("%s: ex vi mode cfg failed\n", __func__); + + afe_spk_config.ex_vi_ftm_cfg.minor_version = 1; + afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] = + this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_1]; + afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] = + this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_2]; + afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] = + this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_1]; + afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] = + this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_2]; + + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG, + &afe_spk_config)) + pr_err("%s: ex vi ftm cfg failed\n", __func__); + this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + +} + +static void afe_send_cal_spkr_prot_rx(int port_id) +{ + union afe_spkr_prot_config afe_spk_config; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + + if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED && + (this_afe.vi_rx_port == port_id)) { + if (this_afe.prot_cfg.mode == + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) + afe_spk_config.mode_rx_cfg.mode = + Q6AFE_MSM_SPKR_CALIBRATION; + else + afe_spk_config.mode_rx_cfg.mode = + Q6AFE_MSM_SPKR_PROCESSING; + afe_spk_config.mode_rx_cfg.minor_version = 1; + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_FBSP_MODE_RX_CFG, + &afe_spk_config)) + pr_err("%s: RX MODE_VI_PROC_CFG failed\n", + __func__); + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); +done: + return; +} + +static int afe_send_hw_delay(u16 port_id, u32 rate) +{ + struct audio_cal_hw_delay_entry delay_entry; + struct afe_audioif_config_command config; + int index = 0; + int ret = -EINVAL; + + pr_debug("%s:\n", __func__); + + delay_entry.sample_rate = rate; + if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) + ret = afe_get_cal_hw_delay(TX_DEVICE, &delay_entry); + else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) + ret = afe_get_cal_hw_delay(RX_DEVICE, &delay_entry); + + /* + * HW delay is only used for IMS calls to sync audio with video + * It is only needed for devices & sample rates used for IMS video + * calls. Values are received from ACDB calbration files + */ + if (ret != 0) { + pr_debug("%s: debug: HW delay info not available %d\n", + __func__, ret); + goto fail_cmd; + } + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = AFE_PARAM_ID_DEVICE_HW_DELAY; + config.pdata.param_size = sizeof(config.port); + + config.port.hw_delay.delay_in_us = delay_entry.delay_usec; + config.port.hw_delay.device_hw_delay_minor_version = + AFE_API_VERSION_DEVICE_HW_DELAY; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE hw delay for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + +fail_cmd: + pr_debug("%s: port_id 0x%x rate %u delay_usec %d status %d\n", + __func__, port_id, rate, delay_entry.delay_usec, ret); + return ret; +} + +static struct cal_block_data *afe_find_cal_topo_id_by_port( + struct cal_type_data *cal_type, u16 port_id) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + int32_t path; + struct audio_cal_info_afe_top *afe_top; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + cal_block = list_entry(ptr, + struct cal_block_data, list); + + path = ((afe_get_port_type(port_id) == + MSM_AFE_PORT_TYPE_TX)?(TX_DEVICE):(RX_DEVICE)); + afe_top = + (struct audio_cal_info_afe_top *)cal_block->cal_info; + if (afe_top->path == path) { + pr_debug("%s: top_id:%x acdb_id:%d afe_port:%d\n", + __func__, afe_top->topology, afe_top->acdb_id, + q6audio_get_port_id(port_id)); + return cal_block; + } + } + return NULL; +} + +static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id) +{ + int ret = 0; + + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_afe_top *afe_top_info = NULL; + + if (this_afe.cal_data[AFE_TOPOLOGY_CAL] == NULL) { + pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized\n", __func__); + return -EINVAL; + } + if (topology_id == NULL) { + pr_err("%s: topology_id is NULL\n", __func__); + return -EINVAL; + } + *topology_id = 0; + + mutex_lock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock); + cal_block = afe_find_cal_topo_id_by_port( + this_afe.cal_data[AFE_TOPOLOGY_CAL], port_id); + if (cal_block == NULL) { + pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized for this port %d\n", + __func__, port_id); + ret = -EINVAL; + goto unlock; + } + + afe_top_info = ((struct audio_cal_info_afe_top *) + cal_block->cal_info); + if (!afe_top_info->topology) { + pr_err("%s: invalid topology id : [%d, %d]\n", + __func__, afe_top_info->acdb_id, afe_top_info->topology); + ret = -EINVAL; + goto unlock; + } + *topology_id = (u32)afe_top_info->topology; + + pr_debug("%s: port_id = %u acdb_id = %d topology_id = %u ret=%d\n", + __func__, port_id, afe_top_info->acdb_id, + afe_top_info->topology, ret); +unlock: + mutex_unlock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock); + return ret; +} + +static int afe_send_port_topology_id(u16 port_id) +{ + struct afe_audioif_config_command config; + int index = 0; + int ret = 0; + u32 topology_id = 0; + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS - 1) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + + ret = afe_get_cal_topology_id(port_id, &topology_id); + if (ret || !topology_id) { + pr_debug("%s: AFE port[%d] get_cal_topology[%d] invalid!\n", + __func__, port_id, topology_id); + goto done; + } + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = AFE_PARAM_ID_SET_TOPOLOGY; + config.pdata.param_size = sizeof(config.port); + config.port.topology.minor_version = AFE_API_VERSION_TOPOLOGY_V1; + config.port.topology.topology_id = topology_id; + + pr_debug("%s: param PL size=%d iparam_size[%d][%zd %zd %zd %zd] param_id[0x%x]\n", + __func__, config.param.payload_size, config.pdata.param_size, + sizeof(config), sizeof(config.param), sizeof(config.port), + sizeof(struct apr_hdr), config.pdata.param_id); + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE set topology id enable for port 0x%x failed %d\n", + __func__, port_id, ret); + goto done; + } + + this_afe.topology[index] = topology_id; +done: + pr_debug("%s: AFE set topology id 0x%x enable for port 0x%x ret %d\n", + __func__, topology_id, port_id, ret); + return ret; + +} + +static int remap_cal_data(struct cal_block_data *cal_block, int cal_index) +{ + int ret = 0; + + if (cal_block->map_data.ion_client == NULL) { + pr_err("%s: No ION allocation for cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + atomic_set(&this_afe.mem_map_cal_index, cal_index); + ret = afe_cmd_memory_map(cal_block->cal_data.paddr, + cal_block->map_data.map_size); + atomic_set(&this_afe.mem_map_cal_index, -1); + if (ret < 0) { + pr_err("%s: mmap did not work! size = %zd ret %d\n", + __func__, + cal_block->map_data.map_size, ret); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_afe. + mem_map_cal_handles[cal_index]); + } +done: + return ret; +} + +static void send_afe_cal_type(int cal_index, int port_id) +{ + struct cal_block_data *cal_block = NULL; + int ret; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[cal_index] == NULL) { + pr_warn("%s: cal_index %d not allocated!\n", + __func__, cal_index); + goto done; + } + + mutex_lock(&this_afe.cal_data[cal_index]->lock); + cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]); + if (cal_block == NULL) { + pr_err("%s cal_block not found!!\n", __func__); + goto unlock; + } + + pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index); + + ret = remap_cal_data(cal_block, cal_index); + if (ret) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto unlock; + } + ret = afe_send_cal_block(port_id, cal_block); + if (ret < 0) + pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d\n", + __func__, cal_index, port_id, ret); +unlock: + mutex_unlock(&this_afe.cal_data[cal_index]->lock); +done: + return; +} + +void afe_send_cal(u16 port_id) +{ + pr_debug("%s: port_id=0x%x\n", __func__, port_id); + + if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) { + afe_send_cal_spkr_prot_tx(port_id); + send_afe_cal_type(AFE_COMMON_TX_CAL, port_id); + } else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) { + afe_send_cal_spkr_prot_rx(port_id); + send_afe_cal_type(AFE_COMMON_RX_CAL, port_id); + } +} + +int afe_turn_onoff_hw_mad(u16 mad_type, u16 enable) +{ + int ret; + struct afe_cmd_hw_mad_ctrl config; + + pr_debug("%s: enter\n", __func__); + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = SLIMBUS_5_TX; + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_HW_MAD; + config.pdata.param_id = AFE_PARAM_ID_HW_MAD_CTRL; + config.pdata.param_size = sizeof(config.payload); + config.payload.minor_version = 1; + config.payload.mad_type = mad_type; + config.payload.mad_enable = enable; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) + pr_err("%s: AFE_PARAM_ID_HW_MAD_CTRL failed %d\n", __func__, + ret); + return ret; +} + +static int afe_send_slimbus_slave_cfg( + struct afe_param_cdc_slimbus_slave_cfg *sb_slave_cfg) +{ + int ret; + struct afe_svc_cmd_sb_slave_cfg config; + + pr_debug("%s: enter\n", __func__); + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG; + config.pdata.param_id = AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG; + config.pdata.param_size = + sizeof(struct afe_param_cdc_slimbus_slave_cfg); + config.sb_slave_cfg = *sb_slave_cfg; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) + pr_err("%s: AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG failed %d\n", + __func__, ret); + + pr_debug("%s: leave %d\n", __func__, ret); + return ret; +} + +static int afe_send_codec_reg_page_config( + struct afe_param_cdc_reg_page_cfg *cdc_reg_page_cfg) +{ + struct afe_svc_cmd_cdc_reg_page_cfg config; + int ret; + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG; + config.pdata.param_id = AFE_PARAM_ID_CDC_REG_PAGE_CFG; + config.pdata.param_size = + sizeof(struct afe_param_cdc_reg_page_cfg); + config.cdc_reg_page_cfg = *cdc_reg_page_cfg; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) + pr_err("%s: AFE_PARAM_ID_CDC_REG_PAGE_CFG failed %d\n", + __func__, ret); + + return ret; +} + +static int afe_send_codec_reg_config( + struct afe_param_cdc_reg_cfg_data *cdc_reg_cfg) +{ + int i, j, ret = -EINVAL; + int pkt_size, payload_size, reg_per_pkt, num_pkts, num_regs; + struct afe_svc_cmd_cdc_reg_cfg *config; + struct afe_svc_cmd_set_param *param; + + reg_per_pkt = (APR_MAX_BUF - sizeof(*config)) / + sizeof(struct afe_param_cdc_reg_cfg_payload); + if (reg_per_pkt > 0) { + num_pkts = (cdc_reg_cfg->num_registers / reg_per_pkt) + + (cdc_reg_cfg->num_registers % reg_per_pkt == 0 ? 0 : 1); + } else { + pr_err("%s: Failed to build codec reg config APR packet\n", + __func__); + return -EINVAL; + } + + for (j = 0; j < num_pkts; ++j) { + /* + * num_regs is set to reg_per_pkt on each pass through the loop + * except the last, when it is set to the number of registers + * remaining from the total + */ + num_regs = (j < (num_pkts - 1) ? reg_per_pkt : + cdc_reg_cfg->num_registers - (reg_per_pkt * j)); + payload_size = sizeof(struct afe_param_cdc_reg_cfg_payload) * + num_regs; + pkt_size = sizeof(*config) + payload_size; + pr_debug("%s: pkt_size %d, payload_size %d\n", __func__, + pkt_size, payload_size); + config = kzalloc(pkt_size, GFP_KERNEL); + if (!config) + return -ENOMEM; + + config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config->hdr.pkt_size = pkt_size; + config->hdr.src_port = 0; + config->hdr.dest_port = 0; + config->hdr.token = IDX_GLOBAL_CFG; + config->hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + param = &config->param; + param->payload_size = payload_size; + param->payload_address_lsw = 0x00; + param->payload_address_msw = 0x00; + param->mem_map_handle = 0x00; + + for (i = 0; i < num_regs; i++) { + config->reg_data[i].common.module_id = + AFE_MODULE_CDC_DEV_CFG; + config->reg_data[i].common.param_id = + AFE_PARAM_ID_CDC_REG_CFG; + config->reg_data[i].common.param_size = + sizeof(config->reg_data[i].reg_cfg); + config->reg_data[i].reg_cfg = + cdc_reg_cfg->reg_data[i + (j * reg_per_pkt)]; + } + + ret = afe_apr_send_pkt(config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) { + pr_err("%s: AFE_PARAM_ID_CDC_REG_CFG failed %d\n", + __func__, ret); + kfree(config); + break; + } + kfree(config); + } + + return ret; +} + +static int afe_init_cdc_reg_config(void) +{ + int ret; + struct afe_svc_cmd_init_cdc_reg_cfg config; + + pr_debug("%s: enter\n", __func__); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + config.param.payload_size = sizeof(struct afe_port_param_data_v2); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + + config.init.module_id = AFE_MODULE_CDC_DEV_CFG; + config.init.param_id = AFE_PARAM_ID_CDC_REG_CFG_INIT; + config.init.param_size = 0; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) { + pr_err("%s: AFE_PARAM_ID_CDC_INIT_REG_CFG failed %d\n", + __func__, ret); + } + + return ret; +} + +static int afe_send_slimbus_slave_port_cfg( + struct afe_param_slimbus_slave_port_cfg *port_config, u16 port_id) +{ + int ret, index; + struct afe_cmd_hw_mad_slimbus_slave_port_cfg config; + + pr_debug("%s: enter, port_id = 0x%x\n", __func__, port_id); + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id = 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = port_id; + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_HW_MAD; + config.pdata.param_id = AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG; + config.pdata.param_size = sizeof(*port_config); + config.sb_port_cfg = *port_config; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG failed %d\n", + __func__, ret); + } + pr_debug("%s: leave %d\n", __func__, ret); + return ret; +} +static int afe_aanc_port_cfg(void *apr, uint16_t tx_port, uint16_t rx_port) +{ + struct afe_port_cmd_set_aanc_param cfg; + int ret = 0; + int index = 0; + + pr_debug("%s: tx_port 0x%x, rx_port 0x%x\n", + __func__, tx_port, rx_port); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return -EINVAL; + } + + index = q6audio_get_port_index(tx_port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(tx_port); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, tx_port, ret); + return -EINVAL; + } + pr_debug("%s: AANC sample rate tx rate: %d rx rate %d\n", + __func__, this_afe.aanc_info.aanc_tx_port_sample_rate, + this_afe.aanc_info.aanc_rx_port_sample_rate); + /* + * If aanc tx sample rate or rx sample rate is zero, skip aanc + * configuration as AFE resampler will fail for invalid sample + * rates. + */ + if (!this_afe.aanc_info.aanc_tx_port_sample_rate || + !this_afe.aanc_info.aanc_rx_port_sample_rate) { + return -EINVAL; + } + + cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cfg.hdr.pkt_size = sizeof(cfg); + cfg.hdr.src_port = 0; + cfg.hdr.dest_port = 0; + cfg.hdr.token = index; + cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + cfg.param.port_id = tx_port; + cfg.param.payload_size = sizeof(struct afe_port_param_data_v2) + + sizeof(struct afe_param_aanc_port_cfg); + cfg.param.payload_address_lsw = 0; + cfg.param.payload_address_msw = 0; + cfg.param.mem_map_handle = 0; + + cfg.pdata.module_id = AFE_MODULE_AANC; + cfg.pdata.param_id = AFE_PARAM_ID_AANC_PORT_CONFIG; + cfg.pdata.param_size = sizeof(struct afe_param_aanc_port_cfg); + cfg.pdata.reserved = 0; + + cfg.data.aanc_port_cfg.aanc_port_cfg_minor_version = + AFE_API_VERSION_AANC_PORT_CONFIG; + cfg.data.aanc_port_cfg.tx_port_sample_rate = + this_afe.aanc_info.aanc_tx_port_sample_rate; + cfg.data.aanc_port_cfg.tx_port_channel_map[0] = AANC_TX_VOICE_MIC; + cfg.data.aanc_port_cfg.tx_port_channel_map[1] = AANC_TX_NOISE_MIC; + cfg.data.aanc_port_cfg.tx_port_channel_map[2] = AANC_TX_ERROR_MIC; + cfg.data.aanc_port_cfg.tx_port_channel_map[3] = AANC_TX_MIC_UNUSED; + cfg.data.aanc_port_cfg.tx_port_channel_map[4] = AANC_TX_MIC_UNUSED; + cfg.data.aanc_port_cfg.tx_port_channel_map[5] = AANC_TX_MIC_UNUSED; + cfg.data.aanc_port_cfg.tx_port_channel_map[6] = AANC_TX_MIC_UNUSED; + cfg.data.aanc_port_cfg.tx_port_channel_map[7] = AANC_TX_MIC_UNUSED; + cfg.data.aanc_port_cfg.tx_port_num_channels = 3; + cfg.data.aanc_port_cfg.rx_path_ref_port_id = rx_port; + cfg.data.aanc_port_cfg.ref_port_sample_rate = + this_afe.aanc_info.aanc_rx_port_sample_rate; + + ret = afe_apr_send_pkt((uint32_t *) &cfg, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE AANC port config failed for tx_port 0x%x, rx_port 0x%x ret %d\n", + __func__, tx_port, rx_port, ret); + } + + return ret; +} + +static int afe_aanc_mod_enable(void *apr, uint16_t tx_port, uint16_t enable) +{ + struct afe_port_cmd_set_aanc_param cfg; + int ret = 0; + int index = 0; + + pr_debug("%s: tx_port 0x%x\n", + __func__, tx_port); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return -EINVAL; + } + + index = q6audio_get_port_index(tx_port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(tx_port); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, tx_port, ret); + return -EINVAL; + } + + cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cfg.hdr.pkt_size = sizeof(cfg); + cfg.hdr.src_port = 0; + cfg.hdr.dest_port = 0; + cfg.hdr.token = index; + cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + cfg.param.port_id = tx_port; + cfg.param.payload_size = sizeof(struct afe_port_param_data_v2) + + sizeof(struct afe_mod_enable_param); + cfg.param.payload_address_lsw = 0; + cfg.param.payload_address_lsw = 0; + cfg.param.mem_map_handle = 0; + + cfg.pdata.module_id = AFE_MODULE_AANC; + cfg.pdata.param_id = AFE_PARAM_ID_ENABLE; + cfg.pdata.param_size = sizeof(struct afe_mod_enable_param); + cfg.pdata.reserved = 0; + + cfg.data.mod_enable.enable = enable; + cfg.data.mod_enable.reserved = 0; + + ret = afe_apr_send_pkt((uint32_t *) &cfg, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE AANC enable failed for tx_port 0x%x ret %d\n", + __func__, tx_port, ret); + } + return ret; +} + +static int afe_send_bank_selection_clip( + struct afe_param_id_clip_bank_sel *param) +{ + int ret; + struct afe_svc_cmd_set_clip_bank_selection config; + + if (!param) { + pr_err("%s: Invalid params", __func__); + return -EINVAL; + } + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + config.param.payload_size = sizeof(struct afe_port_param_data_v2) + + sizeof(struct afe_param_id_clip_bank_sel); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + + config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG; + config.pdata.param_id = AFE_PARAM_ID_CLIP_BANK_SEL_CFG; + config.pdata.param_size = + sizeof(struct afe_param_id_clip_bank_sel); + config.bank_sel = *param; + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) { + pr_err("%s: AFE_PARAM_ID_CLIP_BANK_SEL_CFG failed %d\n", + __func__, ret); + } + return ret; +} +int afe_send_aanc_version( + struct afe_param_id_cdc_aanc_version *version_cfg) +{ + int ret; + struct afe_svc_cmd_cdc_aanc_version config; + + pr_debug("%s: enter\n", __func__); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + config.param.payload_size = sizeof(struct afe_port_param_data_v2) + + sizeof(struct afe_param_id_cdc_aanc_version); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + + config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG; + config.pdata.param_id = AFE_PARAM_ID_CDC_AANC_VERSION; + config.pdata.param_size = + sizeof(struct afe_param_id_cdc_aanc_version); + config.version = *version_cfg; + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) { + pr_err("%s: AFE_PARAM_ID_CDC_AANC_VERSION failed %d\n", + __func__, ret); + } + return ret; +} + +int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type) +{ + int i; + + if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX) { + mad_type = MAD_SW_AUDIO; + return 0; + } + + i = port_id - SLIMBUS_0_RX; + if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + atomic_set(&afe_ports_mad_type[i], mad_type); + return 0; +} + +enum afe_mad_type afe_port_get_mad_type(u16 port_id) +{ + int i; + + if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX) + return MAD_SW_AUDIO; + + i = port_id - SLIMBUS_0_RX; + if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) { + pr_debug("%s: Non Slimbus port_id 0x%x\n", __func__, port_id); + return MAD_HW_NONE; + } + return (enum afe_mad_type) atomic_read(&afe_ports_mad_type[i]); +} + +int afe_set_config(enum afe_config_type config_type, void *config_data, int arg) +{ + int ret; + + pr_debug("%s: enter config_type %d\n", __func__, config_type); + ret = afe_q6_interface_prepare(); + if (ret) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + ret = afe_send_slimbus_slave_cfg(config_data); + if (!ret) + ret = afe_init_cdc_reg_config(); + else + pr_err("%s: Sending slimbus slave config failed %d\n", + __func__, ret); + break; + case AFE_CDC_REGISTERS_CONFIG: + ret = afe_send_codec_reg_config(config_data); + break; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + ret = afe_send_slimbus_slave_port_cfg(config_data, arg); + break; + case AFE_AANC_VERSION: + ret = afe_send_aanc_version(config_data); + break; + case AFE_CLIP_BANK_SEL: + ret = afe_send_bank_selection_clip(config_data); + break; + case AFE_CDC_CLIP_REGISTERS_CONFIG: + ret = afe_send_codec_reg_config(config_data); + break; + case AFE_CDC_REGISTER_PAGE_CONFIG: + ret = afe_send_codec_reg_page_config(config_data); + break; + default: + pr_err("%s: unknown configuration type %d", + __func__, config_type); + ret = -EINVAL; + } + + if (!ret) + set_bit(config_type, &afe_configured_cmd); + + return ret; +} + +/* + * afe_clear_config - If SSR happens ADSP loses AFE configs, let AFE driver know + * about the state so client driver can wait until AFE is + * reconfigured. + */ +void afe_clear_config(enum afe_config_type config) +{ + clear_bit(config, &afe_configured_cmd); +} + +bool afe_has_config(enum afe_config_type config) +{ + return !!test_bit(config, &afe_configured_cmd); +} + +int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg, + u16 port_id) +{ + struct afe_spdif_clk_config_command clk_cfg; + int ret = 0; + int index = 0; + + if (!cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + clk_cfg.hdr.pkt_size = sizeof(clk_cfg); + clk_cfg.hdr.src_port = 0; + clk_cfg.hdr.dest_port = 0; + clk_cfg.hdr.token = index; + + clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + clk_cfg.param.port_id = q6audio_get_port_id(port_id); + clk_cfg.param.payload_address_lsw = 0x00; + clk_cfg.param.payload_address_msw = 0x00; + clk_cfg.param.mem_map_handle = 0x00; + clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + clk_cfg.pdata.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG; + clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg); + clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr) + - sizeof(clk_cfg.param); + clk_cfg.clk_cfg = *cfg; + + pr_debug("%s: Minor version = 0x%x clk val = %d\n" + "clk root = 0x%x\n port id = 0x%x\n", + __func__, cfg->clk_cfg_minor_version, + cfg->clk_value, cfg->clk_root, + q6audio_get_port_id(port_id)); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg); + if (ret < 0) { + pr_err("%s: AFE send clock config for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg + *ch_status_cfg, u16 port_id) +{ + struct afe_spdif_chstatus_config_command ch_status; + int ret = 0; + int index = 0; + + if (!ch_status_cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + ch_status.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + ch_status.hdr.pkt_size = sizeof(ch_status_cfg); + ch_status.hdr.src_port = 0; + ch_status.hdr.dest_port = 0; + ch_status.hdr.token = index; + + ch_status.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + ch_status.param.port_id = q6audio_get_port_id(port_id); + ch_status.param.payload_address_lsw = 0x00; + ch_status.param.payload_address_msw = 0x00; + ch_status.param.mem_map_handle = 0x00; + ch_status.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + ch_status.pdata.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG; + ch_status.pdata.param_size = sizeof(ch_status.ch_status); + ch_status.param.payload_size = sizeof(ch_status) + - sizeof(struct apr_hdr) - sizeof(ch_status.param); + ch_status.ch_status = *ch_status_cfg; + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &ch_status); + if (ret < 0) { + pr_err("%s: AFE send channel status for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +static int afe_send_cmd_port_start(u16 port_id) +{ + struct afe_port_cmd_device_start start; + int ret, index; + + pr_debug("%s: enter\n", __func__); + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = index; + start.hdr.opcode = AFE_PORT_CMD_DEVICE_START; + start.port_id = q6audio_get_port_id(port_id); + pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n", + __func__, start.hdr.opcode, start.port_id); + + ret = afe_apr_send_pkt(&start, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__, + port_id, ret); + } else if (this_afe.task != current) { + this_afe.task = current; + pr_debug("task_name = %s pid = %d\n", + this_afe.task->comm, this_afe.task->pid); + } + + return ret; +} + +static int afe_aanc_start(uint16_t tx_port_id, uint16_t rx_port_id) +{ + int ret; + + pr_debug("%s: Tx port is 0x%x, Rx port is 0x%x\n", + __func__, tx_port_id, rx_port_id); + ret = afe_aanc_port_cfg(this_afe.apr, tx_port_id, rx_port_id); + if (ret) { + pr_err("%s: Send AANC Port Config failed %d\n", + __func__, ret); + goto fail_cmd; + } + send_afe_cal_type(AFE_AANC_CAL, tx_port_id); + +fail_cmd: + return ret; +} + +int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port, + u32 rate) +{ + struct afe_audioif_config_command config; + int ret = 0; + int index = 0; + uint16_t port_index; + + if (!spdif_port) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + afe_send_cal(port_id); + afe_send_hw_delay(port_id, rate); + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = AFE_PARAM_ID_SPDIF_CONFIG; + config.pdata.param_size = sizeof(config.port); + config.port.spdif = spdif_port->cfg; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = rate; + } else { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + + ret = afe_send_spdif_ch_status_cfg(&spdif_port->ch_status, port_id); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + goto fail_cmd; + } + + return afe_send_cmd_port_start(port_id); + +fail_cmd: + return ret; +} + +int afe_send_slot_mapping_cfg( + struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg, + u16 port_id) +{ + struct afe_slot_mapping_config_command config; + int ret = 0; + int index = 0; + + if (!slot_mapping_cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) + - sizeof(struct apr_hdr) - sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_TDM; + config.pdata.param_id = AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG; + config.pdata.param_size = sizeof(config.slot_mapping); + config.slot_mapping = *slot_mapping_cfg; + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config); + if (ret < 0) { + pr_err("%s: AFE send slot mapping for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +int afe_send_custom_tdm_header_cfg( + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg, + u16 port_id) +{ + struct afe_custom_tdm_header_config_command config; + int ret = 0; + int index = 0; + + if (!custom_tdm_header_cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) + - sizeof(struct apr_hdr) - sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_TDM; + config.pdata.param_id = AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG; + config.pdata.param_size = sizeof(config.custom_tdm_header); + config.custom_tdm_header = *custom_tdm_header_cfg; + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config); + if (ret < 0) { + pr_err("%s: AFE send custom tdm header for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, + u32 rate) +{ + struct afe_audioif_config_command config; + int ret = 0; + int index = 0; + uint16_t port_index = 0; + enum afe_mad_type mad_type = MAD_HW_NONE; + + if (!tdm_port) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + /* Also send the topology id here: */ + port_index = afe_get_port_index(port_id); + if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) { + /* One time call: only for first time */ + afe_send_custom_topology(); + afe_send_port_topology_id(port_id); + afe_send_cal(port_id); + afe_send_hw_delay(port_id, rate); + } + + /* Start SW MAD module */ + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) { + if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) || + !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) { + pr_err("%s: AFE isn't configured yet for\n" + "HW MAD try Again\n", __func__); + ret = -EAGAIN; + goto fail_cmd; + } + ret = afe_turn_onoff_hw_mad(mad_type, true); + if (ret) { + pr_err("%s: afe_turn_onoff_hw_mad failed %d\n", + __func__, ret); + goto fail_cmd; + } + } + + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = AFE_PARAM_ID_TDM_CONFIG; + config.pdata.param_size = sizeof(config.port); + config.port.tdm = tdm_port->tdm; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = rate; + } else { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + + ret = afe_send_slot_mapping_cfg(&tdm_port->slot_mapping, port_id); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + goto fail_cmd; + } + + if (tdm_port->custom_tdm_header.header_type) { + ret = afe_send_custom_tdm_header_cfg( + &tdm_port->custom_tdm_header, port_id); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + goto fail_cmd; + } + } + + ret = afe_send_cmd_port_start(port_id); + +fail_cmd: + return ret; +} + +void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode) +{ + uint16_t port_index; + + port_index = afe_get_port_index(port_id); + this_afe.afe_cal_mode[port_index] = afe_cal_mode; +} + +int afe_port_send_usb_dev_param(u16 port_id, union afe_port_config *afe_config) +{ + struct afe_usb_audio_dev_param_command config; + int ret = 0, index = 0; + + if (!afe_config) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + goto exit; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid! for port ID 0x%x\n", + __func__, index, port_id); + ret = -EINVAL; + goto exit; + } + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS; + config.pdata.param_size = sizeof(config.usb_dev); + config.usb_dev.cfg_minor_version = + AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG; + config.usb_dev.dev_token = afe_config->usb_audio.dev_token; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE device param cmd failed %d\n", + __func__, ret); + ret = -EINVAL; + goto exit; + } +exit: + return ret; +} + +static int q6afe_send_enc_config(u16 port_id, + union afe_enc_config_data *cfg, u32 format, + union afe_port_config afe_config, + u16 afe_in_channels, u16 afe_in_bit_width) +{ + struct afe_audioif_config_command config; + int index; + int ret; + int payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param) - sizeof(config.port); + + pr_debug("%s:update DSP for enc format = %d\n", __func__, format); + if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2 && + format != ASM_MEDIA_FMT_APTX && format != ASM_MEDIA_FMT_APTX_HD) { + pr_err("%s:Unsuppported format Ignore AFE config\n", __func__); + return 0; + } + memset(&config, 0, sizeof(config)); + index = q6audio_get_port_index(port_id); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = payload_size + sizeof(config.port.enc_fmt); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_ID_ENCODER; + config.pdata.param_id = AFE_ENCODER_PARAM_ID_ENC_FMT_ID; + config.pdata.param_size = sizeof(config.port.enc_fmt); + config.port.enc_fmt.fmt_id = format; + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_ENC_FMT_ID payload: %d\n", + __func__, config.param.payload_size); + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s:unable to send AFE_ENCODER_PARAM_ID_ENC_FMT_ID", + __func__); + goto exit; + } + + config.param.payload_size = payload_size + + sizeof(config.port.enc_blk_param); + pr_debug("%s:send AFE_ENCODER_PARAM_ID_ENC_CFG_BLK to DSP payload:%d\n", + __func__, config.param.payload_size); + config.pdata.param_id = AFE_ENCODER_PARAM_ID_ENC_CFG_BLK; + config.pdata.param_size = sizeof(config.port.enc_blk_param); + config.port.enc_blk_param.enc_cfg_blk_size = + sizeof(config.port.enc_blk_param.enc_blk_config); + config.port.enc_blk_param.enc_blk_config = *cfg; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_ENC_CFG_BLK for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + config.param.payload_size = + payload_size + sizeof(config.port.enc_pkt_id_param); + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_PACKETIZER to DSP payload = %d", + __func__, config.param.payload_size); + config.pdata.param_id = AFE_ENCODER_PARAM_ID_PACKETIZER_ID; + config.pdata.param_size = sizeof(config.port.enc_pkt_id_param); + config.port.enc_pkt_id_param.enc_packetizer_id = + AFE_MODULE_ID_PACKETIZER_COP; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_PACKETIZER for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + config.param.payload_size = + payload_size + sizeof(config.port.media_type); + config.pdata.param_size = sizeof(config.port.media_type); + + pr_debug("%s:Sending AFE_API_VERSION_PORT_MEDIA_TYPE to DSP", __func__); + config.pdata.module_id = AFE_MODULE_PORT; + config.pdata.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE; + config.port.media_type.minor_version = AFE_API_VERSION_PORT_MEDIA_TYPE; + config.port.media_type.sample_rate = afe_config.slim_sch.sample_rate; + if (afe_in_bit_width) + config.port.media_type.bit_width = afe_in_bit_width; + else + config.port.media_type.bit_width = + afe_config.slim_sch.bit_width; + + if (afe_in_channels) + config.port.media_type.num_channels = afe_in_channels; + else + config.port.media_type.num_channels = + afe_config.slim_sch.num_channels; + config.port.media_type.data_format = AFE_PORT_DATA_FORMAT_PCM; + config.port.media_type.reserved = 0; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_API_VERSION_PORT_MEDIA_TYPE for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + +exit: + return ret; +} + +static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, + u32 rate, u16 afe_in_channels, u16 afe_in_bit_width, + union afe_enc_config_data *cfg, u32 enc_format) +{ + struct afe_audioif_config_command config; + int ret = 0; + int cfg_type; + int index = 0; + enum afe_mad_type mad_type; + uint16_t port_index; + + if (!afe_config) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) { + pr_debug("%s: before incrementing pcm_afe_instance %d", + " port_id 0x%x\n", __func__, + pcm_afe_instance[port_id & 0x1], port_id); + port_id = VIRTUAL_ID_TO_PORTID(port_id); + pcm_afe_instance[port_id & 0x1]++; + return 0; + } + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + pr_debug("%s: before incrementing proxy_afe_instance %d", + " port_id 0x%x\n", __func__, + proxy_afe_instance[port_id & 0x1], port_id); + + if (!afe_close_done[port_id & 0x1]) { + /*close pcm dai corresponding to the proxy dai*/ + afe_close(port_id - 0x10); + pcm_afe_instance[port_id & 0x1]++; + pr_debug("%s: reconfigure afe port again\n", __func__); + } + proxy_afe_instance[port_id & 0x1]++; + afe_close_done[port_id & 0x1] = false; + port_id = VIRTUAL_ID_TO_PORTID(port_id); + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + /* Also send the topology id here: */ + port_index = afe_get_port_index(port_id); + if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) { + /* One time call: only for first time */ + afe_send_custom_topology(); + afe_send_port_topology_id(port_id); + afe_send_cal(port_id); + afe_send_hw_delay(port_id, rate); + } + + /* Start SW MAD module */ + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) { + if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) || + !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) { + pr_err("%s: AFE isn't configured yet for\n" + "HW MAD try Again\n", __func__); + ret = -EAGAIN; + goto fail_cmd; + } + ret = afe_turn_onoff_hw_mad(mad_type, true); + if (ret) { + pr_err("%s: afe_turn_onoff_hw_mad failed %d\n", + __func__, ret); + goto fail_cmd; + } + } + + if ((this_afe.aanc_info.aanc_active) && + (this_afe.aanc_info.aanc_tx_port == port_id)) { + this_afe.aanc_info.aanc_tx_port_sample_rate = rate; + port_index = + afe_get_port_index(this_afe.aanc_info.aanc_rx_port); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.aanc_info.aanc_rx_port_sample_rate = + this_afe.afe_sample_rates[port_index]; + } else { + pr_err("%s: Invalid port index %d\n", + __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port, + this_afe.aanc_info.aanc_rx_port); + pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret); + } + + if ((port_id == AFE_PORT_ID_USB_RX) || + (port_id == AFE_PORT_ID_USB_TX)) { + ret = afe_port_send_usb_dev_param(port_id, afe_config); + if (ret) { + pr_err("%s: AFE device param for port 0x%x failed %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + } + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + + switch (port_id) { + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + cfg_type = AFE_PARAM_ID_PCM_CONFIG; + break; + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case HDMI_RX: + case DISPLAY_PORT_RX: + cfg_type = AFE_PARAM_ID_HDMI_CONFIG; + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + cfg_type = AFE_PARAM_ID_PSEUDO_PORT_CONFIG; + break; + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_5_TX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG; + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG; + break; + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + cfg_type = AFE_PARAM_ID_RT_PROXY_CONFIG; + break; + case INT_BT_SCO_RX: + case INT_BT_A2DP_RX: + case INT_BT_SCO_TX: + case INT_FM_RX: + case INT_FM_TX: + cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG; + break; + default: + pr_err("%s: Invalid port id 0x%x\n", __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = cfg_type; + config.pdata.param_size = sizeof(config.port); + + config.port = *afe_config; + if ((enc_format != ASM_MEDIA_FMT_NONE) && + (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) { + config.port.slim_sch.data_format = + AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED; + } + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + + if ((enc_format != ASM_MEDIA_FMT_NONE) && + (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) { + pr_debug("%s: Found AFE encoder support for SLIMBUS enc_format = %d\n", + __func__, enc_format); + ret = q6afe_send_enc_config(port_id, cfg, enc_format, + *afe_config, afe_in_channels, + afe_in_bit_width); + if (ret) { + pr_err("%s: AFE encoder config for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = rate; + /* + * If afe_port_start() for tx port called before + * rx port, then aanc rx sample rate is zero. So, + * AANC state machine in AFE will not get triggered. + * Make sure to check whether aanc is active during + * afe_port_start() for rx port and if aanc rx + * sample rate is zero, call afe_aanc_start to configure + * aanc with valid sample rates. + */ + if (this_afe.aanc_info.aanc_active && + !this_afe.aanc_info.aanc_rx_port_sample_rate) { + this_afe.aanc_info.aanc_rx_port_sample_rate = + this_afe.afe_sample_rates[port_index]; + ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port, + this_afe.aanc_info.aanc_rx_port); + pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret); + } + } else { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + ret = afe_send_cmd_port_start(port_id); + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +/** + * afe_port_start - to configure AFE session with + * specified port configuration + * + * @port_id: AFE port id number + * @afe_config: port configutation + * @rate: sampling rate of port + * + * Returns 0 on success or error value on port start failure. + */ +int afe_port_start(u16 port_id, union afe_port_config *afe_config, + u32 rate) +{ + return __afe_port_start(port_id, afe_config, rate, + 0, 0, NULL, ASM_MEDIA_FMT_NONE); +} +EXPORT_SYMBOL(afe_port_start); + +/** + * afe_port_start_v2 - to configure AFE session with + * specified port configuration and encoder params + * + * @port_id: AFE port id number + * @afe_config: port configutation + * @rate: sampling rate of port + * @cfg: AFE encoder configuration information to setup encoder + * @afe_in_channels: AFE input channel configuration, this needs + * update only if input channel is differ from AFE output + * + * Returns 0 on success or error value on port start failure. + */ +int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config, + u32 rate, u16 afe_in_channels, u16 afe_in_bit_width, + struct afe_enc_config *enc_cfg) +{ + return __afe_port_start(port_id, afe_config, rate, + afe_in_channels, afe_in_bit_width, + &enc_cfg->data, enc_cfg->format); +} +EXPORT_SYMBOL(afe_port_start_v2); + +int afe_get_port_index(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX; + case AFE_PORT_ID_PRIMARY_PCM_RX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_RX; + case AFE_PORT_ID_PRIMARY_PCM_TX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_TX; + case AFE_PORT_ID_SECONDARY_PCM_RX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_RX; + case AFE_PORT_ID_SECONDARY_PCM_TX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_TX; + case AFE_PORT_ID_TERTIARY_PCM_RX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_RX; + case AFE_PORT_ID_TERTIARY_PCM_TX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_TX; + case AFE_PORT_ID_QUATERNARY_PCM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX; + case AFE_PORT_ID_QUATERNARY_PCM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX; + case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX; + case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX; + case MI2S_RX: return IDX_MI2S_RX; + case MI2S_TX: return IDX_MI2S_TX; + case HDMI_RX: return IDX_HDMI_RX; + case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX; + case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return IDX_DIGI_MIC_TX; + case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX; + case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX; + case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX; + case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX; + case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX; + case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX; + case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX; + case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX; + case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX; + case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX; + case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX; + case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX; + case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX; + case INT_FM_RX: return IDX_INT_FM_RX; + case INT_FM_TX: return IDX_INT_FM_TX; + case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX; + case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX; + case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX; + case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX; + case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX; + case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX; + case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX; + case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX; + case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX; + case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX; + case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX; + case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX; + case AFE_PORT_ID_USB_RX: return IDX_AFE_PORT_ID_USB_RX; + case AFE_PORT_ID_USB_TX: return IDX_AFE_PORT_ID_USB_TX; + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX; + case AFE_PORT_ID_PRIMARY_MI2S_TX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX; + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_TX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX; + case AFE_PORT_ID_TERTIARY_MI2S_RX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + case AFE_PORT_ID_QUINARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUINARY_MI2S_RX; + case AFE_PORT_ID_QUINARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUINARY_MI2S_TX; + case AFE_PORT_ID_SENARY_MI2S_TX: + return IDX_AFE_PORT_ID_SENARY_MI2S_TX; + case AFE_PORT_ID_PRIMARY_TDM_RX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7; + default: + pr_err("%s: port 0x%x\n", __func__, port_id); + return -EINVAL; + } +} + +int afe_open(u16 port_id, + union afe_port_config *afe_config, int rate) +{ + struct afe_port_cmd_device_start start; + struct afe_audioif_config_command config; + int ret = 0; + int cfg_type; + int index = 0; + + if (!afe_config) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + pr_err("%s: port_id 0x%x rate %d\n", __func__, port_id, rate); + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", __func__, port_id, ret); + return -EINVAL; + } + + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) { + pr_err("%s: wrong port 0x%x\n", __func__, port_id); + return -EINVAL; + } + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) + port_id = VIRTUAL_ID_TO_PORTID(port_id); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return -EINVAL; + } + /* Also send the topology id here: */ + afe_send_custom_topology(); /* One time call: only for first time */ + afe_send_port_topology_id(port_id); + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + mutex_lock(&this_afe.afe_cmd_lock); + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = index; + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + cfg_type = AFE_PARAM_ID_PCM_CONFIG; + break; + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case HDMI_RX: + case DISPLAY_PORT_RX: + cfg_type = AFE_PARAM_ID_HDMI_CONFIG; + break; + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG; + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG; + break; + default: + pr_err("%s: Invalid port id 0x%x\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = q6audio_get_port_id(port_id); + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) + - sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = cfg_type; + config.pdata.param_size = sizeof(config.port); + + config.port = *afe_config; + pr_debug("%s: param PL size=%d iparam_size[%d][%zd %zd %zd %zd] param_id[0x%x]\n", + __func__, config.param.payload_size, config.pdata.param_size, + sizeof(config), sizeof(config.param), sizeof(config.port), + sizeof(struct apr_hdr), config.pdata.param_id); + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x opcode[0x%x]failed %d\n", + __func__, port_id, cfg_type, ret); + goto fail_cmd; + } + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = index; + start.hdr.opcode = AFE_PORT_CMD_DEVICE_START; + start.port_id = q6audio_get_port_id(port_id); + pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n", + __func__, start.hdr.opcode, start.port_id); + + ret = afe_apr_send_pkt(&start, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__, + port_id, ret); + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +int afe_loopback(u16 enable, u16 rx_port, u16 tx_port) +{ + struct afe_loopback_cfg_v1 lb_cmd; + int ret = 0; + int index = 0; + + if (rx_port == MI2S_RX) + rx_port = AFE_PORT_ID_PRIMARY_MI2S_RX; + if (tx_port == MI2S_TX) + tx_port = AFE_PORT_ID_PRIMARY_MI2S_TX; + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + index = q6audio_get_port_index(rx_port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(rx_port); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", __func__, rx_port, ret); + return -EINVAL; + } + + lb_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + lb_cmd.hdr.pkt_size = sizeof(lb_cmd); + lb_cmd.hdr.src_port = 0; + lb_cmd.hdr.dest_port = 0; + lb_cmd.hdr.token = index; + lb_cmd.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + lb_cmd.param.port_id = tx_port; + lb_cmd.param.payload_size = (sizeof(lb_cmd) - sizeof(struct apr_hdr) - + sizeof(struct afe_port_cmd_set_param_v2)); + lb_cmd.param.payload_address_lsw = 0x00; + lb_cmd.param.payload_address_msw = 0x00; + lb_cmd.param.mem_map_handle = 0x00; + lb_cmd.pdata.module_id = AFE_MODULE_LOOPBACK; + lb_cmd.pdata.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG; + lb_cmd.pdata.param_size = lb_cmd.param.payload_size - + sizeof(struct afe_port_param_data_v2); + + lb_cmd.dst_port_id = rx_port; + lb_cmd.routing_mode = LB_MODE_DEFAULT; + lb_cmd.enable = (enable ? 1 : 0); + lb_cmd.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG; + + ret = afe_apr_send_pkt(&lb_cmd, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE loopback failed %d\n", __func__, ret); + return ret; +} + +int afe_loopback_gain(u16 port_id, u16 volume) +{ + struct afe_loopback_gain_per_path_param set_param; + int ret = 0; + int index = 0; + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", + __func__, port_id, ret); + return -EINVAL; + } + + /* RX ports numbers are even .TX ports numbers are odd. */ + if (port_id % 2 == 0) { + pr_err("%s: Failed : afe loopback gain only for TX ports. port_id %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: port 0x%x volume %d\n", __func__, port_id, volume); + + set_param.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + set_param.hdr.pkt_size = sizeof(set_param); + set_param.hdr.src_port = 0; + set_param.hdr.dest_port = 0; + set_param.hdr.token = index; + set_param.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + set_param.param.port_id = port_id; + set_param.param.payload_size = + (sizeof(struct afe_loopback_gain_per_path_param) - + sizeof(struct apr_hdr) - sizeof(struct afe_port_cmd_set_param_v2)); + set_param.param.payload_address_lsw = 0; + set_param.param.payload_address_msw = 0; + set_param.param.mem_map_handle = 0; + + set_param.pdata.module_id = AFE_MODULE_LOOPBACK; + set_param.pdata.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH; + set_param.pdata.param_size = + (set_param.param.payload_size - + sizeof(struct afe_port_param_data_v2)); + set_param.rx_port_id = port_id; + set_param.gain = volume; + + ret = afe_apr_send_pkt(&set_param, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE param set failed for port 0x%x ret %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +int afe_pseudo_port_start_nowait(u16 port_id) +{ + struct afe_pseudoport_start_command start; + int ret = 0; + + pr_debug("%s: port_id=0x%x\n", __func__, port_id); + if (this_afe.apr == NULL) { + pr_err("%s: AFE APR is not registered\n", __func__); + return -ENODEV; + } + + + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = 0; + start.hdr.opcode = AFE_PSEUDOPORT_CMD_START; + start.port_id = port_id; + start.timing = 1; + + ret = afe_apr_send_pkt(&start, NULL); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; + } + return 0; +} + +int afe_start_pseudo_port(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_start_command start; + int index = 0; + + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", + __func__, port_id, ret); + return -EINVAL; + } + + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = 0; + start.hdr.opcode = AFE_PSEUDOPORT_CMD_START; + start.port_id = port_id; + start.timing = 1; + start.hdr.token = index; + + ret = afe_apr_send_pkt(&start, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE enable for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; +} + +int afe_pseudo_port_stop_nowait(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_stop_command stop; + int index = 0; + + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + return -EINVAL; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", + __func__, port_id, ret); + return -EINVAL; + } + + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = 0; + stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP; + stop.port_id = port_id; + stop.reserved = 0; + stop.hdr.token = index; + + ret = afe_apr_send_pkt(&stop, NULL); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + + return ret; +} + +int afe_port_group_set_param(u16 group_id, + union afe_port_group_config *afe_group_config) +{ + int ret; + struct afe_port_group_create config; + int cfg_type; + + if (!afe_group_config) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: group id: 0x%x\n", __func__, group_id); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + switch (group_id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX: + cfg_type = AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG; + break; + default: + pr_err("%s: Invalid group id 0x%x\n", __func__, group_id); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_GROUP_DEVICE; + config.pdata.param_id = cfg_type; + config.pdata.param_size = sizeof(config.data); + config.data = *afe_group_config; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) + pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_CFG failed %d\n", + __func__, ret); + + return ret; +} + +int afe_port_group_enable(u16 group_id, + union afe_port_group_config *afe_group_config, + u16 enable) +{ + int ret; + struct afe_port_group_create config; + + pr_debug("%s: group id: 0x%x enable: %d\n", __func__, + group_id, enable); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + if (enable) { + ret = afe_port_group_set_param(group_id, afe_group_config); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + return ret; + } + } + + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_GROUP_DEVICE; + config.pdata.param_id = AFE_PARAM_ID_GROUP_DEVICE_ENABLE; + config.pdata.param_size = sizeof(config.data); + config.data.group_enable.group_id = group_id; + config.data.group_enable.enable = enable; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (ret) + pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_ENABLE failed %d\n", + __func__, ret); + + return ret; +} + +int afe_stop_pseudo_port(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_stop_command stop; + int index = 0; + + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + return -EINVAL; + } + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = 0; + stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP; + stop.port_id = port_id; + stop.reserved = 0; + stop.hdr.token = index; + + ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + + return ret; +} + +uint32_t afe_req_mmap_handle(struct afe_audio_client *ac) +{ + return ac->mem_map_handle; +} + +struct afe_audio_client *q6afe_audio_client_alloc(void *priv) +{ + struct afe_audio_client *ac; + int lcnt = 0; + + ac = kzalloc(sizeof(struct afe_audio_client), GFP_KERNEL); + if (!ac) + return NULL; + + ac->priv = priv; + + init_waitqueue_head(&ac->cmd_wait); + INIT_LIST_HEAD(&ac->port[0].mem_map_handle); + INIT_LIST_HEAD(&ac->port[1].mem_map_handle); + pr_debug("%s: mem_map_handle list init'ed\n", __func__); + mutex_init(&ac->cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&ac->port[lcnt].lock); + spin_lock_init(&ac->port[lcnt].dsp_lock); + } + atomic_set(&ac->cmd_state, 0); + + return ac; +} + +int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir, + struct afe_audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt) +{ + int cnt = 0; + int rc = 0; + struct afe_audio_buffer *buf; + size_t len; + + if (!(ac) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return -EINVAL; + } + + pr_debug("%s: bufsz[%d]bufcnt[%d]\n", + __func__, + bufsz, bufcnt); + + if (ac->port[dir].buf) { + pr_debug("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + buf = kzalloc(((sizeof(struct afe_audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + pr_err("%s: null buf\n", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + rc = msm_audio_ion_alloc("afe_client", &buf[0].client, + &buf[0].handle, bufsz*bufcnt, + &buf[0].phys, &len, + &buf[0].data); + if (rc) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, rc); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + buf[0].used = dir ^ 1; + buf[0].size = bufsz; + buf[0].actual_size = bufsz; + cnt = 1; + while (cnt < bufcnt) { + if (bufsz > 0) { + buf[cnt].data = buf[0].data + (cnt * bufsz); + buf[cnt].phys = buf[0].phys + (cnt * bufsz); + if (!buf[cnt].data) { + pr_err("%s: Buf alloc failed\n", + __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf[cnt].used = dir ^ 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s: data[%pK]phys[%pK][%pK]\n", __func__, + buf[cnt].data, + &buf[cnt].phys, + &buf[cnt].phys); + } + cnt++; + } + ac->port[dir].max_buf_cnt = cnt; + mutex_unlock(&ac->cmd_lock); + return 0; +fail: + pr_err("%s: jump fail\n", __func__); + q6afe_audio_client_buf_free_contiguous(dir, ac); + return -EINVAL; +} + +int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz, + struct afe_audio_client *ac) +{ + int ret = 0; + + mutex_lock(&this_afe.afe_cmd_lock); + ac->mem_map_handle = 0; + ret = afe_cmd_memory_map(dma_addr_p, dma_buf_sz); + if (ret < 0) { + pr_err("%s: afe_cmd_memory_map failed %d\n", + __func__, ret); + + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; + } + ac->mem_map_handle = this_afe.mmap_handle; + mutex_unlock(&this_afe.afe_cmd_lock); + + return ret; +} + +int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz) +{ + int ret = 0; + int cmd_size = 0; + void *payload = NULL; + void *mmap_region_cmd = NULL; + struct afe_service_cmd_shared_mem_map_regions *mregion = NULL; + struct afe_service_shared_map_region_payload *mregion_pl = NULL; + int index = 0; + + pr_debug("%s:\n", __func__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + if (dma_buf_sz % SZ_4K != 0) { + /* + * The memory allocated by msm_audio_ion_alloc is always 4kB + * aligned, ADSP expects the size to be 4kB aligned as well + * so re-adjusts the buffer size before passing to ADSP. + */ + dma_buf_sz = PAGE_ALIGN(dma_buf_sz); + } + + cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions) + + sizeof(struct afe_service_shared_map_region_payload); + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) + return -ENOMEM; + + mregion = (struct afe_service_cmd_shared_mem_map_regions *) + mmap_region_cmd; + mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mregion->hdr.pkt_size = cmd_size; + mregion->hdr.src_port = 0; + mregion->hdr.dest_port = 0; + mregion->hdr.token = 0; + mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS; + mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mregion->num_regions = 1; + mregion->property_flag = 0x00; + /* Todo */ + index = mregion->hdr.token = IDX_RSVD_2; + + payload = ((u8 *) mmap_region_cmd + + sizeof(struct afe_service_cmd_shared_mem_map_regions)); + + mregion_pl = (struct afe_service_shared_map_region_payload *)payload; + + mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p); + mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p); + mregion_pl->mem_size_bytes = dma_buf_sz; + + pr_debug("%s: dma_addr_p 0x%pK , size %d\n", __func__, + &dma_addr_p, dma_buf_sz); + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + this_afe.mmap_handle = 0; + ret = apr_send_pkt(this_afe.apr, (uint32_t *) mmap_region_cmd); + if (ret < 0) { + pr_err("%s: AFE memory map cmd failed %d\n", + __func__, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + + kfree(mmap_region_cmd); + return 0; +fail_cmd: + kfree(mmap_region_cmd); + pr_err("%s: fail_cmd\n", __func__); + return ret; +} + +int afe_cmd_memory_map_nowait(int port_id, phys_addr_t dma_addr_p, + u32 dma_buf_sz) +{ + int ret = 0; + int cmd_size = 0; + void *payload = NULL; + void *mmap_region_cmd = NULL; + struct afe_service_cmd_shared_mem_map_regions *mregion = NULL; + struct afe_service_shared_map_region_payload *mregion_pl = NULL; + int index = 0; + + pr_debug("%s:\n", __func__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", + __func__, port_id, ret); + return -EINVAL; + } + + cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions) + + sizeof(struct afe_service_shared_map_region_payload); + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) + return -ENOMEM; + + mregion = (struct afe_service_cmd_shared_mem_map_regions *) + mmap_region_cmd; + mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mregion->hdr.pkt_size = sizeof(mregion); + mregion->hdr.src_port = 0; + mregion->hdr.dest_port = 0; + mregion->hdr.token = 0; + mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS; + mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mregion->num_regions = 1; + mregion->property_flag = 0x00; + + payload = ((u8 *) mmap_region_cmd + + sizeof(struct afe_service_cmd_shared_mem_map_regions)); + mregion_pl = (struct afe_service_shared_map_region_payload *)payload; + + mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p); + mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p); + mregion_pl->mem_size_bytes = dma_buf_sz; + + ret = afe_apr_send_pkt(mmap_region_cmd, NULL); + if (ret) + pr_err("%s: AFE memory map cmd failed %d\n", + __func__, ret); + kfree(mmap_region_cmd); + return ret; +} +int q6afe_audio_client_buf_free_contiguous(unsigned int dir, + struct afe_audio_client *ac) +{ + struct afe_audio_port_data *port; + int cnt = 0; + + mutex_lock(&ac->cmd_lock); + port = &ac->port[dir]; + if (!port->buf) { + pr_err("%s: buf is null\n", __func__); + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (port->buf[0].data) { + pr_debug("%s: data[%pK]phys[%pK][%pK] , client[%pK] handle[%pK]\n", + __func__, + port->buf[0].data, + &port->buf[0].phys, + &port->buf[0].phys, + port->buf[0].client, + port->buf[0].handle); + msm_audio_ion_free(port->buf[0].client, port->buf[0].handle); + port->buf[0].client = NULL; + port->buf[0].handle = NULL; + } + + while (cnt >= 0) { + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + cnt--; + } + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + mutex_unlock(&ac->cmd_lock); + return 0; +} + +void q6afe_audio_client_free(struct afe_audio_client *ac) +{ + int loopcnt; + struct afe_audio_port_data *port; + + if (!ac) { + pr_err("%s: audio client is NULL\n", __func__); + return; + } + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + if (!port->buf) + continue; + pr_debug("%s: loopcnt = %d\n", __func__, loopcnt); + q6afe_audio_client_buf_free_contiguous(loopcnt, ac); + } + kfree(ac); +} + +int afe_cmd_memory_unmap(u32 mem_map_handle) +{ + int ret = 0; + struct afe_service_cmd_shared_mem_unmap_regions mregion; + int index = 0; + + pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mregion.hdr.pkt_size = sizeof(mregion); + mregion.hdr.src_port = 0; + mregion.hdr.dest_port = 0; + mregion.hdr.token = 0; + mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS; + mregion.mem_map_handle = mem_map_handle; + + /* Todo */ + index = mregion.hdr.token = IDX_RSVD_2; + + atomic_set(&this_afe.status, 0); + ret = afe_apr_send_pkt(&mregion, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE memory unmap cmd failed %d\n", + __func__, ret); + + return ret; +} + +int afe_cmd_memory_unmap_nowait(u32 mem_map_handle) +{ + int ret = 0; + struct afe_service_cmd_shared_mem_unmap_regions mregion; + + pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mregion.hdr.pkt_size = sizeof(mregion); + mregion.hdr.src_port = 0; + mregion.hdr.dest_port = 0; + mregion.hdr.token = 0; + mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS; + mregion.mem_map_handle = mem_map_handle; + + ret = afe_apr_send_pkt(&mregion, NULL); + if (ret) + pr_err("%s: AFE memory unmap cmd failed %d\n", + __func__, ret); + return ret; +} + +int afe_register_get_events(u16 port_id, + void (*cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv), + void *private_data) +{ + int ret = 0; + struct afe_service_cmd_register_rt_port_driver rtproxy; + + pr_debug("%s: port_id: 0x%x\n", __func__, port_id); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + port_id = VIRTUAL_ID_TO_PORTID(port_id); + } else { + pr_err("%s: wrong port id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (port_id == RT_PROXY_PORT_001_TX) { + this_afe.tx_cb = cb; + this_afe.tx_private_data = private_data; + } else if (port_id == RT_PROXY_PORT_001_RX) { + this_afe.rx_cb = cb; + this_afe.rx_private_data = private_data; + } + + rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + rtproxy.hdr.pkt_size = sizeof(rtproxy); + rtproxy.hdr.src_port = 1; + rtproxy.hdr.dest_port = 1; + rtproxy.hdr.opcode = AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER; + rtproxy.port_id = port_id; + rtproxy.reserved = 0; + + ret = afe_apr_send_pkt(&rtproxy, NULL); + if (ret) + pr_err("%s: AFE reg. rtproxy_event failed %d\n", + __func__, ret); + return ret; +} + +int afe_unregister_get_events(u16 port_id) +{ + int ret = 0; + struct afe_service_cmd_unregister_rt_port_driver rtproxy; + int index = 0; + + pr_debug("%s:\n", __func__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + port_id = VIRTUAL_ID_TO_PORTID(port_id); + } else { + pr_err("%s: wrong port id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x ret %d", __func__, port_id, ret); + return -EINVAL; + } + + rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + rtproxy.hdr.pkt_size = sizeof(rtproxy); + rtproxy.hdr.src_port = 0; + rtproxy.hdr.dest_port = 0; + rtproxy.hdr.token = 0; + rtproxy.hdr.opcode = AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER; + rtproxy.port_id = port_id; + rtproxy.reserved = 0; + + rtproxy.hdr.token = index; + + if (port_id == RT_PROXY_PORT_001_TX) { + this_afe.tx_cb = NULL; + this_afe.tx_private_data = NULL; + } else if (port_id == RT_PROXY_PORT_001_RX) { + this_afe.rx_cb = NULL; + this_afe.rx_private_data = NULL; + } + + ret = afe_apr_send_pkt(&rtproxy, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE enable Unreg. rtproxy_event failed %d\n", + __func__, ret); + return ret; +} + +int afe_rt_proxy_port_write(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes) +{ + int ret = 0; + struct afe_port_data_cmd_rt_proxy_port_write_v2 afecmd_wr; + + if (this_afe.apr == NULL) { + pr_err("%s: register to AFE is not done\n", __func__); + ret = -ENODEV; + return ret; + } + pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__, + &buf_addr_p, bytes); + + afecmd_wr.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afecmd_wr.hdr.pkt_size = sizeof(afecmd_wr); + afecmd_wr.hdr.src_port = 0; + afecmd_wr.hdr.dest_port = 0; + afecmd_wr.hdr.token = 0; + afecmd_wr.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2; + afecmd_wr.port_id = RT_PROXY_PORT_001_TX; + afecmd_wr.buffer_address_lsw = lower_32_bits(buf_addr_p); + afecmd_wr.buffer_address_msw = + msm_audio_populate_upper_32_bits(buf_addr_p); + afecmd_wr.mem_map_handle = mem_map_handle; + afecmd_wr.available_bytes = bytes; + afecmd_wr.reserved = 0; + + ret = afe_apr_send_pkt(&afecmd_wr, NULL); + if (ret) + pr_err("%s: AFE rtproxy write to port 0x%x failed %d\n", + __func__, afecmd_wr.port_id, ret); + return ret; + +} + +int afe_rt_proxy_port_read(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes) +{ + int ret = 0; + struct afe_port_data_cmd_rt_proxy_port_read_v2 afecmd_rd; + + if (this_afe.apr == NULL) { + pr_err("%s: register to AFE is not done\n", __func__); + ret = -ENODEV; + return ret; + } + pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__, + &buf_addr_p, bytes); + + afecmd_rd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afecmd_rd.hdr.pkt_size = sizeof(afecmd_rd); + afecmd_rd.hdr.src_port = 0; + afecmd_rd.hdr.dest_port = 0; + afecmd_rd.hdr.token = 0; + afecmd_rd.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2; + afecmd_rd.port_id = RT_PROXY_PORT_001_RX; + afecmd_rd.buffer_address_lsw = lower_32_bits(buf_addr_p); + afecmd_rd.buffer_address_msw = + msm_audio_populate_upper_32_bits(buf_addr_p); + afecmd_rd.available_bytes = bytes; + afecmd_rd.mem_map_handle = mem_map_handle; + + ret = afe_apr_send_pkt(&afecmd_rd, NULL); + if (ret) + pr_err("%s: AFE rtproxy read cmd to port 0x%x failed %d\n", + __func__, afecmd_rd.port_id, ret); + return ret; +} + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_afelb; +static struct dentry *debugfs_afelb_gain; + +static int afe_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_info("%s: debug intf %s\n", __func__, (char *) file->private_data); + return 0; +} + +static int afe_get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtoul(token, base, ¶m1[cnt]) != 0) { + pr_err("%s: kstrtoul failed\n", + __func__); + return -EINVAL; + } + + token = strsep(&buf, " "); + } else { + pr_err("%s: token NULL\n", __func__); + return -EINVAL; + } + } + return 0; +} +#define AFE_LOOPBACK_ON (1) +#define AFE_LOOPBACK_OFF (0) +static ssize_t afe_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char lbuf[32]; + int rc; + unsigned long param[5]; + + if (cnt > sizeof(lbuf) - 1) { + pr_err("%s: cnt %zd size %zd\n", __func__, cnt, sizeof(lbuf)-1); + return -EINVAL; + } + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) { + pr_err("%s: copy from user failed %d\n", __func__, rc); + return -EFAULT; + } + + lbuf[cnt] = '\0'; + + if (!strcmp(lb_str, "afe_loopback")) { + rc = afe_get_parameters(lbuf, param, 3); + if (!rc) { + pr_info("%s: %lu %lu %lu\n", lb_str, param[0], param[1], + param[2]); + + if ((param[0] != AFE_LOOPBACK_ON) && (param[0] != + AFE_LOOPBACK_OFF)) { + pr_err("%s: Error, parameter 0 incorrect\n", + __func__); + rc = -EINVAL; + goto afe_error; + } + if ((q6audio_validate_port(param[1]) < 0) || + (q6audio_validate_port(param[2])) < 0) { + pr_err("%s: Error, invalid afe port\n", + __func__); + } + if (this_afe.apr == NULL) { + pr_err("%s: Error, AFE not opened\n", __func__); + rc = -EINVAL; + } else { + rc = afe_loopback(param[0], param[1], param[2]); + } + } else { + pr_err("%s: Error, invalid parameters\n", __func__); + rc = -EINVAL; + } + + } else if (!strcmp(lb_str, "afe_loopback_gain")) { + rc = afe_get_parameters(lbuf, param, 2); + if (!rc) { + pr_info("%s: %s %lu %lu\n", + __func__, lb_str, param[0], param[1]); + + rc = q6audio_validate_port(param[0]); + if (rc < 0) { + pr_err("%s: Error, invalid afe port %d %lu\n", + __func__, rc, param[0]); + rc = -EINVAL; + goto afe_error; + } + + if (param[1] > 100) { + pr_err("%s: Error, volume should be 0 to 100 percentage param = %lu\n", + __func__, param[1]); + rc = -EINVAL; + goto afe_error; + } + + param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100; + + if (this_afe.apr == NULL) { + pr_err("%s: Error, AFE not opened\n", __func__); + rc = -EINVAL; + } else { + rc = afe_loopback_gain(param[0], param[1]); + } + } else { + pr_err("%s: Error, invalid parameters\n", __func__); + rc = -EINVAL; + } + } + +afe_error: + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations afe_debug_fops = { + .open = afe_debug_open, + .write = afe_debug_write +}; + +static void config_debug_fs_init(void) +{ + debugfs_afelb = debugfs_create_file("afe_loopback", + 0664, NULL, (void *) "afe_loopback", + &afe_debug_fops); + + debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain", + 0664, NULL, (void *) "afe_loopback_gain", + &afe_debug_fops); +} +static void config_debug_fs_exit(void) +{ + debugfs_remove(debugfs_afelb); + debugfs_remove(debugfs_afelb_gain); +} +#else +static void config_debug_fs_init(void) +{ +} +static void config_debug_fs_exit(void) +{ +} +#endif + +void afe_set_dtmf_gen_rx_portid(u16 port_id, int set) +{ + if (set) + this_afe.dtmf_gen_rx_portid = port_id; + else if (this_afe.dtmf_gen_rx_portid == port_id) + this_afe.dtmf_gen_rx_portid = -1; +} + +int afe_dtmf_generate_rx(int64_t duration_in_ms, + uint16_t high_freq, + uint16_t low_freq, uint16_t gain) +{ + int ret = 0; + int index = 0; + struct afe_dtmf_generation_command cmd_dtmf; + + pr_debug("%s: DTMF AFE Gen\n", __func__); + + if (afe_validate_port(this_afe.dtmf_gen_rx_portid) < 0) { + pr_err("%s: Failed : Invalid Port id = 0x%x\n", + __func__, this_afe.dtmf_gen_rx_portid); + ret = -EINVAL; + goto fail_cmd; + } + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + pr_debug("%s: dur=%lld: hfreq=%d lfreq=%d gain=%d portid=0x%x\n", + __func__, + duration_in_ms, high_freq, low_freq, gain, + this_afe.dtmf_gen_rx_portid); + + cmd_dtmf.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd_dtmf.hdr.pkt_size = sizeof(cmd_dtmf); + cmd_dtmf.hdr.src_port = 0; + cmd_dtmf.hdr.dest_port = 0; + cmd_dtmf.hdr.token = 0; + cmd_dtmf.hdr.opcode = AFE_PORTS_CMD_DTMF_CTL; + cmd_dtmf.duration_in_ms = duration_in_ms; + cmd_dtmf.high_freq = high_freq; + cmd_dtmf.low_freq = low_freq; + cmd_dtmf.gain = gain; + cmd_dtmf.num_ports = 1; + cmd_dtmf.port_ids = q6audio_get_port_id(this_afe.dtmf_gen_rx_portid); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_dtmf); + if (ret < 0) { + pr_err("%s: AFE DTMF failed for num_ports:%d ids:0x%x\n", + __func__, cmd_dtmf.num_ports, cmd_dtmf.port_ids); + ret = -EINVAL; + goto fail_cmd; + } + index = q6audio_get_port_index(this_afe.dtmf_gen_rx_portid); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + return 0; + +fail_cmd: + pr_err("%s: failed %d\n", __func__, ret); + return ret; +} + +int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain) +{ + struct afe_loopback_cfg_v1 cmd_sidetone; + int ret = 0; + int index = 0; + + pr_info("%s: tx_port_id: 0x%x rx_port_id: 0x%x enable:%d gain:%d\n", + __func__, tx_port_id, rx_port_id, enable, gain); + index = q6audio_get_port_index(rx_port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(rx_port_id); + if (ret < 0) { + pr_err("%s: Invalid port 0x%x %d", __func__, rx_port_id, ret); + return -EINVAL; + } + + cmd_sidetone.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd_sidetone.hdr.pkt_size = sizeof(cmd_sidetone); + cmd_sidetone.hdr.src_port = 0; + cmd_sidetone.hdr.dest_port = 0; + cmd_sidetone.hdr.token = 0; + cmd_sidetone.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + /* should it be rx or tx port id ?? , bharath*/ + cmd_sidetone.param.port_id = tx_port_id; + /* size of data param & payload */ + cmd_sidetone.param.payload_size = (sizeof(cmd_sidetone) - + sizeof(struct apr_hdr) - + sizeof(struct afe_port_cmd_set_param_v2)); + cmd_sidetone.param.payload_address_lsw = 0x00; + cmd_sidetone.param.payload_address_msw = 0x00; + cmd_sidetone.param.mem_map_handle = 0x00; + cmd_sidetone.pdata.module_id = AFE_MODULE_LOOPBACK; + cmd_sidetone.pdata.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG; + /* size of actual payload only */ + cmd_sidetone.pdata.param_size = cmd_sidetone.param.payload_size - + sizeof(struct afe_port_param_data_v2); + + cmd_sidetone.loopback_cfg_minor_version = + AFE_API_VERSION_LOOPBACK_CONFIG; + cmd_sidetone.dst_port_id = rx_port_id; + cmd_sidetone.routing_mode = LB_MODE_SIDETONE; + cmd_sidetone.enable = enable; + + ret = afe_apr_send_pkt(&cmd_sidetone, &this_afe.wait[index]); + if (ret) + pr_err("%s: sidetone failed tx_port:0x%x rx_port:0x%x ret%d\n", + __func__, tx_port_id, rx_port_id, ret); + return ret; +} + +int afe_validate_port(u16 port_id) +{ + int ret; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case HDMI_RX: + case DISPLAY_PORT_RX: + case AFE_PORT_ID_SPDIF_RX: + case RSVD_2: + case RSVD_3: + case DIGI_MIC_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case INT_BT_SCO_RX: + case INT_BT_SCO_TX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case INT_FM_TX: + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + { + ret = 0; + break; + } + + default: + pr_err("%s: default ret 0x%x\n", __func__, port_id); + ret = -EINVAL; + } + + return ret; +} + +int afe_convert_virtual_to_portid(u16 port_id) +{ + int ret; + + /* + * if port_id is virtual, convert to physical.. + * if port_id is already physical, return physical + */ + if (afe_validate_port(port_id) < 0) { + if (port_id == RT_PROXY_DAI_001_RX || + port_id == RT_PROXY_DAI_001_TX || + port_id == RT_PROXY_DAI_002_RX || + port_id == RT_PROXY_DAI_002_TX) { + ret = VIRTUAL_ID_TO_PORTID(port_id); + } else { + pr_err("%s: wrong port 0x%x\n", + __func__, port_id); + ret = -EINVAL; + } + } else + ret = port_id; + + return ret; +} +int afe_port_stop_nowait(int port_id) +{ + struct afe_port_cmd_device_stop stop; + int ret = 0; + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + port_id = q6audio_convert_virtual_to_portid(port_id); + + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = 0; + stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP; + stop.port_id = port_id; + stop.reserved = 0; + + ret = afe_apr_send_pkt(&stop, NULL); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + +fail_cmd: + return ret; + +} + +int afe_close(int port_id) +{ + struct afe_port_cmd_device_stop stop; + enum afe_mad_type mad_type; + int ret = 0; + int index = 0; + uint16_t port_index; + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) + pcm_afe_instance[port_id & 0x1] = 0; + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) + proxy_afe_instance[port_id & 0x1] = 0; + afe_close_done[port_id & 0x1] = true; + ret = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) { + pr_debug("%s: before decrementing pcm_afe_instance %d\n", + __func__, pcm_afe_instance[port_id & 0x1]); + port_id = VIRTUAL_ID_TO_PORTID(port_id); + pcm_afe_instance[port_id & 0x1]--; + if ((!(pcm_afe_instance[port_id & 0x1] == 0 && + proxy_afe_instance[port_id & 0x1] == 0)) || + afe_close_done[port_id & 0x1] == true) + return 0; + + afe_close_done[port_id & 0x1] = true; + } + + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + pr_debug("%s: before decrementing proxy_afe_instance %d\n", + __func__, proxy_afe_instance[port_id & 0x1]); + port_id = VIRTUAL_ID_TO_PORTID(port_id); + proxy_afe_instance[port_id & 0x1]--; + if ((!(pcm_afe_instance[port_id & 0x1] == 0 && + proxy_afe_instance[port_id & 0x1] == 0)) || + afe_close_done[port_id & 0x1] == true) + return 0; + + afe_close_done[port_id & 0x1] = true; + } + + port_id = q6audio_convert_virtual_to_portid(port_id); + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_warn("%s: Not a valid port id 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) { + pr_debug("%s: Turn off MAD\n", __func__); + ret = afe_turn_onoff_hw_mad(mad_type, false); + if (ret) { + pr_err("%s: afe_turn_onoff_hw_mad failed %d\n", + __func__, ret); + return ret; + } + } else { + pr_debug("%s: Not a MAD port\n", __func__); + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = 0; + this_afe.topology[port_index] = 0; + } else { + pr_err("%s: port %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + + if ((port_id == this_afe.aanc_info.aanc_tx_port) && + (this_afe.aanc_info.aanc_active)) { + memset(&this_afe.aanc_info, 0x00, sizeof(this_afe.aanc_info)); + ret = afe_aanc_mod_enable(this_afe.apr, port_id, 0); + if (ret) + pr_err("%s: AFE mod disable failed %d\n", + __func__, ret); + } + + /* + * even if ramp down configuration failed it is not serious enough to + * warrant bailaing out. + */ + if (afe_spk_ramp_dn_cfg(port_id) < 0) + pr_err("%s: ramp down configuration failed\n", __func__); + + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = index; + stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP; + stop.port_id = q6audio_get_port_id(port_id); + stop.reserved = 0; + + ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + +fail_cmd: + return ret; +} + +int afe_set_digital_codec_core_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg) +{ + struct afe_lpass_digital_clk_config_command clk_cfg; + int index = 0; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + clk_cfg.hdr.pkt_size = sizeof(clk_cfg); + clk_cfg.hdr.src_port = 0; + clk_cfg.hdr.dest_port = 0; + clk_cfg.hdr.token = index; + + clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + /*default rx port is taken to enable the codec digital clock*/ + clk_cfg.param.port_id = q6audio_get_port_id(port_id); + clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr) + - sizeof(clk_cfg.param); + clk_cfg.param.payload_address_lsw = 0x00; + clk_cfg.param.payload_address_msw = 0x00; + clk_cfg.param.mem_map_handle = 0x00; + clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + clk_cfg.pdata.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG; + clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg); + clk_cfg.clk_cfg = *cfg; + + pr_debug("%s: Minor version =0x%x clk val = %d\n" + "clk root = 0x%x resrv = 0x%x\n", + __func__, cfg->i2s_cfg_minor_version, + cfg->clk_val, cfg->clk_root, cfg->reserved); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg); + if (ret < 0) { + pr_err("%s: AFE enable for port 0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg) +{ + struct afe_lpass_clk_config_command clk_cfg; + int index = 0; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + clk_cfg.hdr.pkt_size = sizeof(clk_cfg); + clk_cfg.hdr.src_port = 0; + clk_cfg.hdr.dest_port = 0; + clk_cfg.hdr.token = index; + + clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + clk_cfg.param.port_id = q6audio_get_port_id(port_id); + clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr) + - sizeof(clk_cfg.param); + clk_cfg.param.payload_address_lsw = 0x00; + clk_cfg.param.payload_address_msw = 0x00; + clk_cfg.param.mem_map_handle = 0x00; + clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + clk_cfg.pdata.param_id = AFE_PARAM_ID_LPAIF_CLK_CONFIG; + clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg); + clk_cfg.clk_cfg = *cfg; + + pr_debug("%s: Minor version =0x%x clk val1 = %d\n" + "clk val2 = %d, clk src = 0x%x\n" + "clk root = 0x%x clk mode = 0x%x resrv = 0x%x\n" + "port id = 0x%x\n", + __func__, cfg->i2s_cfg_minor_version, + cfg->clk_val1, cfg->clk_val2, cfg->clk_src, + cfg->clk_root, cfg->clk_set_mode, + cfg->reserved, q6audio_get_port_id(port_id)); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg); + if (ret < 0) { + pr_err("%s: AFE enable for port 0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg) +{ + struct afe_lpass_clk_config_command_v2 clk_cfg; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: index[%d] invalid!\n", __func__, index); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + clk_cfg.hdr.pkt_size = sizeof(clk_cfg); + clk_cfg.hdr.src_port = 0; + clk_cfg.hdr.dest_port = 0; + clk_cfg.hdr.token = index; + + clk_cfg.hdr.opcode = AFE_SVC_CMD_SET_PARAM; + clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr) + - sizeof(clk_cfg.param); + clk_cfg.param.payload_address_lsw = 0x00; + clk_cfg.param.payload_address_msw = 0x00; + clk_cfg.param.mem_map_handle = 0x00; + clk_cfg.pdata.module_id = AFE_MODULE_CLOCK_SET; + clk_cfg.pdata.param_id = AFE_PARAM_ID_CLOCK_SET; + clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg); + clk_cfg.clk_cfg = *cfg; + + + pr_debug("%s: Minor version =0x%x clk id = %d\n" + "clk freq (Hz) = %d, clk attri = 0x%x\n" + "clk root = 0x%x clk enable = 0x%x\n", + __func__, cfg->clk_set_minor_version, + cfg->clk_id, cfg->clk_freq_in_hz, cfg->clk_attri, + cfg->clk_root, cfg->enable); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg); + if (ret < 0) { + pr_err("%s: AFE clk cfg failed with ret %d\n", + __func__, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } else { + /* set ret to 0 as no timeout happened */ + ret = 0; + } + if (atomic_read(&this_afe.status) != 0) { + pr_err("%s: config cmd failed\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg) +{ + int index = 0; + int ret = 0; + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + ret = afe_set_lpass_clk_cfg(index, cfg); + if (ret) + pr_err("%s: afe_set_lpass_clk_cfg_v2 failed %d\n", + __func__, ret); + + return ret; +} + +int afe_set_lpass_internal_digital_codec_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg) +{ + struct afe_lpass_digital_clk_config_command clk_cfg; + int index = 0; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + clk_cfg.hdr.pkt_size = sizeof(clk_cfg); + clk_cfg.hdr.src_port = 0; + clk_cfg.hdr.dest_port = 0; + clk_cfg.hdr.token = index; + + clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + clk_cfg.param.port_id = q6audio_get_port_id(port_id); + clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr) + - sizeof(clk_cfg.param); + clk_cfg.param.payload_address_lsw = 0x00; + clk_cfg.param.payload_address_msw = 0x00; + clk_cfg.param.mem_map_handle = 0x00; + clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + clk_cfg.pdata.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG; + clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg); + clk_cfg.clk_cfg = *cfg; + + pr_debug("%s: Minor version =0x%x clk val = %d\n" + "clk root = 0x%x resrv = 0x%x port id = 0x%x\n", + __func__, cfg->i2s_cfg_minor_version, + cfg->clk_val, cfg->clk_root, cfg->reserved, + q6audio_get_port_id(port_id)); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg); + if (ret < 0) { + pr_err("%s: AFE enable for port 0x0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable) +{ + struct afe_lpass_core_shared_clk_config_command clk_cfg; + int index = 0; + int ret = 0; + + index = q6audio_get_port_index(port_id); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + return -EINVAL; + } + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + clk_cfg.hdr.pkt_size = sizeof(clk_cfg); + clk_cfg.hdr.src_port = 0; + clk_cfg.hdr.dest_port = 0; + clk_cfg.hdr.token = index; + + clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + clk_cfg.param.port_id = q6audio_get_port_id(port_id); + clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr) + - sizeof(clk_cfg.param); + clk_cfg.param.payload_address_lsw = 0x00; + clk_cfg.param.payload_address_msw = 0x00; + clk_cfg.param.mem_map_handle = 0x00; + clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + clk_cfg.pdata.param_id = AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG; + clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg); + clk_cfg.clk_cfg.lpass_core_shared_clk_cfg_minor_version = + AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG; + clk_cfg.clk_cfg.enable = enable; + + pr_debug("%s: port id = %d, enable = %d\n", + __func__, q6audio_get_port_id(port_id), enable); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg); + if (ret < 0) { + pr_err("%s: AFE enable for port 0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +int q6afe_check_osr_clk_freq(u32 freq) +{ + int ret = 0; + + switch (freq) { + case Q6AFE_LPASS_OSR_CLK_12_P288_MHZ: + case Q6AFE_LPASS_OSR_CLK_8_P192_MHZ: + case Q6AFE_LPASS_OSR_CLK_6_P144_MHZ: + case Q6AFE_LPASS_OSR_CLK_4_P096_MHZ: + case Q6AFE_LPASS_OSR_CLK_3_P072_MHZ: + case Q6AFE_LPASS_OSR_CLK_2_P048_MHZ: + case Q6AFE_LPASS_OSR_CLK_1_P536_MHZ: + case Q6AFE_LPASS_OSR_CLK_1_P024_MHZ: + case Q6AFE_LPASS_OSR_CLK_768_kHZ: + case Q6AFE_LPASS_OSR_CLK_512_kHZ: + break; + default: + pr_err("%s: deafault freq 0x%x\n", + __func__, freq); + ret = -EINVAL; + } + return ret; +} + +int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi) +{ + int ret = -EINVAL; + int index = 0, port = SLIMBUS_4_TX; + + if (!th_vi) { + pr_err("%s: Invalid params\n", __func__); + goto done; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + ret = q6audio_validate_port(port); + if (ret < 0) { + pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret); + goto done; + } + index = q6audio_get_port_index(port); + if (index < 0) { + pr_err("%s: invalid port 0x%x, index %d\n", + __func__, port, index); + ret = -EINVAL; + goto done; + } + th_vi->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + th_vi->hdr.pkt_size = sizeof(*th_vi); + th_vi->hdr.src_port = 0; + th_vi->hdr.dest_port = 0; + th_vi->hdr.token = index; + th_vi->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + th_vi->get_param.mem_map_handle = 0; + th_vi->get_param.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI; + th_vi->get_param.param_id = AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS; + th_vi->get_param.payload_address_lsw = 0; + th_vi->get_param.payload_address_msw = 0; + th_vi->get_param.payload_size = sizeof(*th_vi) + - sizeof(th_vi->get_param) - sizeof(th_vi->hdr); + th_vi->get_param.port_id = q6audio_get_port_id(port); + th_vi->pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI; + th_vi->pdata.param_id = AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS; + th_vi->pdata.param_size = sizeof(th_vi->param); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *)th_vi); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n", + __func__, port, th_vi->get_param.param_id, ret); + goto done; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto done; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code(atomic_read(&this_afe.status)); + goto done; + } + memcpy(&th_vi->param, &this_afe.th_vi_resp.param, + sizeof(this_afe.th_vi_resp.param)); + pr_debug("%s: DC resistance %d %d temp %d %d status %d %d\n", + __func__, th_vi->param.dc_res_q24[SP_V2_SPKR_1], + th_vi->param.dc_res_q24[SP_V2_SPKR_2], + th_vi->param.temp_q22[SP_V2_SPKR_1], + th_vi->param.temp_q22[SP_V2_SPKR_2], + th_vi->param.status[SP_V2_SPKR_1], + th_vi->param.status[SP_V2_SPKR_2]); + ret = 0; +done: + return ret; +} + +int afe_get_sp_ex_vi_ftm_data(struct afe_sp_ex_vi_get_param *ex_vi) +{ + int ret = -EINVAL; + int index = 0, port = SLIMBUS_4_TX; + + if (!ex_vi) { + pr_err("%s: Invalid params\n", __func__); + goto done; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + ret = q6audio_validate_port(port); + if (ret < 0) { + pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret); + goto done; + } + + index = q6audio_get_port_index(port); + if (index < 0) { + pr_err("%s: invalid index %d port 0x%x\n", __func__, + index, port); + ret = -EINVAL; + goto done; + } + + ex_vi->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + ex_vi->hdr.pkt_size = sizeof(*ex_vi); + ex_vi->hdr.src_port = 0; + ex_vi->hdr.dest_port = 0; + ex_vi->hdr.token = index; + ex_vi->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + ex_vi->get_param.mem_map_handle = 0; + ex_vi->get_param.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI; + ex_vi->get_param.param_id = AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS; + ex_vi->get_param.payload_address_lsw = 0; + ex_vi->get_param.payload_address_msw = 0; + ex_vi->get_param.payload_size = sizeof(*ex_vi) + - sizeof(ex_vi->get_param) - sizeof(ex_vi->hdr); + ex_vi->get_param.port_id = q6audio_get_port_id(port); + ex_vi->pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI; + ex_vi->pdata.param_id = AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS; + ex_vi->pdata.param_size = sizeof(ex_vi->param); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *)ex_vi); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n", + __func__, port, ex_vi->get_param.param_id, ret); + goto done; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto done; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code(atomic_read(&this_afe.status)); + goto done; + } + memcpy(&ex_vi->param, &this_afe.ex_vi_resp.param, + sizeof(this_afe.ex_vi_resp.param)); + pr_debug("%s: freq %d %d resistance %d %d qfactor %d %d state %d %d\n", + __func__, ex_vi->param.freq_q20[SP_V2_SPKR_1], + ex_vi->param.freq_q20[SP_V2_SPKR_2], + ex_vi->param.resis_q24[SP_V2_SPKR_1], + ex_vi->param.resis_q24[SP_V2_SPKR_2], + ex_vi->param.qmct_q24[SP_V2_SPKR_1], + ex_vi->param.qmct_q24[SP_V2_SPKR_2], + ex_vi->param.status[SP_V2_SPKR_1], + ex_vi->param.status[SP_V2_SPKR_2]); + ret = 0; +done: + return ret; +} + +int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp) +{ + int ret = -EINVAL; + int index = 0, port = SLIMBUS_4_TX; + + if (!calib_resp) { + pr_err("%s: Invalid params\n", __func__); + goto fail_cmd; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + ret = q6audio_validate_port(port); + if (ret < 0) { + pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret); + ret = -EINVAL; + goto fail_cmd; + } + index = q6audio_get_port_index(port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + calib_resp->hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + calib_resp->hdr.pkt_size = sizeof(*calib_resp); + calib_resp->hdr.src_port = 0; + calib_resp->hdr.dest_port = 0; + calib_resp->hdr.token = index; + calib_resp->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + calib_resp->get_param.mem_map_handle = 0; + calib_resp->get_param.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2; + calib_resp->get_param.param_id = AFE_PARAM_ID_CALIB_RES_CFG_V2; + calib_resp->get_param.payload_address_lsw = 0; + calib_resp->get_param.payload_address_msw = 0; + calib_resp->get_param.payload_size = sizeof(*calib_resp) + - sizeof(calib_resp->get_param) - sizeof(calib_resp->hdr); + calib_resp->get_param.port_id = q6audio_get_port_id(port); + calib_resp->pdata.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2; + calib_resp->pdata.param_id = AFE_PARAM_ID_CALIB_RES_CFG_V2; + calib_resp->pdata.param_size = sizeof(calib_resp->res_cfg); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *)calib_resp); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n", + __func__, port, calib_resp->get_param.param_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + memcpy(&calib_resp->res_cfg, &this_afe.calib_data.res_cfg, + sizeof(this_afe.calib_data.res_cfg)); + pr_info("%s: state %s resistance %d %d\n", __func__, + fbsp_state[calib_resp->res_cfg.th_vi_ca_state], + calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_1], + calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_2]); + ret = 0; +fail_cmd: + return ret; +} + +int afe_spk_prot_feed_back_cfg(int src_port, int dst_port, + int l_ch, int r_ch, u32 enable) +{ + int ret = -EINVAL; + union afe_spkr_prot_config prot_config; + int index = 0; + + if (!enable) { + pr_debug("%s: Disable Feedback tx path", __func__); + this_afe.vi_tx_port = -1; + this_afe.vi_rx_port = -1; + return 0; + } + + if ((q6audio_validate_port(src_port) < 0) || + (q6audio_validate_port(dst_port) < 0)) { + pr_err("%s: invalid ports src 0x%x dst 0x%x", + __func__, src_port, dst_port); + goto fail_cmd; + } + if (!l_ch && !r_ch) { + pr_err("%s: error ch values zero\n", __func__); + goto fail_cmd; + } + pr_debug("%s: src_port 0x%x dst_port 0x%x l_ch %d r_ch %d\n", + __func__, src_port, dst_port, l_ch, r_ch); + memset(&prot_config, 0, sizeof(prot_config)); + prot_config.feedback_path_cfg.dst_portid = + q6audio_get_port_id(dst_port); + if (l_ch) { + prot_config.feedback_path_cfg.chan_info[index++] = 1; + prot_config.feedback_path_cfg.chan_info[index++] = 2; + } + if (r_ch) { + prot_config.feedback_path_cfg.chan_info[index++] = 3; + prot_config.feedback_path_cfg.chan_info[index++] = 4; + } + prot_config.feedback_path_cfg.num_channels = index; + pr_debug("%s no of channels: %d\n", __func__, index); + prot_config.feedback_path_cfg.minor_version = 1; + ret = afe_spk_prot_prepare(src_port, dst_port, + AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config); +fail_cmd: + return ret; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case AFE_COMMON_RX_CAL_TYPE: + ret = AFE_COMMON_RX_CAL; + break; + case AFE_COMMON_TX_CAL_TYPE: + ret = AFE_COMMON_TX_CAL; + break; + case AFE_AANC_CAL_TYPE: + ret = AFE_AANC_CAL; + break; + case AFE_HW_DELAY_CAL_TYPE: + ret = AFE_HW_DELAY_CAL; + break; + case AFE_FB_SPKR_PROT_CAL_TYPE: + ret = AFE_FB_SPKR_PROT_CAL; + break; + case AFE_SIDETONE_CAL_TYPE: + ret = AFE_SIDETONE_CAL; + break; + case AFE_TOPOLOGY_CAL_TYPE: + ret = AFE_TOPOLOGY_CAL; + break; + case AFE_CUST_TOPOLOGY_CAL_TYPE: + ret = AFE_CUST_TOPOLOGY_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +int afe_alloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + pr_debug("%s: cal_type = %d cal_index = %d\n", + __func__, cal_type, cal_index); + + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + this_afe.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int afe_dealloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + this_afe.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int afe_set_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + this_afe.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == AFE_CUST_TOPOLOGY_CAL) { + mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + this_afe.set_custom_topology = 1; + pr_debug("%s:[AFE_CUSTOM_TOPOLOGY] ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + } + +done: + return ret; +} + +static struct cal_block_data *afe_find_hw_delay_by_path( + struct cal_type_data *cal_type, int path) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + pr_debug("%s:\n", __func__); + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (((struct audio_cal_info_hw_delay *)cal_block->cal_info) + ->path == path) { + return cal_block; + } + } + return NULL; +} + +static int afe_get_cal_hw_delay(int32_t path, + struct audio_cal_hw_delay_entry *entry) +{ + int ret = 0; + int i; + struct cal_block_data *cal_block = NULL; + struct audio_cal_hw_delay_data *hw_delay_info = NULL; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[AFE_HW_DELAY_CAL] == NULL) { + pr_err("%s: AFE_HW_DELAY_CAL not initialized\n", __func__); + ret = -EINVAL; + goto done; + } + if (entry == NULL) { + pr_err("%s: entry is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + if ((path >= MAX_PATH_TYPE) || (path < 0)) { + pr_err("%s: bad path: %d\n", + __func__, path); + ret = -EINVAL; + goto done; + } + + mutex_lock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock); + cal_block = afe_find_hw_delay_by_path( + this_afe.cal_data[AFE_HW_DELAY_CAL], path); + if (cal_block == NULL) + goto unlock; + + hw_delay_info = &((struct audio_cal_info_hw_delay *) + cal_block->cal_info)->data; + if (hw_delay_info->num_entries > MAX_HW_DELAY_ENTRIES) { + pr_err("%s: invalid num entries: %d\n", + __func__, hw_delay_info->num_entries); + ret = -EINVAL; + goto unlock; + } + + for (i = 0; i < hw_delay_info->num_entries; i++) { + if (hw_delay_info->entry[i].sample_rate == + entry->sample_rate) { + entry->delay_usec = hw_delay_info->entry[i].delay_usec; + break; + } + } + if (i == hw_delay_info->num_entries) { + pr_err("%s: Unable to find delay for sample rate %d\n", + __func__, entry->sample_rate); + ret = -EFAULT; + goto unlock; + } + pr_debug("%s: Path = %d samplerate = %u usec = %u status %d\n", + __func__, path, entry->sample_rate, entry->delay_usec, ret); +unlock: + mutex_unlock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock); +done: + return ret; +} + +static int afe_set_cal_sp_th_vi_ftm_cfg(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_sp_th_vi_ftm_cfg *cal_data = data; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + pr_debug("%s: cal_type = %d\n", __func__, cal_type); + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + memcpy(&this_afe.th_ftm_cfg, &cal_data->cal_info, + sizeof(this_afe.th_ftm_cfg)); + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); +done: + return ret; +} + +static int afe_set_cal_sp_ex_vi_ftm_cfg(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_sp_ex_vi_ftm_cfg *cal_data = data; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + pr_debug("%s: cal_type = %d\n", __func__, cal_type); + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + memcpy(&this_afe.ex_ftm_cfg, &cal_data->cal_info, + sizeof(this_afe.ex_ftm_cfg)); + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); +done: + return ret; +} + +static int afe_set_cal_fb_spkr_prot(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_fb_spk_prot_cfg *cal_data = data; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) + goto done; + if (cal_data == NULL) + goto done; + if (data_size != sizeof(*cal_data)) + goto done; + + if (cal_data->cal_info.mode == MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) + __pm_wakeup_event(&wl.ws, jiffies_to_msecs(WAKELOCK_TIMEOUT)); + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + memcpy(&this_afe.prot_cfg, &cal_data->cal_info, + sizeof(this_afe.prot_cfg)); + this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode; + this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode; + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); +done: + return ret; +} + +static int afe_get_cal_sp_th_vi_ftm_param(int32_t cal_type, size_t data_size, + void *data) +{ + int i, ret = 0; + struct audio_cal_type_sp_th_vi_param *cal_data = data; + struct afe_sp_th_vi_get_param th_vi; + + pr_debug("%s: cal_type = %d\n", __func__, cal_type); + if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + cal_data->cal_info.status[i] = -EINVAL; + cal_data->cal_info.r_dc_q24[i] = -1; + cal_data->cal_info.temp_q22[i] = -1; + } + if (!afe_get_sp_th_vi_ftm_data(&th_vi)) { + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + pr_debug("%s: ftm param status = %d\n", + __func__, th_vi.param.status[i]); + if (th_vi.param.status[i] == FBSP_IN_PROGRESS) { + cal_data->cal_info.status[i] = -EAGAIN; + } else if (th_vi.param.status[i] == FBSP_SUCCESS) { + cal_data->cal_info.status[i] = 0; + cal_data->cal_info.r_dc_q24[i] = + th_vi.param.dc_res_q24[i]; + cal_data->cal_info.temp_q22[i] = + th_vi.param.temp_q22[i]; + } + } + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); +done: + return ret; +} + +static int afe_get_cal_sp_ex_vi_ftm_param(int32_t cal_type, size_t data_size, + void *data) +{ + int i, ret = 0; + struct audio_cal_type_sp_ex_vi_param *cal_data = data; + struct afe_sp_ex_vi_get_param ex_vi; + + pr_debug("%s: cal_type = %d\n", __func__, cal_type); + if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + cal_data->cal_info.status[i] = -EINVAL; + cal_data->cal_info.freq_q20[i] = -1; + cal_data->cal_info.resis_q24[i] = -1; + cal_data->cal_info.qmct_q24[i] = -1; + } + if (!afe_get_sp_ex_vi_ftm_data(&ex_vi)) { + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + pr_debug("%s: ftm param status = %d\n", + __func__, ex_vi.param.status[i]); + if (ex_vi.param.status[i] == FBSP_IN_PROGRESS) { + cal_data->cal_info.status[i] = -EAGAIN; + } else if (ex_vi.param.status[i] == FBSP_SUCCESS) { + cal_data->cal_info.status[i] = 0; + cal_data->cal_info.freq_q20[i] = + ex_vi.param.freq_q20[i]; + cal_data->cal_info.resis_q24[i] = + ex_vi.param.resis_q24[i]; + cal_data->cal_info.qmct_q24[i] = + ex_vi.param.qmct_q24[i]; + } + } + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); +done: + return ret; +} + +static int afe_get_cal_fb_spkr_prot(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_fb_spk_prot_status *cal_data = data; + struct afe_spkr_prot_get_vi_calib calib_resp; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) + goto done; + if (cal_data == NULL) + goto done; + if (data_size != sizeof(*cal_data)) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_CALIBRATED) { + cal_data->cal_info.r0[SP_V2_SPKR_1] = + this_afe.prot_cfg.r0[SP_V2_SPKR_1]; + cal_data->cal_info.r0[SP_V2_SPKR_2] = + this_afe.prot_cfg.r0[SP_V2_SPKR_2]; + cal_data->cal_info.status = 0; + } else if (this_afe.prot_cfg.mode == + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) { + /*Call AFE to query the status*/ + cal_data->cal_info.status = -EINVAL; + cal_data->cal_info.r0[SP_V2_SPKR_1] = -1; + cal_data->cal_info.r0[SP_V2_SPKR_2] = -1; + if (!afe_spk_prot_get_calib_data(&calib_resp)) { + if (calib_resp.res_cfg.th_vi_ca_state == + FBSP_IN_PROGRESS) + cal_data->cal_info.status = -EAGAIN; + else if (calib_resp.res_cfg.th_vi_ca_state == + FBSP_SUCCESS) { + cal_data->cal_info.status = 0; + cal_data->cal_info.r0[SP_V2_SPKR_1] = + calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_1]; + cal_data->cal_info.r0[SP_V2_SPKR_2] = + calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_2]; + } + } + if (!cal_data->cal_info.status) { + this_afe.prot_cfg.mode = + MSM_SPKR_PROT_CALIBRATED; + this_afe.prot_cfg.r0[SP_V2_SPKR_1] = + cal_data->cal_info.r0[SP_V2_SPKR_1]; + this_afe.prot_cfg.r0[SP_V2_SPKR_2] = + cal_data->cal_info.r0[SP_V2_SPKR_2]; + } + } else { + /*Indicates calibration data is invalid*/ + cal_data->cal_info.status = -EINVAL; + cal_data->cal_info.r0[SP_V2_SPKR_1] = -1; + cal_data->cal_info.r0[SP_V2_SPKR_2] = -1; + } + this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode; + this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode; + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + __pm_relax(&wl.ws); +done: + return ret; +} + +static int afe_map_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + atomic_set(&this_afe.mem_map_cal_index, cal_index); + ret = afe_cmd_memory_map(cal_block->cal_data.paddr, + cal_block->map_data.map_size); + atomic_set(&this_afe.mem_map_cal_index, -1); + if (ret < 0) { + pr_err("%s: mmap did not work! size = %zd ret %d\n", + __func__, + cal_block->map_data.map_size, ret); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_afe. + mem_map_cal_handles[cal_index]); +done: + return ret; +} + +static int afe_unmap_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", + __func__); + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_err("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + atomic_set(&this_afe.mem_map_cal_handles[cal_index], + cal_block->map_data.q6map_handle); + atomic_set(&this_afe.mem_map_cal_index, cal_index); + ret = afe_cmd_memory_unmap_nowait( + cal_block->map_data.q6map_handle); + atomic_set(&this_afe.mem_map_cal_index, -1); + if (ret < 0) { + pr_err("%s: unmap did not work! cal_type %i ret %d\n", + __func__, cal_index, ret); + } + cal_block->map_data.q6map_handle = 0; +done: + return ret; +} + +static void afe_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data); +} + +static int afe_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{AFE_COMMON_RX_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_COMMON_TX_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_AANC_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_FB_SPKR_PROT_CAL_TYPE, + {NULL, NULL, NULL, afe_set_cal_fb_spkr_prot, + afe_get_cal_fb_spkr_prot, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_HW_DELAY_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_SIDETONE_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, + cal_utils_match_buf_num} }, + + {{AFE_CUST_TOPOLOGY_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE, + {NULL, NULL, NULL, afe_set_cal_sp_th_vi_ftm_cfg, + afe_get_cal_sp_th_vi_ftm_param, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE, + {NULL, NULL, NULL, afe_set_cal_sp_ex_vi_ftm_cfg, + afe_get_cal_sp_ex_vi_ftm_param, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type! %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + return ret; +err: + afe_delete_cal_data(); + return ret; +} + +int afe_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + result = -EINVAL; + goto done; + } + + result = afe_cmd_memory_map(cal_block->cal_data.paddr, + cal_block->map_data.map_size); + if (result < 0) { + pr_err("%s: afe_cmd_memory_map failed for addr = 0x%pK, size = %d, err %d\n", + __func__, &cal_block->cal_data.paddr, + cal_block->map_data.map_size, result); + return result; + } + cal_block->map_data.map_handle = this_afe.mmap_handle; + +done: + return result; +} + +int afe_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (mem_map_handle == NULL) { + pr_err("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + goto done; + } + + result = afe_cmd_memory_unmap(*mem_map_handle); + if (result) { + pr_err("%s: AFE memory unmap failed %d, handle 0x%x\n", + __func__, result, *mem_map_handle); + goto done; + } else { + *mem_map_handle = 0; + } + +done: + return result; +} + +static int __init afe_init(void) +{ + int i = 0, ret; + + atomic_set(&this_afe.state, 0); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.mem_map_cal_index, -1); + this_afe.apr = NULL; + this_afe.dtmf_gen_rx_portid = -1; + this_afe.mmap_handle = 0; + this_afe.vi_tx_port = -1; + this_afe.vi_rx_port = -1; + this_afe.prot_cfg.mode = MSM_SPKR_PROT_DISABLED; + this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + mutex_init(&this_afe.afe_cmd_lock); + for (i = 0; i < AFE_MAX_PORTS; i++) { + this_afe.afe_cal_mode[i] = AFE_CAL_MODE_DEFAULT; + init_waitqueue_head(&this_afe.wait[i]); + } + wakeup_source_init(&wl.ws, "spkr-prot"); + ret = afe_init_cal_data(); + if (ret) + pr_err("%s: could not init cal data! %d\n", __func__, ret); + + config_debug_fs_init(); + return 0; +} + +static void __exit afe_exit(void) +{ + afe_delete_cal_data(); + + config_debug_fs_exit(); + mutex_destroy(&this_afe.afe_cmd_lock); + wakeup_source_trash(&wl.ws); +} + +device_initcall(afe_init); +__exitcall(afe_exit); diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c new file mode 100644 index 000000000000..779bdd59202f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -0,0 +1,8578 @@ +/* + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define TRUE 0x01 +#define FALSE 0x00 +#define SESSION_MAX 8 + +enum { + ASM_TOPOLOGY_CAL = 0, + ASM_CUSTOM_TOP_CAL, + ASM_AUDSTRM_CAL, + ASM_RTAC_APR_CAL, + ASM_MAX_CAL_TYPES +}; + +union asm_token_struct { + struct { + u8 stream_id; + u8 session_id; + u8 buf_index; + u8 flags; + } _token; + u32 token; +} __packed; + + +enum { + ASM_DIRECTION_OFFSET, + ASM_CMD_NO_WAIT_OFFSET, + /* + * Offset is limited to 7 because flags is stored in u8 + * field in asm_token_structure defined above. The offset + * starts from 0. + */ + ASM_MAX_OFFSET = 7, +}; + +enum { + WAIT_CMD, + NO_WAIT_CMD +}; + +#define ASM_SET_BIT(n, x) (n |= 1 << x) +#define ASM_TEST_BIT(n, x) ((n >> x) & 1) + +/* TODO, combine them together */ +static DEFINE_MUTEX(session_lock); +struct asm_mmap { + atomic_t ref_cnt; + void *apr; +}; + +static struct asm_mmap this_mmap; +/* session id: 0 reserved */ +static struct audio_client *session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; + +struct asm_buffer_node { + struct list_head list; + phys_addr_t buf_phys_addr; + uint32_t mmap_hdl; +}; +static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv); +static int32_t q6asm_callback(struct apr_client_data *data, void *priv); +static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg); +static void q6asm_add_hdr_custom_topology(struct audio_client *ac, + struct apr_hdr *hdr, + uint32_t pkt_size); +static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg); +static int q6asm_memory_map_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt, + bool is_contiguous); +static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir); +static void q6asm_reset_buf_state(struct audio_client *ac); + +static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels, + bool use_back_flavor); +void *q6asm_mmap_apr_reg(void); + +static int q6asm_is_valid_session(struct apr_client_data *data, void *priv); +static int q6asm_get_asm_topology_cal(void); +static int q6asm_get_asm_app_type_cal(void); + +/* for ASM custom topology */ +static struct cal_type_data *cal_data[ASM_MAX_CAL_TYPES]; +static struct audio_buffer common_buf[2]; +static struct audio_client common_client; +static int set_custom_topology; +static int topology_map_handle; + +struct generic_get_data_ { + int valid; + int is_inband; + int size_in_ints; + int ints[]; +}; +static struct generic_get_data_ *generic_get_data; + +#ifdef CONFIG_DEBUG_FS +#define OUT_BUFFER_SIZE 56 +#define IN_BUFFER_SIZE 24 + +static struct timeval out_cold_tv; +static struct timeval out_warm_tv; +static struct timeval out_cont_tv; +static struct timeval in_cont_tv; +static long out_enable_flag; +static long in_enable_flag; +static struct dentry *out_dentry; +static struct dentry *in_dentry; +static int in_cont_index; +/*This var is used to keep track of first write done for cold output latency */ +static int out_cold_index; +static char *out_buffer; +static char *in_buffer; + +static inline void q6asm_set_flag_in_token(union asm_token_struct *asm_token, + int flag, int flag_offset) +{ + if (flag) + ASM_SET_BIT(asm_token->_token.flags, flag_offset); +} + +static inline int q6asm_get_flag_from_token(union asm_token_struct *asm_token, + int flag_offset) +{ + return ASM_TEST_BIT(asm_token->_token.flags, flag_offset); +} + +static inline void q6asm_update_token(u32 *token, u8 session_id, u8 stream_id, + u8 buf_index, u8 dir, u8 nowait_flag) +{ + union asm_token_struct asm_token; + + asm_token.token = 0; + asm_token._token.session_id = session_id; + asm_token._token.stream_id = stream_id; + asm_token._token.buf_index = buf_index; + q6asm_set_flag_in_token(&asm_token, dir, ASM_DIRECTION_OFFSET); + q6asm_set_flag_in_token(&asm_token, nowait_flag, + ASM_CMD_NO_WAIT_OFFSET); + *token = asm_token.token; +} + +static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver) +{ + uint32_t pcm_format_id; + + switch (media_format_block_ver) { + case PCM_MEDIA_FORMAT_V4: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4; + break; + case PCM_MEDIA_FORMAT_V3: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + case PCM_MEDIA_FORMAT_V2: + default: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + } + return pcm_format_id; +} + +/* + * q6asm_get_buf_index_from_token: + * Retrieve buffer index from token. + * + * @token: token value sent to ASM service on q6. + * Returns buffer index in the read/write commands. + */ +uint8_t q6asm_get_buf_index_from_token(uint32_t token) +{ + union asm_token_struct asm_token; + + asm_token.token = token; + return asm_token._token.buf_index; +} +EXPORT_SYMBOL(q6asm_get_buf_index_from_token); + +/* + * q6asm_get_stream_id_from_token: + * Retrieve stream id from token. + * + * @token: token value sent to ASM service on q6. + * Returns stream id. + */ +uint8_t q6asm_get_stream_id_from_token(uint32_t token) +{ + union asm_token_struct asm_token; + + asm_token.token = token; + return asm_token._token.stream_id; +} +EXPORT_SYMBOL(q6asm_get_stream_id_from_token); + +static int audio_output_latency_dbgfs_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +static ssize_t audio_output_latency_dbgfs_read(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + if (out_buffer == NULL) { + pr_err("%s: out_buffer is null\n", __func__); + return 0; + } + snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,", + out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec, + out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec); + return simple_read_from_buffer(buf, OUT_BUFFER_SIZE, ppos, + out_buffer, OUT_BUFFER_SIZE); +} +static ssize_t audio_output_latency_dbgfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + char *temp; + + if (count > 2*sizeof(char)) { + pr_err("%s: err count is more %zd\n", __func__, count); + return -EINVAL; + } + temp = kmalloc(2*sizeof(char), GFP_KERNEL); + + out_cold_index = 0; + + if (temp) { + if (copy_from_user(temp, buf, 2*sizeof(char))) { + pr_err("%s: copy from user failed for size %zd\n", + __func__, 2*sizeof(char)); + kfree(temp); + return -EFAULT; + } + if (!kstrtol(temp, 10, &out_enable_flag)) { + kfree(temp); + return count; + } + kfree(temp); + } + return -EINVAL; +} +static const struct file_operations audio_output_latency_debug_fops = { + .open = audio_output_latency_dbgfs_open, + .read = audio_output_latency_dbgfs_read, + .write = audio_output_latency_dbgfs_write +}; +static int audio_input_latency_dbgfs_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +static ssize_t audio_input_latency_dbgfs_read(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + if (in_buffer == NULL) { + pr_err("%s: in_buffer is null\n", __func__); + return 0; + } + snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,", + in_cont_tv.tv_sec, in_cont_tv.tv_usec); + return simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos, + in_buffer, IN_BUFFER_SIZE); +} +static ssize_t audio_input_latency_dbgfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + char *temp; + + if (count > 2*sizeof(char)) { + pr_err("%s: err count is more %zd\n", __func__, count); + return -EINVAL; + } + temp = kmalloc(2*sizeof(char), GFP_KERNEL); + + if (temp) { + if (copy_from_user(temp, buf, 2*sizeof(char))) { + pr_err("%s: copy from user failed for size %zd\n", + __func__, 2*sizeof(char)); + kfree(temp); + return -EFAULT; + } + if (!kstrtol(temp, 10, &in_enable_flag)) { + kfree(temp); + return count; + } + kfree(temp); + } + return -EINVAL; +} +static const struct file_operations audio_input_latency_debug_fops = { + .open = audio_input_latency_dbgfs_open, + .read = audio_input_latency_dbgfs_read, + .write = audio_input_latency_dbgfs_write +}; + +static void config_debug_fs_write_cb(void) +{ + if (out_enable_flag) { + /* For first Write done log the time and reset + * out_cold_index + */ + if (out_cold_index != 1) { + do_gettimeofday(&out_cold_tv); + pr_debug("COLD: apr_send_pkt at %ld sec %ld microsec\n", + out_cold_tv.tv_sec, + out_cold_tv.tv_usec); + out_cold_index = 1; + } + pr_debug("%s: out_enable_flag %ld\n", + __func__, out_enable_flag); + } +} +static void config_debug_fs_read_cb(void) +{ + if (in_enable_flag) { + /* when in_cont_index == 7, DSP would be + * writing into the 8th 512 byte buffer and this + * timestamp is tapped here.Once done it then writes + * to 9th 512 byte buffer.These two buffers(8th, 9th) + * reach the test application in 5th iteration and that + * timestamp is tapped at user level. The difference + * of these two timestamps gives us the time between + * the time at which dsp started filling the sample + * required and when it reached the test application. + * Hence continuous input latency + */ + if (in_cont_index == 7) { + do_gettimeofday(&in_cont_tv); + pr_info("%s: read buffer at %ld sec %ld microsec\n", + __func__, + in_cont_tv.tv_sec, in_cont_tv.tv_usec); + } + in_cont_index++; + } +} + +static void config_debug_fs_reset_index(void) +{ + in_cont_index = 0; +} + +static void config_debug_fs_run(void) +{ + if (out_enable_flag) { + do_gettimeofday(&out_cold_tv); + pr_debug("%s: COLD apr_send_pkt at %ld sec %ld microsec\n", + __func__, out_cold_tv.tv_sec, out_cold_tv.tv_usec); + } +} + +static void config_debug_fs_write(struct audio_buffer *ab) +{ + if (out_enable_flag) { + char zero_pattern[2] = {0x00, 0x00}; + /* If First two byte is non zero and last two byte + * is zero then it is warm output pattern + */ + if ((strcmp(((char *)ab->data), zero_pattern)) && + (!strcmp(((char *)ab->data + 2), zero_pattern))) { + do_gettimeofday(&out_warm_tv); + pr_debug("%s: WARM:apr_send_pkt at %ld sec %ld microsec\n", + __func__, + out_warm_tv.tv_sec, + out_warm_tv.tv_usec); + pr_debug("%s: Warm Pattern Matched\n", __func__); + } + /* If First two byte is zero and last two byte is + * non zero then it is cont output pattern + */ + else if ((!strcmp(((char *)ab->data), zero_pattern)) + && (strcmp(((char *)ab->data + 2), zero_pattern))) { + do_gettimeofday(&out_cont_tv); + pr_debug("%s: CONT:apr_send_pkt at %ld sec %ld microsec\n", + __func__, + out_cont_tv.tv_sec, + out_cont_tv.tv_usec); + pr_debug("%s: Cont Pattern Matched\n", __func__); + } + } +} +static void config_debug_fs_init(void) +{ + out_buffer = kzalloc(OUT_BUFFER_SIZE, GFP_KERNEL); + if (out_buffer == NULL) + goto outbuf_fail; + + in_buffer = kzalloc(IN_BUFFER_SIZE, GFP_KERNEL); + if (in_buffer == NULL) + goto inbuf_fail; + + out_dentry = debugfs_create_file("audio_out_latency_measurement_node", + 0664, + NULL, NULL, &audio_output_latency_debug_fops); + if (IS_ERR(out_dentry)) { + pr_err("%s: debugfs_create_file failed\n", __func__); + goto file_fail; + } + in_dentry = debugfs_create_file("audio_in_latency_measurement_node", + 0664, + NULL, NULL, &audio_input_latency_debug_fops); + if (IS_ERR(in_dentry)) { + pr_err("%s: debugfs_create_file failed\n", __func__); + goto file_fail; + } + return; +file_fail: + kfree(in_buffer); +inbuf_fail: + kfree(out_buffer); +outbuf_fail: + in_buffer = NULL; + out_buffer = NULL; +} +#else +static void config_debug_fs_write(struct audio_buffer *ab) +{ +} +static void config_debug_fs_run(void) +{ +} +static void config_debug_fs_reset_index(void) +{ +} +static void config_debug_fs_read_cb(void) +{ +} +static void config_debug_fs_write_cb(void) +{ +} +static void config_debug_fs_init(void) +{ +} +#endif + +int q6asm_mmap_apr_dereg(void) +{ + int c; + + c = atomic_sub_return(1, &this_mmap.ref_cnt); + if (c == 0) { + apr_deregister(this_mmap.apr); + common_client.mmap_apr = NULL; + pr_debug("%s: APR De-Register common port\n", __func__); + } else if (c < 0) { + pr_err("%s: APR Common Port Already Closed %d\n", + __func__, c); + atomic_set(&this_mmap.ref_cnt, 0); + } + + return 0; +} + +static int q6asm_session_alloc(struct audio_client *ac) +{ + int n; + + for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { + if (!session[n]) { + session[n] = ac; + return n; + } + } + pr_err("%s: session not available\n", __func__); + return -ENOMEM; +} + +static bool q6asm_is_valid_audio_client(struct audio_client *ac) +{ + int n; + + for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { + if (session[n] == ac) + return 1; + } + return 0; +} + +static void q6asm_session_free(struct audio_client *ac) +{ + pr_debug("%s: sessionid[%d]\n", __func__, ac->session); + rtac_remove_popp_from_adm_devices(ac->session); + session[ac->session] = 0; + ac->session = 0; + ac->perf_mode = LEGACY_PCM_MODE; + ac->fptr_cache_ops = NULL; +} + +static uint32_t q6asm_get_next_buf(struct audio_client *ac, + uint32_t curr_buf, uint32_t max_buf_cnt) +{ + dev_vdbg(ac->dev, "%s: curr_buf = %d, max_buf_cnt = %d\n", + __func__, curr_buf, max_buf_cnt); + curr_buf += 1; + return (curr_buf >= max_buf_cnt) ? 0 : curr_buf; +} + +static int q6asm_map_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int result = 0; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + goto done; + } + + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + common_client.apr = common_client.mmap_apr; + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + goto done; + } + + /* Use second asm buf to map memory */ + if (common_client.port[IN].buf == NULL) { + pr_err("%s: common buf is NULL\n", + __func__); + result = -EINVAL; + goto done; + } + + common_client.port[IN].buf->phys = cal_block->cal_data.paddr; + + result = q6asm_memory_map_regions(&common_client, + IN, cal_block->map_data.map_size, 1, 1); + if (result < 0) { + pr_err("%s: mmap did not work! size = %zd result %d\n", + __func__, + cal_block->map_data.map_size, result); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + + list_for_each_safe(ptr, next, + &common_client.port[IN].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) { + cal_block->map_data.q6map_handle = buf_node->mmap_hdl; + break; + } + } +done: + return result; +} + +static int remap_cal_data(int32_t cal_type, struct cal_block_data *cal_block) +{ + int ret = 0; + + if (cal_block->map_data.ion_client == NULL) { + pr_err("%s: No ION allocation for cal type %d!\n", + __func__, cal_type); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + + ret = q6asm_map_cal_memory(cal_type, cal_block); + if (ret < 0) { + pr_err("%s: mmap did not work! size = %zd ret %d\n", + __func__, cal_block->map_data.map_size, ret); + goto done; + } + } +done: + return ret; +} + +static int q6asm_unmap_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int result = 0; + int result2 = 0; + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_debug("%s: No address to unmap!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (common_client.mmap_apr == NULL) { + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + } + + result2 = q6asm_memory_unmap_regions(&common_client, IN); + if (result2 < 0) { + pr_err("%s: unmap failed, err %d\n", + __func__, result2); + result = result2; + } + + cal_block->map_data.q6map_handle = 0; +done: + return result; +} + +int q6asm_unmap_cal_data(int cal_type, struct cal_block_data *cal_block) +{ + int ret = 0; + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle != 0)) { + + ret = q6asm_unmap_cal_memory(cal_type, cal_block); + if (ret < 0) { + pr_err("%s: unmap did not work! size = %zd ret %d\n", + __func__, cal_block->map_data.map_size, ret); + goto done; + } + } +done: + return ret; +} + +int send_asm_custom_topology(struct audio_client *ac) +{ + struct cal_block_data *cal_block = NULL; + struct cmd_set_topologies asm_top; + int result = 0; + int result1 = 0; + + if (cal_data[ASM_CUSTOM_TOP_CAL] == NULL) + goto done; + + mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + if (!set_custom_topology) + goto unlock; + set_custom_topology = 0; + + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_CUSTOM_TOP_CAL]); + if (cal_block == NULL) + goto unlock; + + if (cal_block->cal_data.size == 0) { + pr_debug("%s: No cal to send!\n", __func__); + goto unlock; + } + + pr_debug("%s: Sending cal_index %d\n", __func__, ASM_CUSTOM_TOP_CAL); + + result = remap_cal_data(ASM_CUST_TOPOLOGY_CAL_TYPE, cal_block); + if (result) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, ASM_CUSTOM_TOP_CAL); + goto unlock; + } + + q6asm_add_hdr_custom_topology(ac, &asm_top.hdr, sizeof(asm_top)); + atomic_set(&ac->mem_state, -1); + asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES; + asm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr); + asm_top.payload_addr_msw = msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + asm_top.mem_map_handle = cal_block->map_data.q6map_handle; + asm_top.payload_size = cal_block->cal_data.size; + + pr_debug("%s: Sending ASM_CMD_ADD_TOPOLOGIES payload = %pK, size = %d, map handle = 0x%x\n", + __func__, &cal_block->cal_data.paddr, + asm_top.payload_size, asm_top.mem_map_handle); + + result = apr_send_pkt(ac->apr, (uint32_t *) &asm_top); + if (result < 0) { + pr_err("%s: Set topologies failed result %d\n", + __func__, result); + pr_debug("%s: Set topologies failed payload = 0x%pK\n", + __func__, &cal_block->cal_data.paddr); + goto unmap; + + } + + result = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0), 5*HZ); + if (!result) { + pr_err("%s: Set topologies failed timeout\n", __func__); + pr_debug("%s: Set topologies failed after timedout payload = 0x%pK\n", + __func__, &cal_block->cal_data.paddr); + result = -ETIMEDOUT; + goto unmap; + } + if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + result = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + goto unmap; + } + +unmap: + result1 = q6asm_unmap_cal_memory(ASM_CUST_TOPOLOGY_CAL_TYPE, + cal_block); + if (result1 < 0) { + result = result1; + pr_debug("%s: unmap cal failed! %d\n", __func__, result); + } +unlock: + mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); +done: + return result; +} + +int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + + pr_debug("%s:\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (common_client.mmap_apr == NULL) { + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + result = -EINVAL; + goto done; + } + + /* Use second asm buf to map memory */ + if (common_client.port[OUT].buf == NULL) { + pr_err("%s: common buf is NULL\n", + __func__); + result = -EINVAL; + goto done; + } + + common_client.port[OUT].buf->phys = cal_block->cal_data.paddr; + + result = q6asm_memory_map_regions(&common_client, + OUT, cal_block->map_data.map_size, 1, 1); + if (result < 0) { + pr_err("%s: mmap did not work! size = %d result %d\n", + __func__, + cal_block->map_data.map_size, result); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + + list_for_each_safe(ptr, next, + &common_client.port[OUT].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) { + cal_block->map_data.map_handle = buf_node->mmap_hdl; + break; + } + } +done: + return result; +} + +int q6asm_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + int result2 = 0; + + pr_debug("%s:\n", __func__); + + if (mem_map_handle == NULL) { + pr_debug("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + goto done; + } + + if (common_client.mmap_apr == NULL) { + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + } + + + result2 = q6asm_memory_unmap_regions(&common_client, OUT); + if (result2 < 0) { + pr_err("%s: unmap failed, err %d\n", + __func__, result2); + result = result2; + } else { + mem_map_handle = 0; + } + + result2 = q6asm_mmap_apr_dereg(); + if (result2 < 0) { + pr_err("%s: q6asm_mmap_apr_dereg failed, err %d\n", + __func__, result2); + result = result2; + } +done: + return result; +} + +int q6asm_audio_client_buf_free(unsigned int dir, + struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + + pr_debug("%s: Session id %d\n", __func__, ac->session); + mutex_lock(&ac->cmd_lock); + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + if (!port->buf) { + pr_err("%s: buf NULL\n", __func__); + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (cnt >= 0) { + rc = q6asm_memory_unmap_regions(ac, dir); + if (rc < 0) + pr_err("%s: Memory_unmap_regions failed %d\n", + __func__, rc); + } + + while (cnt >= 0) { + if (port->buf[cnt].data) { + if (!rc || atomic_read(&ac->reset)) + msm_audio_ion_free( + port->buf[cnt].client, + port->buf[cnt].handle); + + port->buf[cnt].client = NULL; + port->buf[cnt].handle = NULL; + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + --(port->max_buf_cnt); + } + --cnt; + } + kfree(port->buf); + port->buf = NULL; + } + mutex_unlock(&ac->cmd_lock); + return 0; +} + +int q6asm_audio_client_buf_free_contiguous(unsigned int dir, + struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + + pr_debug("%s: Session id %d\n", __func__, ac->session); + mutex_lock(&ac->cmd_lock); + port = &ac->port[dir]; + if (!port->buf) { + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (cnt >= 0) { + rc = q6asm_memory_unmap(ac, port->buf[0].phys, dir); + if (rc < 0) + pr_err("%s: Memory_unmap_regions failed %d\n", + __func__, rc); + } + + if (port->buf[0].data) { + pr_debug("%s: data[%pK]phys[%pK][%pK] , client[%pK] handle[%pK]\n", + __func__, + port->buf[0].data, + &port->buf[0].phys, + &port->buf[0].phys, + port->buf[0].client, + port->buf[0].handle); + if (!rc || atomic_read(&ac->reset)) + msm_audio_ion_free(port->buf[0].client, + port->buf[0].handle); + port->buf[0].client = NULL; + port->buf[0].handle = NULL; + } + + while (cnt >= 0) { + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + cnt--; + } + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + mutex_unlock(&ac->cmd_lock); + return 0; +} + +void q6asm_audio_client_free(struct audio_client *ac) +{ + int loopcnt; + struct audio_port_data *port; + + if (!ac) { + pr_err("%s: ac %pK\n", __func__, ac); + return; + } + if (!ac->session) { + pr_err("%s: ac session invalid\n", __func__); + return; + } + + mutex_lock(&session_lock); + + pr_debug("%s: Session id %d\n", __func__, ac->session); + if (ac->io_mode & SYNC_IO_MODE) { + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + if (!port->buf) + continue; + pr_debug("%s: loopcnt = %d\n", + __func__, loopcnt); + q6asm_audio_client_buf_free(loopcnt, ac); + } + } + + rtac_set_asm_handle(ac->session, NULL); + apr_deregister(ac->apr2); + apr_deregister(ac->apr); + q6asm_mmap_apr_dereg(); + ac->apr2 = NULL; + ac->apr = NULL; + ac->mmap_apr = NULL; + q6asm_session_free(ac); + + pr_debug("%s: APR De-Register\n", __func__); + +/*done:*/ + kfree(ac); + ac = NULL; + mutex_unlock(&session_lock); +} + +int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1) +{ + uint32_t mode; + int ret = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + ac->io_mode &= 0xFF00; + mode = (mode1 & 0xF); + + pr_debug("%s: ac->mode after anding with FF00:0x%x,\n", + __func__, ac->io_mode); + + if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) { + ac->io_mode |= mode1; + pr_debug("%s: Set Mode to 0x%x\n", __func__, ac->io_mode); + } else { + pr_err("%s: Not an valid IO Mode:%d\n", __func__, ac->io_mode); + ret = -EINVAL; + } + + return ret; +} + +void *q6asm_mmap_apr_reg(void) +{ + if ((atomic_read(&this_mmap.ref_cnt) == 0) || + (this_mmap.apr == NULL)) { + this_mmap.apr = apr_register("ADSP", "ASM", + (apr_fn)q6asm_srvc_callback, + 0x0FFFFFFFF, &this_mmap); + if (this_mmap.apr == NULL) { + pr_debug("%s: Unable to register APR ASM common port\n", + __func__); + goto fail; + } + } + atomic_inc(&this_mmap.ref_cnt); + + return this_mmap.apr; +fail: + return NULL; +} + +struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) +{ + struct audio_client *ac; + int n; + int lcnt = 0; + int rc = 0; + + ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL); + if (!ac) + return NULL; + + mutex_lock(&session_lock); + n = q6asm_session_alloc(ac); + if (n <= 0) { + pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n); + mutex_unlock(&session_lock); + goto fail_session; + } + ac->session = n; + ac->cb = cb; + ac->path_delay = UINT_MAX; + ac->priv = priv; + ac->io_mode = SYNC_IO_MODE; + ac->perf_mode = LEGACY_PCM_MODE; + ac->fptr_cache_ops = NULL; + /* DSP expects stream id from 1 */ + ac->stream_id = 1; + ac->apr = apr_register("ADSP", "ASM", + (apr_fn)q6asm_callback, + ((ac->session) << 8 | 0x0001), + ac); + + if (ac->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + mutex_unlock(&session_lock); + goto fail_apr1; + } + ac->apr2 = apr_register("ADSP", "ASM", + (apr_fn)q6asm_callback, + ((ac->session) << 8 | 0x0002), + ac); + + if (ac->apr2 == NULL) { + pr_err("%s: Registration with APR-2 failed\n", __func__); + mutex_unlock(&session_lock); + goto fail_apr2; + } + + rtac_set_asm_handle(n, ac->apr); + + pr_debug("%s: Registering the common port with APR\n", __func__); + ac->mmap_apr = q6asm_mmap_apr_reg(); + if (ac->mmap_apr == NULL) { + mutex_unlock(&session_lock); + goto fail_mmap; + } + + init_waitqueue_head(&ac->cmd_wait); + init_waitqueue_head(&ac->time_wait); + init_waitqueue_head(&ac->mem_wait); + atomic_set(&ac->time_flag, 1); + atomic_set(&ac->reset, 0); + INIT_LIST_HEAD(&ac->port[0].mem_map_handle); + INIT_LIST_HEAD(&ac->port[1].mem_map_handle); + pr_debug("%s: mem_map_handle list init'ed\n", __func__); + mutex_init(&ac->cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&ac->port[lcnt].lock); + spin_lock_init(&ac->port[lcnt].dsp_lock); + } + atomic_set(&ac->cmd_state, 0); + atomic_set(&ac->mem_state, 0); + + rc = send_asm_custom_topology(ac); + if (rc < 0) { + mutex_unlock(&session_lock); + goto fail_mmap; + } + + pr_debug("%s: session[%d]\n", __func__, ac->session); + + mutex_unlock(&session_lock); + + return ac; +fail_mmap: + apr_deregister(ac->apr2); +fail_apr2: + apr_deregister(ac->apr); +fail_apr1: + q6asm_session_free(ac); +fail_session: + kfree(ac); + return NULL; +} + +struct audio_client *q6asm_get_audio_client(int session_id) +{ + if (session_id == ASM_CONTROL_SESSION) + return &common_client; + + if ((session_id <= 0) || (session_id > ASM_ACTIVE_STREAMS_ALLOWED)) { + pr_err("%s: invalid session: %d\n", __func__, session_id); + goto err; + } + + if (!session[session_id]) { + pr_err("%s: session not active: %d\n", __func__, session_id); + goto err; + } + return session[session_id]; +err: + return NULL; +} + +int q6asm_audio_client_buf_alloc(unsigned int dir, + struct audio_client *ac, + unsigned int bufsz, + uint32_t bufcnt) +{ + int cnt = 0; + int rc = 0; + struct audio_buffer *buf; + size_t len; + + if (!(ac) || !(bufsz) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK bufsz %d dir %d\n", __func__, ac, bufsz, + dir); + return -EINVAL; + } + + pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session, + bufsz, bufcnt); + + if (ac->session <= 0 || ac->session > 8) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + ac->session); + goto fail; + } + + if (ac->io_mode & SYNC_IO_MODE) { + if (ac->port[dir].buf) { + pr_debug("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + if (bufcnt > (U32_MAX/sizeof(struct audio_buffer))) { + pr_err("%s: Buffer size overflows", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + while (cnt < bufcnt) { + if (bufsz > 0) { + if (!buf[cnt].data) { + rc = msm_audio_ion_alloc("asm_client", + &buf[cnt].client, &buf[cnt].handle, + bufsz, + (ion_phys_addr_t *)&buf[cnt].phys, + &len, + &buf[cnt].data); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + buf[cnt].used = 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s: data[%pK]phys[%pK][%pK]\n", + __func__, + buf[cnt].data, + &buf[cnt].phys, + &buf[cnt].phys); + cnt++; + } + } + } + ac->port[dir].max_buf_cnt = cnt; + + mutex_unlock(&ac->cmd_lock); + rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 0); + if (rc < 0) { + pr_err("%s: CMD Memory_map_regions failed %d for size %d\n", + __func__, rc, bufsz); + goto fail; + } + } + return 0; +fail: + q6asm_audio_client_buf_free(dir, ac); + return -EINVAL; +} + +int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt) +{ + int cnt = 0; + int rc = 0; + struct audio_buffer *buf; + size_t len; + int bytes_to_alloc; + + if (!(ac) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return -EINVAL; + } + + pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", + __func__, ac->session, + bufsz, bufcnt); + + if (ac->session <= 0 || ac->session > 8) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + ac->session); + goto fail; + } + + if (ac->port[dir].buf) { + pr_err("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + pr_err("%s: buffer allocation failed\n", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + /* check for integer overflow */ + if ((bufcnt > 0) && ((INT_MAX / bufcnt) < bufsz)) { + pr_err("%s: integer overflow\n", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + bytes_to_alloc = bufsz * bufcnt; + + /* The size to allocate should be multiple of 4K bytes */ + bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); + + rc = msm_audio_ion_alloc("asm_client", &buf[0].client, &buf[0].handle, + bytes_to_alloc, + (ion_phys_addr_t *)&buf[0].phys, &len, + &buf[0].data); + if (rc) { + pr_err("%s: Audio ION alloc is failed, rc = %d\n", + __func__, rc); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + buf[0].used = dir ^ 1; + buf[0].size = bufsz; + buf[0].actual_size = bufsz; + cnt = 1; + while (cnt < bufcnt) { + if (bufsz > 0) { + buf[cnt].data = buf[0].data + (cnt * bufsz); + buf[cnt].phys = buf[0].phys + (cnt * bufsz); + if (!buf[cnt].data) { + pr_err("%s: Buf alloc failed\n", + __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf[cnt].used = dir ^ 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s: data[%pK]phys[%pK][%pK]\n", + __func__, + buf[cnt].data, + &buf[cnt].phys, + &buf[cnt].phys); + } + cnt++; + } + ac->port[dir].max_buf_cnt = cnt; + mutex_unlock(&ac->cmd_lock); + rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 1); + if (rc < 0) { + pr_err("%s: CMD Memory_map_regions failed %d for size %d\n", + __func__, rc, bufsz); + goto fail; + } + return 0; +fail: + q6asm_audio_client_buf_free_contiguous(dir, ac); + return -EINVAL; +} + +static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) +{ + uint32_t dir = 0; + uint32_t i = IN; + uint32_t *payload; + unsigned long dsp_flags; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + union asm_token_struct asm_token; + + struct audio_client *ac = NULL; + struct audio_port_data *port; + + if (!data) { + pr_err("%s: Invalid CB\n", __func__); + return 0; + } + + payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", + __func__, + data->reset_event, + data->reset_proc, + this_mmap.apr); + atomic_set(&this_mmap.ref_cnt, 0); + apr_reset(this_mmap.apr); + this_mmap.apr = NULL; + for (; i <= OUT; i++) { + list_for_each_safe(ptr, next, + &common_client.port[i].mem_map_handle) { + buf_node = list_entry(ptr, + struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == + common_client.port[i].buf->phys) { + list_del(&buf_node->list); + kfree(buf_node); + } + } + pr_debug("%s: Clearing custom topology\n", __func__); + } + this_mmap.apr = NULL; + + cal_utils_clear_cal_block_q6maps(ASM_MAX_CAL_TYPES, cal_data); + common_client.mmap_apr = NULL; + mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + set_custom_topology = 1; + mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + topology_map_handle = 0; + rtac_clear_mapping(ASM_RTAC_CAL); + return 0; + } + asm_token.token = data->token; + ac = q6asm_get_audio_client(asm_token._token.session_id); + dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET); + + if (!ac) { + pr_debug("%s: session[%d] already freed\n", + __func__, asm_token._token.session_id); + return 0; + } + + if (data->payload_size > sizeof(int)) { + pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n", + __func__, payload[0], payload[1], data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port, asm_token._token.session_id, dir); + pr_debug("%s:Payload = [0x%x] status[0x%x]\n", + __func__, payload[0], payload[1]); + } else if (data->payload_size == sizeof(int)) { + pr_debug("%s:ptr0[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n", + __func__, payload[0], data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port, asm_token._token.session_id, dir); + pr_debug("%s:Payload = [0x%x]\n", + __func__, payload[0]); + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + switch (payload[0]) { + case ASM_CMD_SHARED_MEM_MAP_REGIONS: + case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: + case ASM_CMD_ADD_TOPOLOGIES: + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n", + __func__, payload[0], payload[1], + asm_token._token.session_id); + if (payload[0] == + ASM_CMD_SHARED_MEM_UNMAP_REGIONS) + atomic_set(&ac->unmap_cb_success, 0); + + atomic_set(&ac->mem_state, payload[1]); + wake_up(&ac->mem_wait); + } else { + if (payload[0] == + ASM_CMD_SHARED_MEM_UNMAP_REGIONS) + atomic_set(&ac->unmap_cb_success, 1); + } + + if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) + wake_up(&ac->mem_wait); + dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n", + __func__, payload[0], payload[1]); + break; + default: + pr_debug("%s: command[0x%x] not expecting rsp\n", + __func__, payload[0]); + break; + } + return 0; + } + + port = &ac->port[dir]; + + switch (data->opcode) { + case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:{ + pr_debug("%s:PL#0[0x%x] dir=0x%x s_id=0x%x\n", + __func__, payload[0], dir, asm_token._token.session_id); + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) { + ac->port[dir].tmp_hdl = payload[0]; + wake_up(&ac->mem_wait); + } + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + break; + } + case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{ + pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n", + __func__, payload[0], payload[1]); + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) + wake_up(&ac->mem_wait); + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + break; + } + default: + pr_debug("%s: command[0x%x]success [0x%x]\n", + __func__, payload[0], payload[1]); + } + if (ac->cb) + ac->cb(data->opcode, data->token, + data->payload, ac->priv); + return 0; +} + +static void q6asm_process_mtmx_get_param_rsp(struct audio_client *ac, + struct asm_mtmx_strtr_get_params_cmdrsp *cmdrsp) +{ + struct asm_session_mtmx_strtr_param_session_time_v3_t *time; + + if (cmdrsp->err_code) { + dev_err_ratelimited(ac->dev, + "%s: err=%x, mod_id=%x, param_id=%x\n", + __func__, cmdrsp->err_code, + cmdrsp->param_info.module_id, + cmdrsp->param_info.param_id); + return; + } + dev_dbg_ratelimited(ac->dev, + "%s: mod_id=%x, param_id=%x\n", __func__, + cmdrsp->param_info.module_id, + cmdrsp->param_info.param_id); + + switch (cmdrsp->param_info.module_id) { + case ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC: + switch (cmdrsp->param_info.param_id) { + case ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3: + time = &cmdrsp->param_data.session_time; + dev_vdbg(ac->dev, "%s: GET_TIME_V3, time_lsw=%x, time_msw=%x\n", + __func__, time->session_time_lsw, + time->session_time_msw); + ac->time_stamp = (uint64_t)(((uint64_t) + time->session_time_msw << 32) | + time->session_time_lsw); + if (time->flags & + ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK) + dev_warn_ratelimited(ac->dev, + "%s: recv inval tstmp\n", + __func__); + if (atomic_cmpxchg(&ac->time_flag, 1, 0)) + wake_up(&ac->time_wait); + + break; + default: + dev_err(ac->dev, "%s: unexpected param_id %x\n", + __func__, cmdrsp->param_info.param_id); + break; + } + break; + default: + dev_err(ac->dev, "%s: unexpected mod_id %x\n", __func__, + cmdrsp->param_info.module_id); + break; + } +} + +static int32_t q6asm_callback(struct apr_client_data *data, void *priv) +{ + int i = 0; + struct audio_client *ac = (struct audio_client *)priv; + unsigned long dsp_flags; + uint32_t *payload; + uint32_t wakeup_flag = 1; + int32_t ret = 0; + union asm_token_struct asm_token; + uint8_t buf_index; + + if (ac == NULL) { + pr_err("%s: ac NULL\n", __func__); + return -EINVAL; + } + if (data == NULL) { + pr_err("%s: data NULL\n", __func__); + return -EINVAL; + } + if (!q6asm_is_valid_audio_client(ac)) { + pr_err("%s: audio client pointer is invalid, ac = %pK\n", + __func__, ac); + return -EINVAL; + } + + if (ac->session <= 0 || ac->session > 8) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + ac->session); + return -EINVAL; + } + + payload = data->payload; + asm_token.token = data->token; + if (q6asm_get_flag_from_token(&asm_token, ASM_CMD_NO_WAIT_OFFSET)) { + pr_debug("%s: No wait command opcode[0x%x] cmd_opcode:%x\n", + __func__, data->opcode, payload ? payload[0] : 0); + wakeup_flag = 0; + } + + if (data->opcode == RESET_EVENTS) { + mutex_lock(&ac->cmd_lock); + atomic_set(&ac->reset, 1); + if (ac->apr == NULL) + ac->apr = ac->apr2; + pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", + __func__, + data->reset_event, data->reset_proc, ac->apr); + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + apr_reset(ac->apr); + ac->apr = NULL; + atomic_set(&ac->time_flag, 0); + atomic_set(&ac->cmd_state, 0); + atomic_set(&ac->mem_state, 0); + wake_up(&ac->time_wait); + wake_up(&ac->cmd_wait); + wake_up(&ac->mem_wait); + mutex_unlock(&ac->cmd_lock); + return 0; + } + + dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x] token[0x%x]payload_size[%d] src[%d] dest[%d]\n", + __func__, + ac->session, data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port); + if ((data->opcode != ASM_DATA_EVENT_RENDERED_EOS) && + (data->opcode != ASM_DATA_EVENT_EOS) && + (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { + if (payload == NULL) { + pr_err("%s: payload is null\n", __func__); + return -EINVAL; + } + dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", + __func__, payload[0], payload[1], data->opcode); + } + if (data->opcode == APR_BASIC_RSP_RESULT) { + switch (payload[0]) { + case ASM_STREAM_CMD_SET_PP_PARAMS_V2: + if (rtac_make_asm_callback(ac->session, payload, + data->payload_size)) + break; + case ASM_SESSION_CMD_PAUSE: + case ASM_SESSION_CMD_SUSPEND: + case ASM_DATA_CMD_EOS: + case ASM_STREAM_CMD_CLOSE: + case ASM_STREAM_CMD_FLUSH: + case ASM_SESSION_CMD_RUN_V2: + case ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS: + case ASM_STREAM_CMD_FLUSH_READBUFS: + pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] src %d dest %d\n", + __func__, ac->session, data->opcode, data->token, + payload[0], data->src_port, data->dest_port); + ret = q6asm_is_valid_session(data, priv); + if (ret != 0) { + pr_err("%s: session invalid %d\n", __func__, ret); + return ret; + } + case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2: + case ASM_STREAM_CMD_OPEN_READ_V3: + case ASM_STREAM_CMD_OPEN_WRITE_V3: + case ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE: + case ASM_STREAM_CMD_OPEN_PUSH_MODE_READ: + case ASM_STREAM_CMD_OPEN_READWRITE_V2: + case ASM_STREAM_CMD_OPEN_LOOPBACK_V2: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: + case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS: + case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED: + pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + payload[0], payload[1], + data->src_port, data->dest_port); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + if (wakeup_flag) { + atomic_set(&ac->cmd_state, payload[1]); + wake_up(&ac->cmd_wait); + } + return 0; + } + if (atomic_read(&ac->cmd_state) && wakeup_flag) { + atomic_set(&ac->cmd_state, 0); + wake_up(&ac->cmd_wait); + } + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + break; + case ASM_CMD_ADD_TOPOLOGIES: + pr_debug("%s:Payload = [0x%x]stat[0x%x]\n", + __func__, payload[0], payload[1]); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + if (wakeup_flag) { + atomic_set(&ac->mem_state, payload[1]); + wake_up(&ac->mem_wait); + } + return 0; + } + if (atomic_read(&ac->mem_state) && wakeup_flag) { + atomic_set(&ac->mem_state, 0); + wake_up(&ac->mem_wait); + } + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + break; + case ASM_DATA_EVENT_WATERMARK: { + pr_debug("%s: Watermark opcode[0x%x] status[0x%x]", + __func__, payload[0], payload[1]); + break; + } + case ASM_STREAM_CMD_GET_PP_PARAMS_V2: + pr_debug("%s: ASM_STREAM_CMD_GET_PP_PARAMS_V2 session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */ + if (payload[1] != 0) { + pr_err("%s: ASM get param error = %d, resuming\n", + __func__, payload[1]); + rtac_make_asm_callback(ac->session, payload, + data->payload_size); + } + break; + default: + pr_debug("%s: command[0x%x] not expecting rsp\n", + __func__, payload[0]); + break; + } + return 0; + } + + switch (data->opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2:{ + struct audio_port_data *port = &ac->port[IN]; + + dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]", + __func__, payload[0], payload[1], + data->token); + if (ac->io_mode & SYNC_IO_MODE) { + if (port->buf == NULL) { + pr_err("%s: Unexpected Write Done\n", + __func__); + return -EINVAL; + } + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + buf_index = asm_token._token.buf_index; + if (lower_32_bits(port->buf[buf_index].phys) != + payload[0] || + msm_audio_populate_upper_32_bits( + port->buf[buf_index].phys) != payload[1]) { + pr_debug("%s: Expected addr %pK\n", + __func__, &port->buf[buf_index].phys); + pr_err("%s: rxedl[0x%x] rxedu [0x%x]\n", + __func__, payload[0], payload[1]); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + return -EINVAL; + } + port->buf[buf_index].used = 1; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + config_debug_fs_write_cb(); + + for (i = 0; i < port->max_buf_cnt; i++) + dev_vdbg(ac->dev, "%s %d\n", + __func__, port->buf[i].used); + + } + break; + } + case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2: + pr_debug("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, data->opcode, + data->token, + data->src_port, data->dest_port); + if (payload[0] != 0) { + pr_err("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 returned error = 0x%x\n", + __func__, payload[0]); + } else if (generic_get_data) { + generic_get_data->valid = 1; + if (generic_get_data->is_inband) { + pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n", + __func__, payload[1], payload[2], payload[3]); + generic_get_data->size_in_ints = payload[3]>>2; + for (i = 0; i < payload[3]>>2; i++) { + generic_get_data->ints[i] = + payload[4+i]; + pr_debug("%s: ASM callback val %i = %i\n", + __func__, i, payload[4+i]); + } + pr_debug("%s: callback size in ints = %i\n", + __func__, + generic_get_data->size_in_ints); + } + if (atomic_read(&ac->cmd_state) && wakeup_flag) { + atomic_set(&ac->cmd_state, 0); + wake_up(&ac->cmd_wait); + } + break; + } + rtac_make_asm_callback(ac->session, payload, + data->payload_size); + break; + case ASM_DATA_EVENT_READ_DONE_V2:{ + + struct audio_port_data *port = &ac->port[OUT]; + + config_debug_fs_read_cb(); + + dev_vdbg(ac->dev, "%s: ReadDone: status=%d buff_add=0x%x act_size=%d offset=%d\n", + __func__, payload[READDONE_IDX_STATUS], + payload[READDONE_IDX_BUFADD_LSW], + payload[READDONE_IDX_SIZE], + payload[READDONE_IDX_OFFSET]); + + dev_vdbg(ac->dev, "%s: ReadDone:msw_ts=%d lsw_ts=%d memmap_hdl=0x%x flags=%d id=%d num=%d\n", + __func__, payload[READDONE_IDX_MSW_TS], + payload[READDONE_IDX_LSW_TS], + payload[READDONE_IDX_MEMMAP_HDL], + payload[READDONE_IDX_FLAGS], + payload[READDONE_IDX_SEQ_ID], + payload[READDONE_IDX_NUMFRAMES]); + + if (ac->io_mode & SYNC_IO_MODE) { + if (port->buf == NULL) { + pr_err("%s: Unexpected Write Done\n", __func__); + return -EINVAL; + } + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + buf_index = asm_token._token.buf_index; + port->buf[buf_index].used = 0; + if (lower_32_bits(port->buf[buf_index].phys) != + payload[READDONE_IDX_BUFADD_LSW] || + msm_audio_populate_upper_32_bits( + port->buf[buf_index].phys) != + payload[READDONE_IDX_BUFADD_MSW]) { + dev_vdbg(ac->dev, "%s: Expected addr %pK\n", + __func__, &port->buf[buf_index].phys); + pr_err("%s: rxedl[0x%x] rxedu[0x%x]\n", + __func__, + payload[READDONE_IDX_BUFADD_LSW], + payload[READDONE_IDX_BUFADD_MSW]); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } + port->buf[buf_index].actual_size = + payload[READDONE_IDX_SIZE]; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + } + break; + } + case ASM_DATA_EVENT_EOS: + case ASM_DATA_EVENT_RENDERED_EOS: + pr_debug("%s: EOS ACK received: rxed session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + break; + case ASM_SESSION_EVENTX_OVERFLOW: + pr_debug("%s: ASM_SESSION_EVENTX_OVERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + break; + case ASM_SESSION_EVENT_RX_UNDERFLOW: + pr_debug("%s: ASM_SESSION_EVENT_RX_UNDERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + break; + case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3: + dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n", + __func__, + payload[0], payload[1], payload[2]); + ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) | + payload[1]); + if (atomic_cmpxchg(&ac->time_flag, 1, 0)) + wake_up(&ac->time_wait); + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n", + __func__, + payload[0], payload[1], payload[2], + payload[3]); + break; + case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2: + q6asm_process_mtmx_get_param_rsp(ac, (void *) payload); + break; + case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2: + pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n", + __func__, ac->session, payload[0], payload[2], + payload[1]); + if (payload[0] == 0) { + atomic_set(&ac->cmd_state, 0); + /* ignore msw, as a delay that large shouldn't happen */ + ac->path_delay = payload[1]; + } else { + atomic_set(&ac->cmd_state, payload[0]); + ac->path_delay = UINT_MAX; + } + wake_up(&ac->cmd_wait); + break; + } + if (ac->cb) + ac->cb(data->opcode, data->token, + data->payload, ac->priv); + + return 0; +} + +void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size, + uint32_t *index) +{ + void *data; + unsigned char idx; + struct audio_port_data *port; + + if (!ac || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return NULL; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + + mutex_lock(&port->lock); + idx = port->cpu_buf; + if (port->buf == NULL) { + pr_err("%s: Buffer pointer null\n", __func__); + mutex_unlock(&port->lock); + return NULL; + } + /* dir 0: used = 0 means buf in use + * dir 1: used = 1 means buf in use + */ + if (port->buf[idx].used == dir) { + /* To make it more robust, we could loop and get the + * next avail buf, its risky though + */ + pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n", + __func__, idx, dir); + mutex_unlock(&port->lock); + return NULL; + } + *size = port->buf[idx].actual_size; + *index = port->cpu_buf; + data = port->buf[idx].data; + dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n", + __func__, + ac->session, + port->cpu_buf, + data, *size); + /* By default increase the cpu_buf cnt + * user accesses this function,increase cpu + * buf(to avoid another api) + */ + port->buf[idx].used = dir; + port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf, + port->max_buf_cnt); + mutex_unlock(&port->lock); + return data; + } + return NULL; +} + +int q6asm_cpu_buf_release(int dir, struct audio_client *ac) +{ + struct audio_port_data *port; + int ret = 0; + int idx; + + if (!ac || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + ret = -EINVAL; + goto exit; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + mutex_lock(&port->lock); + idx = port->cpu_buf; + if (port->cpu_buf == 0) { + port->cpu_buf = port->max_buf_cnt - 1; + } else if (port->cpu_buf < port->max_buf_cnt) { + port->cpu_buf = port->cpu_buf - 1; + } else { + pr_err("%s: buffer index(%d) out of range\n", + __func__, port->cpu_buf); + ret = -EINVAL; + mutex_unlock(&port->lock); + goto exit; + } + port->buf[port->cpu_buf].used = dir ^ 1; + mutex_unlock(&port->lock); + } +exit: + return ret; +} + +void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *index) +{ + void *data; + unsigned char idx; + struct audio_port_data *port; + + if (!ac || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return NULL; + } + + port = &ac->port[dir]; + + idx = port->cpu_buf; + if (port->buf == NULL) { + pr_err("%s: Buffer pointer null\n", __func__); + return NULL; + } + /* + * dir 0: used = 0 means buf in use + * dir 1: used = 1 means buf in use + */ + if (port->buf[idx].used == dir) { + /* + * To make it more robust, we could loop and get the + * next avail buf, its risky though + */ + pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n", + __func__, idx, dir); + return NULL; + } + *size = port->buf[idx].actual_size; + *index = port->cpu_buf; + data = port->buf[idx].data; + dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n", + __func__, ac->session, port->cpu_buf, + data, *size); + /* + * By default increase the cpu_buf cnt + * user accesses this function,increase cpu + * buf(to avoid another api) + */ + port->buf[idx].used = dir; + port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf, + port->max_buf_cnt); + return data; +} + +int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) +{ + int ret = -1; + struct audio_port_data *port; + uint32_t idx; + + if (!ac || (dir != OUT)) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return ret; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + + mutex_lock(&port->lock); + idx = port->dsp_buf; + + if (port->buf[idx].used == (dir ^ 1)) { + /* To make it more robust, we could loop and get the + * next avail buf, its risky though + */ + pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n", + __func__, idx, dir); + mutex_unlock(&port->lock); + return ret; + } + dev_vdbg(ac->dev, "%s: session[%d]dsp_buf=%d cpu_buf=%d\n", + __func__, + ac->session, port->dsp_buf, port->cpu_buf); + ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1); + mutex_unlock(&port->lock); + } + return ret; +} + +static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id) +{ + dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n", + __func__, pkt_size, cmd_flg, ac->session, stream_id); + mutex_lock(&ac->cmd_lock); + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL", __func__); + mutex_unlock(&ac->cmd_lock); + return; + } + + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); + hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); + if (cmd_flg) + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + hdr->pkt_size = pkt_size; + mutex_unlock(&ac->cmd_lock); +} + +static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg) +{ + __q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, ac->stream_id); +} + +static void q6asm_stream_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg, int32_t stream_id) +{ + __q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, stream_id); +} + +static void __q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg, + uint32_t stream_id, u8 no_wait_flag) +{ + dev_vdbg(ac->dev, "%s: pkt_size = %d, cmd_flg = %d, session = %d stream_id=%d\n", + __func__, pkt_size, cmd_flg, ac->session, stream_id); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + if (ac->apr == NULL) { + pr_err("%s: AC APR is NULL", __func__); + return; + } + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); + hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); + if (cmd_flg) { + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + no_wait_flag); + + } + hdr->pkt_size = pkt_size; +} + +static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg) +{ + __q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg, + ac->stream_id, WAIT_CMD); +} + +static void q6asm_stream_add_hdr_async(struct audio_client *ac, + struct apr_hdr *hdr, uint32_t pkt_size, + uint32_t cmd_flg, int32_t stream_id) +{ + __q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg, + stream_id, NO_WAIT_CMD); +} + +static void q6asm_add_hdr_custom_topology(struct audio_client *ac, + struct apr_hdr *hdr, + uint32_t pkt_size) +{ + pr_debug("%s: pkt_size=%d session=%d\n", + __func__, pkt_size, ac->session); + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return; + } + + mutex_lock(&ac->cmd_lock); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01; + hdr->dest_port = 0; + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + hdr->pkt_size = pkt_size; + mutex_unlock(&ac->cmd_lock); +} + +static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr, + u32 pkt_size, int dir) +{ + pr_debug("%s: pkt size=%d\n", + __func__, pkt_size); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0; + hdr->dest_port = 0; + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + dir, + WAIT_CMD); + hdr->pkt_size = pkt_size; +} + +static int __q6asm_open_read(struct audio_client *ac, + uint32_t format, uint16_t bits_per_sample, + uint32_t pcm_format_block_ver, + bool ts_mode) +{ + int rc = 0x00; + struct asm_stream_cmd_open_read_v3 open; + + config_debug_fs_reset_index(); + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; + /* Stream prio : High, provide meta info with encoded frames */ + open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX; + + open.preprocopo_id = q6asm_get_asm_topology_cal(); + if ((open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || + (open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS)) + open.preprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE; + open.bits_per_sample = bits_per_sample; + open.mode_flags = 0x0; + + ac->topology = open.preprocopo_id; + ac->app_type = q6asm_get_asm_app_type_cal(); + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) { + open.mode_flags |= ASM_LOW_LATENCY_TX_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; + } else { + open.mode_flags |= ASM_LEGACY_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; + } + + switch (format) { + case FORMAT_LINEAR_PCM: + open.mode_flags |= 0x00; + open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver); + if (ts_mode) + open.mode_flags |= ABSOLUTE_TIMESTAMP_ENABLE; + break; + case FORMAT_MPEG4_AAC: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_G711_ALAW_FS: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS; + break; + case FORMAT_G711_MLAW_FS: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS; + break; + case FORMAT_V13K: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS; + break; + case FORMAT_EVRC: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS; + break; + case FORMAT_AMRNB: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS; + break; + case FORMAT_AMRWB: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS; + break; + default: + pr_err("%s: Invalid format 0x%x\n", + __func__, format); + rc = -EINVAL; + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for open read\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + ac->io_mode |= TUN_READ_IO_MODE; + + return 0; +fail_cmd: + return rc; +} + +int q6asm_open_read(struct audio_client *ac, + uint32_t format) +{ + return __q6asm_open_read(ac, format, 16, + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/, + false/*ts_mode*/); +} + +int q6asm_open_read_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/, + false/*ts_mode*/); +} + +/* + * asm_open_read_v3 - Opens audio capture session + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + */ +int q6asm_open_read_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V3/*media fmt block ver*/, + false/*ts_mode*/); +} +EXPORT_SYMBOL(q6asm_open_read_v3); + +/* + * asm_open_read_v4 - Opens audio capture session + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + */ +int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/, + true/*ts_mode*/); +} +EXPORT_SYMBOL(q6asm_open_read_v4); + +int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, + uint32_t passthrough_flag) +{ + int rc = 0; + struct asm_stream_cmd_open_write_compressed open; + + if (ac == NULL) { + pr_err("%s: ac[%pK] NULL\n", __func__, ac); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: APR handle[%pK] NULL\n", __func__, ac->apr); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session, + format); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED; + atomic_set(&ac->cmd_state, -1); + + switch (format) { + case FORMAT_AC3: + open.fmt_id = ASM_MEDIA_FMT_AC3; + break; + case FORMAT_EAC3: + open.fmt_id = ASM_MEDIA_FMT_EAC3; + break; + case FORMAT_DTS: + open.fmt_id = ASM_MEDIA_FMT_DTS; + break; + case FORMAT_DSD: + open.fmt_id = ASM_MEDIA_FMT_DSD; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, format); + rc = -EINVAL; + goto fail_cmd; + } + /* Below flag indicates the DSP that Compressed audio input + * stream is not IEC 61937 or IEC 60958 packetizied + */ + if (passthrough_flag == COMPRESSED_PASSTHROUGH || + passthrough_flag == COMPRESSED_PASSTHROUGH_DSD) { + open.flags = 0x0; + pr_debug("%s: Flag 0 COMPRESSED_PASSTHROUGH\n", __func__); + } else if (passthrough_flag == COMPRESSED_PASSTHROUGH_CONVERT) { + open.flags = 0x8; + pr_debug("%s: Flag 8 - COMPRESSED_PASSTHROUGH_CONVERT\n", + __func__); + } else { + pr_err("%s: Invalid passthrough type[%d]\n", + __func__, passthrough_flag); + rc = -EINVAL; + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + if (!rc) { + pr_err("%s: timeout. waited for OPEN_WRITE_COMPR rc[%d]\n", + __func__, rc); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; + +fail_cmd: + return rc; +} + +static int __q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, uint32_t stream_id, + bool is_gapless_mode, + uint32_t pcm_format_block_ver) +{ + int rc = 0x00; + struct asm_stream_cmd_open_write_v3 open; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + dev_vdbg(ac->dev, "%s: session[%d] wr_format[0x%x]\n", + __func__, ac->session, format); + + q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&open.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + dev_vdbg(ac->dev, "%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, open.hdr.token, stream_id, ac->session); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; + open.mode_flags = 0x00; + if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE) + open.mode_flags |= ASM_ULL_POST_PROCESSING_STREAM_SESSION; + else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_ULTRA_LOW_LATENCY_STREAM_SESSION; + else if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; + else { + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + if (is_gapless_mode) + open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG; + } + + /* source endpoint : matrix */ + open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; + open.bits_per_sample = bits_per_sample; + + open.postprocopo_id = q6asm_get_asm_topology_cal(); + if ((ac->perf_mode != LEGACY_PCM_MODE) && + ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || + (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS))) + open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE; + + /* For DTS EAGLE only, force 24 bit */ + if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || + (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS)) + open.bits_per_sample = 24; + + pr_debug("%s: perf_mode %d asm_topology 0x%x bps %d\n", __func__, + ac->perf_mode, open.postprocopo_id, open.bits_per_sample); + + /* + * For Gapless playback it will use the same session for next stream, + * So use the same topology + */ + if (!ac->topology) { + ac->topology = open.postprocopo_id; + ac->app_type = q6asm_get_asm_app_type_cal(); + } + switch (format) { + case FORMAT_LINEAR_PCM: + open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver); + break; + case FORMAT_MPEG4_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_MPEG4_MULTI_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_WMA_V9: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2; + break; + case FORMAT_WMA_V10PRO: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2; + break; + case FORMAT_MP3: + open.dec_fmt_id = ASM_MEDIA_FMT_MP3; + break; + case FORMAT_AC3: + open.dec_fmt_id = ASM_MEDIA_FMT_AC3; + break; + case FORMAT_EAC3: + open.dec_fmt_id = ASM_MEDIA_FMT_EAC3; + break; + case FORMAT_MP2: + open.dec_fmt_id = ASM_MEDIA_FMT_MP2; + break; + case FORMAT_FLAC: + open.dec_fmt_id = ASM_MEDIA_FMT_FLAC; + break; + case FORMAT_ALAC: + open.dec_fmt_id = ASM_MEDIA_FMT_ALAC; + break; + case FORMAT_VORBIS: + open.dec_fmt_id = ASM_MEDIA_FMT_VORBIS; + break; + case FORMAT_APE: + open.dec_fmt_id = ASM_MEDIA_FMT_APE; + break; + case FORMAT_DSD: + open.dec_fmt_id = ASM_MEDIA_FMT_DSD; + break; + default: + pr_err("%s: Invalid format 0x%x\n", __func__, format); + rc = -EINVAL; + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for open write\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + ac->io_mode |= TUN_WRITE_IO_MODE; + + return 0; +fail_cmd: + return rc; +} + +int q6asm_open_write(struct audio_client *ac, uint32_t format) +{ + return __q6asm_open_write(ac, format, 16, ac->stream_id, + false /*gapless*/, + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); +} + +int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); +} + +/* + * q6asm_open_write_v3 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v3); + +/* + * q6asm_open_write_v4 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v4); + +int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); +} + +/* + * q6asm_stream_open_write_v3 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v3); + +/* + * q6asm_stream_open_write_v4 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v4); + +static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format, bool is_meta_data_mode, + uint32_t bits_per_sample, + bool overwrite_topology, int topology) +{ + int rc = 0x00; + struct asm_stream_cmd_open_readwrite_v2 open; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + pr_debug("%s: wr_format[0x%x]rd_format[0x%x]\n", + __func__, wr_format, rd_format); + + ac->io_mode |= NT_MODE; + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE_V2; + + open.mode_flags = is_meta_data_mode ? BUFFER_META_ENABLE : 0; + open.bits_per_sample = bits_per_sample; + /* source endpoint : matrix */ + open.postprocopo_id = q6asm_get_asm_topology_cal(); + + open.postprocopo_id = overwrite_topology ? + topology : open.postprocopo_id; + ac->topology = open.postprocopo_id; + ac->app_type = q6asm_get_asm_app_type_cal(); + + /* For DTS EAGLE only, force 24 bit */ + if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || + (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER)) + open.bits_per_sample = 24; + + switch (wr_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + case FORMAT_MPEG4_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_MPEG4_MULTI_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_WMA_V9: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2; + break; + case FORMAT_WMA_V10PRO: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2; + break; + case FORMAT_AMRNB: + open.dec_fmt_id = ASM_MEDIA_FMT_AMRNB_FS; + break; + case FORMAT_AMRWB: + open.dec_fmt_id = ASM_MEDIA_FMT_AMRWB_FS; + break; + case FORMAT_AMR_WB_PLUS: + open.dec_fmt_id = ASM_MEDIA_FMT_AMR_WB_PLUS_V2; + break; + case FORMAT_V13K: + open.dec_fmt_id = ASM_MEDIA_FMT_V13K_FS; + break; + case FORMAT_EVRC: + open.dec_fmt_id = ASM_MEDIA_FMT_EVRC_FS; + break; + case FORMAT_EVRCB: + open.dec_fmt_id = ASM_MEDIA_FMT_EVRCB_FS; + break; + case FORMAT_EVRCWB: + open.dec_fmt_id = ASM_MEDIA_FMT_EVRCWB_FS; + break; + case FORMAT_MP3: + open.dec_fmt_id = ASM_MEDIA_FMT_MP3; + break; + case FORMAT_ALAC: + open.dec_fmt_id = ASM_MEDIA_FMT_ALAC; + break; + case FORMAT_APE: + open.dec_fmt_id = ASM_MEDIA_FMT_APE; + break; + case FORMAT_DSD: + open.dec_fmt_id = ASM_MEDIA_FMT_DSD; + case FORMAT_G711_ALAW_FS: + open.dec_fmt_id = ASM_MEDIA_FMT_G711_ALAW_FS; + break; + case FORMAT_G711_MLAW_FS: + open.dec_fmt_id = ASM_MEDIA_FMT_G711_MLAW_FS; + break; + default: + pr_err("%s: Invalid format 0x%x\n", + __func__, wr_format); + rc = -EINVAL; + goto fail_cmd; + } + + switch (rd_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + case FORMAT_MPEG4_AAC: + open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_G711_ALAW_FS: + open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS; + break; + case FORMAT_G711_MLAW_FS: + open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS; + break; + case FORMAT_V13K: + open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS; + break; + case FORMAT_EVRC: + open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS; + break; + case FORMAT_AMRNB: + open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS; + break; + case FORMAT_AMRWB: + open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS; + break; + case FORMAT_ALAC: + open.enc_cfg_id = ASM_MEDIA_FMT_ALAC; + break; + case FORMAT_APE: + open.enc_cfg_id = ASM_MEDIA_FMT_APE; + break; + default: + pr_err("%s: Invalid format 0x%x\n", + __func__, rd_format); + rc = -EINVAL; + goto fail_cmd; + } + dev_vdbg(ac->dev, "%s: rdformat[0x%x]wrformat[0x%x]\n", __func__, + open.enc_cfg_id, open.dec_fmt_id); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for open read-write\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} + +int q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format) +{ + return __q6asm_open_read_write(ac, rd_format, wr_format, + true/*meta data mode*/, + 16 /*bits_per_sample*/, + false /*overwrite_topology*/, 0); +} + +int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format, bool is_meta_data_mode, + uint32_t bits_per_sample, bool overwrite_topology, + int topology) +{ + return __q6asm_open_read_write(ac, rd_format, wr_format, + is_meta_data_mode, bits_per_sample, + overwrite_topology, topology); +} + +int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample) +{ + int rc = 0x00; + struct asm_stream_cmd_open_loopback_v2 open; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK_V2; + + open.mode_flags = 0; + open.src_endpointype = 0; + open.sink_endpointype = 0; + /* source endpoint : matrix */ + open.postprocopo_id = q6asm_get_asm_topology_cal(); + + ac->app_type = q6asm_get_asm_app_type_cal(); + ac->topology = open.postprocopo_id; + open.bits_per_sample = bits_per_sample; + open.reserved = 0; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", __func__, + open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for open_loopback\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} + +static +int q6asm_set_shared_circ_buff(struct audio_client *ac, + struct asm_stream_cmd_open_shared_io *open, + int bufsz, int bufcnt, + int dir) +{ + struct audio_buffer *buf_circ; + int bytes_to_alloc, rc; + size_t len; + + buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL); + + if (!buf_circ) { + rc = -ENOMEM; + goto done; + } + + mutex_lock(&ac->cmd_lock); + + ac->port[dir].buf = buf_circ; + + bytes_to_alloc = bufsz * bufcnt; + bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); + + rc = msm_audio_ion_alloc("audio_client", &buf_circ->client, + &buf_circ->handle, bytes_to_alloc, + (ion_phys_addr_t *)&buf_circ->phys, + &len, &buf_circ->data); + + if (rc) { + pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__, + rc); + mutex_unlock(&ac->cmd_lock); + kfree(buf_circ); + goto done; + } + + buf_circ->used = dir ^ 1; + buf_circ->size = bytes_to_alloc; + buf_circ->actual_size = bytes_to_alloc; + memset(buf_circ->data, 0, buf_circ->actual_size); + + ac->port[dir].max_buf_cnt = 1; + + open->shared_circ_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + open->shared_circ_buf_num_regions = 1; + open->shared_circ_buf_property_flag = 0x00; + open->shared_circ_buf_start_phy_addr_lsw = + lower_32_bits(buf_circ->phys); + open->shared_circ_buf_start_phy_addr_msw = + upper_32_bits(buf_circ->phys); + open->shared_circ_buf_size = bufsz * bufcnt; + + open->map_region_circ_buf.shm_addr_lsw = lower_32_bits(buf_circ->phys); + open->map_region_circ_buf.shm_addr_msw = upper_32_bits(buf_circ->phys); + open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc; + + mutex_unlock(&ac->cmd_lock); +done: + return rc; +} + + +static +int q6asm_set_shared_pos_buff(struct audio_client *ac, + struct asm_stream_cmd_open_shared_io *open, + int dir) +{ + struct audio_buffer *buf_pos = &ac->shared_pos_buf; + int rc; + size_t len; + int bytes_to_alloc = sizeof(struct asm_shared_position_buffer); + + mutex_lock(&ac->cmd_lock); + + bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); + + rc = msm_audio_ion_alloc("audio_client", &buf_pos->client, + &buf_pos->handle, bytes_to_alloc, + (ion_phys_addr_t *)&buf_pos->phys, &len, + &buf_pos->data); + + if (rc) { + pr_err("%s: Audio pos buf ION alloc is failed, rc = %d\n", + __func__, rc); + goto done; + } + + buf_pos->used = dir ^ 1; + buf_pos->size = bytes_to_alloc; + buf_pos->actual_size = bytes_to_alloc; + + open->shared_pos_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + open->shared_pos_buf_num_regions = 1; + open->shared_pos_buf_property_flag = 0x00; + open->shared_pos_buf_phy_addr_lsw = lower_32_bits(buf_pos->phys); + open->shared_pos_buf_phy_addr_msw = upper_32_bits(buf_pos->phys); + + open->map_region_pos_buf.shm_addr_lsw = lower_32_bits(buf_pos->phys); + open->map_region_pos_buf.shm_addr_msw = upper_32_bits(buf_pos->phys); + open->map_region_pos_buf.mem_size_bytes = bytes_to_alloc; + +done: + mutex_unlock(&ac->cmd_lock); + return rc; +} + +/* + * q6asm_open_shared_io: Open an ASM session for pull mode (playback) + * or push mode (capture). + * parameters + * config - session parameters (channels, bits_per_sample, sr) + * dir - stream direction (IN for playback, OUT for capture) + * returns 0 if successful, error code otherwise + */ +int q6asm_open_shared_io(struct audio_client *ac, + struct shared_io_config *config, + int dir) +{ + struct asm_stream_cmd_open_shared_io *open; + u8 *channel_mapping; + int i, size_of_open, num_watermarks, bufsz, bufcnt, rc, flags = 0; + + if (!ac || !config) + return -EINVAL; + + bufsz = config->bufsz; + bufcnt = config->bufcnt; + num_watermarks = 0; + + ac->config = *config; + + if (ac->session <= 0 || ac->session > SESSION_MAX) { + pr_err("%s: Session %d is out of bounds\n", + __func__, ac->session); + return -EINVAL; + } + + size_of_open = sizeof(struct asm_stream_cmd_open_shared_io) + + (sizeof(struct asm_shared_watermark_level) * num_watermarks); + + open = kzalloc(PAGE_ALIGN(size_of_open), GFP_KERNEL); + if (!open) + return -ENOMEM; + + q6asm_stream_add_hdr(ac, &open->hdr, size_of_open, TRUE, + ac->stream_id); + + atomic_set(&ac->cmd_state, 1); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x, perf %d\n", + __func__, open->hdr.token, ac->stream_id, ac->session, + ac->perf_mode); + + open->hdr.opcode = + dir == IN ? ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE : + ASM_STREAM_CMD_OPEN_PUSH_MODE_READ; + + pr_debug("%s perf_mode %d\n", __func__, ac->perf_mode); + if (dir == IN) + if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE) + flags = 4 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE; + else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) + flags = 2 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE; + else if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + flags = 1 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE; + else + pr_err("Invalid perf mode for pull write\n"); + else + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + flags = ASM_LOW_LATENCY_TX_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ; + else + pr_err("Invalid perf mode for push read\n"); + + if (flags == 0) { + pr_err("%s: Invalid mode[%d]\n", __func__, + ac->perf_mode); + kfree(open); + return -EINVAL; + + } + + pr_debug("open.mode_flags = 0x%x\n", flags); + open->mode_flags = flags; + open->endpoint_type = ASM_END_POINT_DEVICE_MATRIX; + open->topo_bits_per_sample = config->bits_per_sample; + + open->topo_id = q6asm_get_asm_topology_cal(); + + if (config->format == FORMAT_LINEAR_PCM) + open->fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + else { + pr_err("%s: Invalid format[%d]\n", __func__, config->format); + rc = -EINVAL; + goto done; + } + + if (ac->port[dir].buf) { + pr_err("%s: Buffer already allocated\n", __func__); + rc = -EINVAL; + goto done; + } + + rc = q6asm_set_shared_circ_buff(ac, open, bufsz, bufcnt, dir); + + if (rc) + goto done; + + ac->port[dir].tmp_hdl = 0; + + rc = q6asm_set_shared_pos_buff(ac, open, dir); + + if (rc) + goto done; + + /* asm_multi_channel_pcm_fmt_blk_v3 */ + open->fmt.num_channels = config->channels; + open->fmt.bits_per_sample = config->bits_per_sample; + open->fmt.sample_rate = config->rate; + open->fmt.is_signed = 1; + open->fmt.sample_word_size = config->sample_word_size; + + channel_mapping = open->fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + rc = q6asm_map_channels(channel_mapping, config->channels, false); + if (rc) { + pr_err("%s: Map channels failed, ret: %d\n", __func__, rc); + goto done; + } + + open->num_watermark_levels = num_watermarks; + for (i = 0; i < num_watermarks; i++) { + open->watermark[i].watermark_level_bytes = i * + ((bufsz * bufcnt) / num_watermarks); + pr_debug("%s: Watermark level set for %i\n", + __func__, + open->watermark[i].watermark_level_bytes); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) open); + if (rc < 0) { + pr_err("%s: Open failed op[0x%x]rc[%d]\n", + __func__, open->hdr.opcode, rc); + goto done; + } + + pr_debug("%s: sent open apr pkt\n", __func__); + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) <= 0), 5*HZ); + if (!rc) { + pr_err("%s: Timeout. Waited for open write apr pkt rc[%d]\n", + __func__, rc); + rc = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&ac->cmd_state) < 0) { + pr_err("%s: DSP returned error [%d]\n", __func__, + atomic_read(&ac->cmd_state)); + rc = -EINVAL; + goto done; + } + + ac->io_mode |= TUN_WRITE_IO_MODE; + rc = 0; +done: + kfree(open); + return rc; +} +EXPORT_SYMBOL(q6asm_open_shared_io); + +/* + * q6asm_shared_io_buf: Returns handle to the shared circular buffer being + * used for pull/push mode. + * parameters + * dir - used to identify input/output port + * returns buffer handle + */ +struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac, + int dir) +{ + struct audio_port_data *port; + + if (!ac) { + pr_err("%s: ac is null\n", __func__); + return NULL; + } + port = &ac->port[dir]; + return port->buf; +} +EXPORT_SYMBOL(q6asm_shared_io_buf); + +/* + * q6asm_shared_io_free: Frees memory allocated for a pull/push session + * parameters + * dir - port direction + * returns 0 if successful, error otherwise + */ +int q6asm_shared_io_free(struct audio_client *ac, int dir) +{ + struct audio_port_data *port; + + if (!ac) { + pr_err("%s: audio client is null\n", __func__); + return -EINVAL; + } + port = &ac->port[dir]; + mutex_lock(&ac->cmd_lock); + if (port->buf && port->buf->data) { + msm_audio_ion_free(port->buf->client, port->buf->handle); + port->buf->client = NULL; + port->buf->handle = NULL; + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + } + if (ac->shared_pos_buf.data) { + msm_audio_ion_free(ac->shared_pos_buf.client, + ac->shared_pos_buf.handle); + ac->shared_pos_buf.client = NULL; + ac->shared_pos_buf.handle = NULL; + } + mutex_unlock(&ac->cmd_lock); + return 0; +} +EXPORT_SYMBOL(q6asm_shared_io_free); + +/* + * q6asm_get_shared_pos: Returns current read index/write index as observed + * by the DSP. Note that this is an offset and iterates from [0,BUF_SIZE - 1] + * parameters - (all output) + * read_index - offset + * wall_clk_msw1 - ADSP wallclock msw + * wall_clk_lsw1 - ADSP wallclock lsw + * returns 0 if successful, -EAGAIN if DSP failed to update after some + * retries + */ +int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *read_index, + uint32_t *wall_clk_msw1, uint32_t *wall_clk_lsw1) +{ + struct asm_shared_position_buffer *pos_buf; + uint32_t frame_cnt1, frame_cnt2; + int i, j; + + if (!ac) { + pr_err("%s: audio client is null\n", __func__); + return -EINVAL; + } + + pos_buf = ac->shared_pos_buf.data; + + /* always try to get the latest update in the shared pos buffer */ + for (i = 0; i < 2; i++) { + /* retry until there is an update from DSP */ + for (j = 0; j < 5; j++) { + frame_cnt1 = pos_buf->frame_counter; + if (frame_cnt1 != 0) + break; + } + + *wall_clk_msw1 = pos_buf->wall_clock_us_msw; + *wall_clk_lsw1 = pos_buf->wall_clock_us_lsw; + *read_index = pos_buf->index; + frame_cnt2 = pos_buf->frame_counter; + + if (frame_cnt1 != frame_cnt2) + continue; + return 0; + } + pr_err("%s out of tries trying to get a good read, try again\n", + __func__); + return -EAGAIN; +} + +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct asm_session_cmd_run_v2 run; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE); + atomic_set(&ac->cmd_state, -1); + + run.hdr.opcode = ASM_SESSION_CMD_RUN_V2; + run.flags = flags; + run.time_lsw = lsw_ts; + run.time_msw = msw_ts; + + config_debug_fs_run(); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]", + __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for run success", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id) +{ + struct asm_session_cmd_run_v2 run; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_stream_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE, stream_id); + atomic_set(&ac->cmd_state, 1); + run.hdr.opcode = ASM_SESSION_CMD_RUN_V2; + run.flags = flags; + run.time_lsw = lsw_ts; + run.time_msw = msw_ts; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]", __func__, rc); + return -EINVAL; + } + return 0; +} + +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, ac->stream_id); +} + +int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id) +{ + return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, stream_id); +} + +int q6asm_enc_cfg_blk_aac(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, uint32_t mode, uint32_t format) +{ + struct asm_aac_enc_cfg_v2 enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d] format[%d]\n", + __func__, ac->session, frames_per_buf, + sample_rate, channels, bit_rate, mode, format); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_aac_enc_cfg_v2) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + enc_cfg.bit_rate = bit_rate; + enc_cfg.enc_mode = mode; + enc_cfg.aac_fmt_flag = format; + enc_cfg.channel_cfg = channels; + enc_cfg.sample_rate = sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_enc_cfg_blk_g711(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate) +{ + struct asm_g711_enc_cfg_v2 enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]SR[%d]\n", + __func__, ac->session, frames_per_buf, + sample_rate); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_g711_enc_cfg_v2) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + enc_cfg.sample_rate = sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_set_encdec_chan_map(struct audio_client *ac, + uint32_t num_channels) +{ + struct asm_dec_out_chan_map_param chan_map; + u8 *channel_mapping; + int rc = 0; + + pr_debug("%s: Session %d, num_channels = %d\n", + __func__, ac->session, num_channels); + q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE); + atomic_set(&ac->cmd_state, -1); + chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + chan_map.encdec.param_id = ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP; + chan_map.encdec.param_size = sizeof(struct asm_dec_out_chan_map_param) - + (sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_encdec_param)); + chan_map.num_channels = num_channels; + channel_mapping = chan_map.channel_mapping; + memset(channel_mapping, PCM_CHANNEL_NULL, MAX_CHAN_MAP_CHANNELS); + + if (q6asm_map_channels(channel_mapping, num_channels, false)) { + pr_err("%s: map channels failed %d\n", __func__, num_channels); + return -EINVAL; + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &chan_map); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", __func__, + chan_map.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/* + * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + enc_cfg.endianness = endianness; + enc_cfg.mode = mode; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Command open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4); + +/* + * q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size) +{ + struct asm_multi_channel_pcm_enc_cfg_v3 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v3); + +int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample, + bool use_default_chmap, bool use_back_flavor, u8 *channel_map) +{ + struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg; + u8 *channel_mapping; + u32 frames_per_buf = 0; + + int rc = 0; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + + pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__, + ac->session, rate, channels); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size, endianness, mode); +} + +static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + return q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size); +} + +static int __q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample) +{ + return q6asm_enc_cfg_blk_pcm_v2(ac, rate, channels, + bits_per_sample, true, false, NULL); +} + +int q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, 16); +} + +int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample) +{ + return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, bits_per_sample); +} + +/* + * q6asm_enc_cfg_blk_pcm_format_support_v3 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + return __q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels, + bits_per_sample, sample_word_size); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3); + +/* + * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, sample_word_size, + endianness, mode); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4); + +int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg; + u8 *channel_mapping; + u32 frames_per_buf = 0; + + int rc = 0; + + pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__, + ac->session, rate, channels); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.num_channels = 0;/*channels;*/ + enc_cfg.bits_per_sample = 16; + enc_cfg.sample_rate = 0;/*rate;*/ + enc_cfg.is_signed = 1; + channel_mapping = enc_cfg.channel_mapping; + + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", __func__, channels); + return -EINVAL; + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels, + bool use_back_flavor) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[3] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[4] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[5] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = PCM_CHANNEL_LB; + lchannel_mapping[5] = PCM_CHANNEL_RB; + lchannel_mapping[6] = PCM_CHANNEL_LS; + lchannel_mapping[7] = PCM_CHANNEL_RS; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +int q6asm_enable_sbrps(struct audio_client *ac, + uint32_t sbr_ps_enable) +{ + struct asm_aac_sbr_ps_flag_param sbrps; + u32 frames_per_buf = 0; + + int rc = 0; + + pr_debug("%s: Session %d\n", __func__, ac->session); + + q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE); + atomic_set(&ac->cmd_state, -1); + + sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + sbrps.encdec.param_id = ASM_PARAM_ID_AAC_SBR_PS_FLAG; + sbrps.encdec.param_size = sizeof(struct asm_aac_sbr_ps_flag_param) - + sizeof(struct asm_stream_cmd_set_encdec_param); + sbrps.encblk.frames_per_buf = frames_per_buf; + sbrps.encblk.enc_cfg_blk_size = sbrps.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + sbrps.sbr_ps_flag = sbr_ps_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, + ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_AAC_SBR_PS_FLAG, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x] ", __func__, sbrps.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_cfg_dual_mono_aac(struct audio_client *ac, + uint16_t sce_left, uint16_t sce_right) +{ + struct asm_aac_dual_mono_mapping_param dual_mono; + + int rc = 0; + + pr_debug("%s: Session %d, sce_left = %d, sce_right = %d\n", + __func__, ac->session, sce_left, sce_right); + + q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE); + atomic_set(&ac->cmd_state, -1); + + dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + dual_mono.encdec.param_id = ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING; + dual_mono.encdec.param_size = sizeof(dual_mono.left_channel_sce) + + sizeof(dual_mono.right_channel_sce); + dual_mono.left_channel_sce = sce_left; + dual_mono.right_channel_sce = sce_right; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &dual_mono); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", __func__, + dual_mono.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/* Support for selecting stereo mixing coefficients for B family not done */ +int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff) +{ + struct asm_aac_stereo_mix_coeff_selection_param_v2 aac_mix_coeff; + int rc = 0; + + q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE); + atomic_set(&ac->cmd_state, -1); + aac_mix_coeff.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + aac_mix_coeff.param_id = + ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2; + aac_mix_coeff.param_size = + sizeof(struct asm_aac_stereo_mix_coeff_selection_param_v2); + aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff; + pr_debug("%s: mix_coeff = %u\n", __func__, mix_coeff); + rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2, + rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, aac_mix_coeff.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t reduced_rate_level, uint16_t rate_modulation_cmd) +{ + struct asm_v13k_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]\n", + __func__, + ac->session, frames_per_buf, min_rate, max_rate, + reduced_rate_level, rate_modulation_cmd); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_v13k_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.min_rate = min_rate; + enc_cfg.max_rate = max_rate; + enc_cfg.reduced_rate_cmd = reduced_rate_level; + enc_cfg.rate_mod_cmd = rate_modulation_cmd; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for setencdec v13k resp\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t rate_modulation_cmd) +{ + struct asm_evrc_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] rate_modulation_cmd[0x%4x]\n", + __func__, ac->session, + frames_per_buf, min_rate, max_rate, rate_modulation_cmd); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_evrc_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.min_rate = min_rate; + enc_cfg.max_rate = max_rate; + enc_cfg.rate_mod_cmd = rate_modulation_cmd; + enc_cfg.reserved = 0; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for encdec evrc\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable) +{ + struct asm_amrnb_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n", + __func__, ac->session, frames_per_buf, band_mode, dtx_enable); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_amrnb_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.enc_mode = band_mode; + enc_cfg.dtx_mode = dtx_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for set encdec amrnb\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable) +{ + struct asm_amrwb_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n", + __func__, ac->session, frames_per_buf, band_mode, dtx_enable); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_amrwb_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.enc_mode = band_mode; + enc_cfg.dtx_mode = dtx_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + + +static int __q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, int stream_id, + bool use_default_chmap, char *channel_map) +{ + struct asm_multi_channel_pcm_fmt_blk_v2 fmt; + u8 *channel_mapping; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, + channels); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&fmt.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.num_channels = channels; + fmt.bits_per_sample = bits_per_sample; + fmt.sample_rate = rate; + fmt.is_signed = 1; + + channel_mapping = fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + return __q6asm_media_format_block_pcm(ac, rate, + channels, 16, ac->stream_id, + true, NULL); +} + +int q6asm_media_format_block_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample) +{ + return __q6asm_media_format_block_pcm(ac, rate, + channels, bits_per_sample, ac->stream_id, + true, NULL); +} + +int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, int stream_id, + bool use_default_chmap, char *channel_map) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map); +} + +/* + * q6asm_media_format_block_pcm_format_support_v3- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v3(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3); + +/* + * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v4(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size, endianness, + mode); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4); + + +static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample) +{ + struct asm_multi_channel_pcm_fmt_blk_v2 fmt; + u8 *channel_mapping; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, + channels); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.num_channels = channels; + fmt.bits_per_sample = bits_per_sample; + fmt.sample_rate = rate; + fmt.is_signed = 1; + + channel_mapping = fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map) +{ + return __q6asm_media_format_block_multi_ch_pcm(ac, rate, + channels, use_default_chmap, channel_map, 16); +} + +int q6asm_media_format_block_multi_ch_pcm_v2( + struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample) +{ + return __q6asm_media_format_block_multi_ch_pcm(ac, rate, + channels, use_default_chmap, channel_map, + bits_per_sample); +} + +/* + * q6asm_media_format_block_multi_ch_pcm_v3 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + return __q6asm_media_format_block_multi_ch_pcm_v3(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3); + +/* + * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size, + endianness, + mode); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4); + +static int __q6asm_media_format_block_multi_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg, int stream_id) +{ + struct asm_aac_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, + cfg->sample_rate, cfg->ch_cfg); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&fmt.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.aac_fmt_flag = cfg->format; + fmt.audio_objype = cfg->aot; + /* If zero, PCE is assumed to be available in bitstream*/ + fmt.total_size_of_PCE_bits = 0; + fmt.channel_config = cfg->ch_cfg; + fmt.sample_rate = cfg->sample_rate; + + pr_debug("%s: format=0x%x cfg_size=%d aac-cfg=0x%x aot=%d ch=%d sr=%d\n", + __func__, fmt.aac_fmt_flag, fmt.fmt_blk.fmt_blk_size, + fmt.aac_fmt_flag, + fmt.audio_objype, + fmt.channel_config, + fmt.sample_rate); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_multi_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg) +{ + return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id); +} + +int q6asm_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg) +{ + return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id); +} + +int q6asm_stream_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg, int stream_id) +{ + return __q6asm_media_format_block_multi_aac(ac, cfg, stream_id); +} + +int q6asm_media_format_block_wma(struct audio_client *ac, + void *cfg, int stream_id) +{ + struct asm_wmastdv9_fmt_blk_v2 fmt; + struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg; + int rc = 0; + + pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n", + ac->session, wma_cfg->format_tag, wma_cfg->sample_rate, + wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec, + wma_cfg->block_align, wma_cfg->valid_bits_per_sample, + wma_cfg->ch_mask, wma_cfg->encode_opt); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + fmt.fmtag = wma_cfg->format_tag; + fmt.num_channels = wma_cfg->ch_cfg; + fmt.sample_rate = wma_cfg->sample_rate; + fmt.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec; + fmt.blk_align = wma_cfg->block_align; + fmt.bits_per_sample = + wma_cfg->valid_bits_per_sample; + fmt.channel_mask = wma_cfg->ch_mask; + fmt.enc_options = wma_cfg->encode_opt; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_wmapro(struct audio_client *ac, + void *cfg, int stream_id) +{ + struct asm_wmaprov10_fmt_blk_v2 fmt; + struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg; + int rc = 0; + + pr_debug("%s: session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x], adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n", + __func__, + ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate, + wmapro_cfg->ch_cfg, wmapro_cfg->avg_bytes_per_sec, + wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample, + wmapro_cfg->ch_mask, wmapro_cfg->encode_opt, + wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.fmtag = wmapro_cfg->format_tag; + fmt.num_channels = wmapro_cfg->ch_cfg; + fmt.sample_rate = wmapro_cfg->sample_rate; + fmt.avg_bytes_per_sec = + wmapro_cfg->avg_bytes_per_sec; + fmt.blk_align = wmapro_cfg->block_align; + fmt.bits_per_sample = wmapro_cfg->valid_bits_per_sample; + fmt.channel_mask = wmapro_cfg->ch_mask; + fmt.enc_options = wmapro_cfg->encode_opt; + fmt.usAdvancedEncodeOpt = wmapro_cfg->adv_encode_opt; + fmt.advanced_enc_options2 = wmapro_cfg->adv_encode_opt2; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_amrwbplus(struct audio_client *ac, + struct asm_amrwbplus_cfg *cfg) +{ + struct asm_amrwbplus_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s: session[%d]band-mode[%d]frame-fmt[%d]ch[%d]\n", + __func__, + ac->session, + cfg->amr_band_mode, + cfg->amr_frame_fmt, + cfg->num_channels); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + fmt.amr_frame_fmt = cfg->amr_frame_fmt; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd media format update failed.. %d\n", + __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_stream_media_format_block_flac(struct audio_client *ac, + struct asm_flac_cfg *cfg, int stream_id) +{ + struct asm_flac_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d] rate[%d] ch[%d] size[%d] stream_id[%d]\n", + __func__, ac->session, cfg->sample_rate, cfg->ch_cfg, + cfg->sample_size, stream_id); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.is_stream_info_present = cfg->stream_info_present; + fmt.num_channels = cfg->ch_cfg; + fmt.min_blk_size = cfg->min_blk_size; + fmt.max_blk_size = cfg->max_blk_size; + fmt.sample_rate = cfg->sample_rate; + fmt.min_frame_size = cfg->min_frame_size; + fmt.max_frame_size = cfg->max_frame_size; + fmt.sample_size = cfg->sample_size; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_alac(struct audio_client *ac, + struct asm_alac_cfg *cfg, int stream_id) +{ + struct asm_alac_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__, + ac->session, cfg->sample_rate, cfg->num_channels); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.frame_length = cfg->frame_length; + fmt.compatible_version = cfg->compatible_version; + fmt.bit_depth = cfg->bit_depth; + fmt.pb = cfg->pb; + fmt.mb = cfg->mb; + fmt.kb = cfg->kb; + fmt.num_channels = cfg->num_channels; + fmt.max_run = cfg->max_run; + fmt.max_frame_bytes = cfg->max_frame_bytes; + fmt.avg_bit_rate = cfg->avg_bit_rate; + fmt.sample_rate = cfg->sample_rate; + fmt.channel_layout_tag = cfg->channel_layout_tag; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/* + * q6asm_media_format_block_g711 - sends g711 decoder configuration + * parameters + * @ac: Client session handle + * @cfg: Audio stream manager configuration parameters + * @stream_id: Stream id + */ +int q6asm_media_format_block_g711(struct audio_client *ac, + struct asm_g711_dec_cfg *cfg, int stream_id) +{ + struct asm_g711_dec_fmt_blk_v2 fmt; + int rc = 0; + + if (!ac) { + pr_err("%s: audio client is null\n", __func__); + return -EINVAL; + } + if (!cfg) { + pr_err("%s: Invalid ASM config\n", __func__); + return -EINVAL; + } + + if (stream_id <= 0) { + pr_err("%s: Invalid stream id\n", __func__); + return -EINVAL; + } + + pr_debug("%s :session[%d]rate[%d]\n", __func__, + ac->session, cfg->sample_rate); + + memset(&fmt, 0, sizeof(struct asm_g711_dec_fmt_blk_v2)); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.sample_rate = cfg->sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Command media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_g711); + +int q6asm_stream_media_format_block_vorbis(struct audio_client *ac, + struct asm_vorbis_cfg *cfg, int stream_id) +{ + struct asm_vorbis_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d] bit_stream_fmt[%d] stream_id[%d]\n", + __func__, ac->session, cfg->bit_stream_fmt, stream_id); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.bit_stream_fmt = cfg->bit_stream_fmt; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_ape(struct audio_client *ac, + struct asm_ape_cfg *cfg, int stream_id) +{ + struct asm_ape_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__, + ac->session, cfg->sample_rate, cfg->num_channels); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.compatible_version = cfg->compatible_version; + fmt.compression_level = cfg->compression_level; + fmt.format_flags = cfg->format_flags; + fmt.blocks_per_frame = cfg->blocks_per_frame; + fmt.final_frame_blocks = cfg->final_frame_blocks; + fmt.total_frames = cfg->total_frames; + fmt.bits_per_sample = cfg->bits_per_sample; + fmt.num_channels = cfg->num_channels; + fmt.sample_rate = cfg->sample_rate; + fmt.seek_table_present = cfg->seek_table_present; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/* + * q6asm_media_format_block_dsd- Sends DSD Decoder + * configuration parameters + * + * @ac: Client session handle + * @cfg: DSD Media Format Configuration. + * @stream_id: stream id of stream to be associated with this session + * + * Return 0 on success or negative error code on failure + */ +int q6asm_media_format_block_dsd(struct audio_client *ac, + struct asm_dsd_cfg *cfg, int stream_id) +{ + struct asm_dsd_fmt_blk_v2 fmt; + int rc; + + pr_debug("%s: session[%d] data_rate[%d] ch[%d]\n", __func__, + ac->session, cfg->dsd_data_rate, cfg->num_channels); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.num_version = cfg->num_version; + fmt.is_bitwise_big_endian = cfg->is_bitwise_big_endian; + fmt.dsd_channel_block_size = cfg->dsd_channel_block_size; + fmt.num_channels = cfg->num_channels; + fmt.dsd_data_rate = cfg->dsd_data_rate; + atomic_set(&ac->cmd_state, -1); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Command DSD media format update failed, err: %d\n", + __func__, rc); + goto done; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for DSD FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto done; + } + return 0; +done: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_dsd); + +static int __q6asm_ds1_set_endp_params(struct audio_client *ac, int param_id, + int param_value, int stream_id) +{ + struct asm_dec_ddp_endp_param_v2 ddp_cfg; + int rc = 0; + + pr_debug("%s: session[%d] stream[%d],param_id[%d]param_value[%d]", + __func__, ac->session, stream_id, param_id, param_value); + + q6asm_stream_add_hdr(ac, &ddp_cfg.hdr, sizeof(ddp_cfg), TRUE, + stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&ddp_cfg.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + ddp_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + ddp_cfg.encdec.param_id = param_id; + ddp_cfg.encdec.param_size = sizeof(struct asm_dec_ddp_endp_param_v2) - + (sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_encdec_param)); + ddp_cfg.endp_param_value = param_value; + rc = apr_send_pkt(ac->apr, (uint32_t *) &ddp_cfg); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", __func__, + ddp_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_ds1_set_endp_params(struct audio_client *ac, + int param_id, int param_value) +{ + return __q6asm_ds1_set_endp_params(ac, param_id, param_value, + ac->stream_id); +} + +int q6asm_ds1_set_stream_endp_params(struct audio_client *ac, + int param_id, int param_value, + int stream_id) +{ + return __q6asm_ds1_set_endp_params(ac, param_id, param_value, + stream_id); +} + +int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add, int dir, + uint32_t bufsz, uint32_t bufcnt) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + struct audio_port_data *port = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + struct asm_buffer_node *buffer_node = NULL; + int rc = 0; + int cmd_size = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->mmap_apr == NULL) { + pr_err("%s: mmap APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + buffer_node = kmalloc(sizeof(struct asm_buffer_node), GFP_KERNEL); + if (!buffer_node) + return -ENOMEM; + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload) * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) { + rc = -EINVAL; + kfree(buffer_node); + return rc; + } + mmap_regions = (struct avs_cmd_shared_mem_map_regions *) + mmap_region_cmd; + q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir); + atomic_set(&ac->mem_state, -1); + mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mmap_regions->num_regions = bufcnt & 0x00ff; + mmap_regions->property_flag = 0x00; + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + ac->port[dir].tmp_hdl = 0; + port = &ac->port[dir]; + pr_debug("%s: buf_add 0x%pK, bufsz: %d\n", __func__, + &buf_add, bufsz); + mregions->shm_addr_lsw = lower_32_bits(buf_add); + mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(buf_add); + mregions->mem_size_bytes = bufsz; + ++mregions; + + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd); + if (rc < 0) { + pr_err("%s: mmap op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, rc); + rc = -EINVAL; + kfree(buffer_node); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0 && + ac->port[dir].tmp_hdl), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for memory_map\n", __func__); + rc = -ETIMEDOUT; + kfree(buffer_node); + goto fail_cmd; + } + if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s: DSP returned error[%s] for memory_map\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + kfree(buffer_node); + goto fail_cmd; + } + buffer_node->buf_phys_addr = buf_add; + buffer_node->mmap_hdl = ac->port[dir].tmp_hdl; + list_add_tail(&buffer_node->list, &ac->port[dir].mem_map_handle); + ac->port[dir].tmp_hdl = 0; + rc = 0; + +fail_cmd: + kfree(mmap_region_cmd); + return rc; +} + +int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, int dir) +{ + struct avs_cmd_shared_mem_unmap_regions mem_unmap; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + + int rc = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (this_mmap.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + q6asm_add_mmaphdr(ac, &mem_unmap.hdr, + sizeof(struct avs_cmd_shared_mem_unmap_regions), + dir); + atomic_set(&ac->mem_state, -1); + mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; + mem_unmap.mem_map_handle = 0; + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + pr_debug("%s: Found the element\n", __func__); + mem_unmap.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n", + __func__, mem_unmap.mem_map_handle); + + if (mem_unmap.mem_map_handle == 0) { + pr_err("%s: Do not send null mem handle to DSP\n", __func__); + rc = 0; + goto fail_cmd; + } + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("%s: mem_unmap op[0x%x]rc[%d]\n", __func__, + mem_unmap.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0), 5 * HZ); + if (!rc) { + pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -ETIMEDOUT; + goto fail_cmd; + } else if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s DSP returned error [%s] map handle 0x%x\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state)), + mem_unmap.mem_map_handle); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + goto fail_cmd; + } else if (atomic_read(&ac->unmap_cb_success) == 0) { + pr_err("%s: Error in mem unmap callback of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -EINVAL; + goto fail_cmd; + } + + rc = 0; +fail_cmd: + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + list_del(&buf_node->list); + kfree(buf_node); + break; + } + } + return rc; +} + + +static int q6asm_memory_map_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt, + bool is_contiguous) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + struct audio_port_data *port = NULL; + struct audio_buffer *ab = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + struct asm_buffer_node *buffer_node = NULL; + int rc = 0; + int i = 0; + uint32_t cmd_size = 0; + uint32_t bufcnt_t; + uint32_t bufsz_t; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->mmap_apr == NULL) { + pr_err("%s: mmap APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + bufcnt_t = (is_contiguous) ? 1 : bufcnt; + bufsz_t = (is_contiguous) ? (bufsz * bufcnt) : bufsz; + + if (is_contiguous) { + /* The size to memory map should be multiple of 4K bytes */ + bufsz_t = PAGE_ALIGN(bufsz_t); + } + + if (bufcnt_t > (UINT_MAX + - sizeof(struct avs_cmd_shared_mem_map_regions)) + / sizeof(struct avs_shared_map_region_payload)) { + pr_err("%s: Unsigned Integer Overflow. bufcnt_t = %u\n", + __func__, bufcnt_t); + return -EINVAL; + } + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + (sizeof(struct avs_shared_map_region_payload) + * bufcnt_t); + + + if (bufcnt > (UINT_MAX / sizeof(struct asm_buffer_node))) { + pr_err("%s: Unsigned Integer Overflow. bufcnt = %u\n", + __func__, bufcnt); + return -EINVAL; + } + + buffer_node = kzalloc(sizeof(struct asm_buffer_node) * bufcnt, + GFP_KERNEL); + if (!buffer_node) + return -ENOMEM; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) { + rc = -EINVAL; + kfree(buffer_node); + return rc; + } + mmap_regions = (struct avs_cmd_shared_mem_map_regions *) + mmap_region_cmd; + q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir); + atomic_set(&ac->mem_state, -1); + pr_debug("%s: mmap_region=0x%pK token=0x%x\n", __func__, + mmap_regions, ((ac->session << 8) | dir)); + + mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mmap_regions->num_regions = bufcnt_t; /*bufcnt & 0x00ff; */ + mmap_regions->property_flag = 0x00; + pr_debug("%s: map_regions->nregions = %d\n", __func__, + mmap_regions->num_regions); + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + ac->port[dir].tmp_hdl = 0; + port = &ac->port[dir]; + for (i = 0; i < bufcnt_t; i++) { + ab = &port->buf[i]; + mregions->shm_addr_lsw = lower_32_bits(ab->phys); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(ab->phys); + mregions->mem_size_bytes = bufsz_t; + ++mregions; + } + + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd); + if (rc < 0) { + pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, rc); + rc = -EINVAL; + kfree(buffer_node); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0) + , 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for memory_map\n", __func__); + rc = -ETIMEDOUT; + kfree(buffer_node); + goto fail_cmd; + } + if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s DSP returned error for memory_map [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + kfree(buffer_node); + goto fail_cmd; + } + mutex_lock(&ac->cmd_lock); + + for (i = 0; i < bufcnt; i++) { + ab = &port->buf[i]; + buffer_node[i].buf_phys_addr = ab->phys; + buffer_node[i].mmap_hdl = ac->port[dir].tmp_hdl; + list_add_tail(&buffer_node[i].list, + &ac->port[dir].mem_map_handle); + pr_debug("%s: i=%d, bufadd[i] = 0x%pK, maphdl[i] = 0x%x\n", + __func__, i, &buffer_node[i].buf_phys_addr, + buffer_node[i].mmap_hdl); + } + ac->port[dir].tmp_hdl = 0; + mutex_unlock(&ac->cmd_lock); + rc = 0; +fail_cmd: + kfree(mmap_region_cmd); + return rc; +} + +static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir) +{ + struct avs_cmd_shared_mem_unmap_regions mem_unmap; + struct audio_port_data *port = NULL; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + phys_addr_t buf_add; + int rc = 0; + int cmd_size = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->mmap_apr == NULL) { + pr_err("%s: mmap APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions); + q6asm_add_mmaphdr(ac, &mem_unmap.hdr, cmd_size, dir); + atomic_set(&ac->mem_state, -1); + port = &ac->port[dir]; + buf_add = port->buf->phys; + mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; + mem_unmap.mem_map_handle = 0; + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + pr_debug("%s: Found the element\n", __func__); + mem_unmap.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n", + __func__, mem_unmap.mem_map_handle); + + if (mem_unmap.mem_map_handle == 0) { + pr_err("%s: Do not send null mem handle to DSP\n", __func__); + rc = 0; + goto fail_cmd; + } + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("mmap_regions op[0x%x]rc[%d]\n", + mem_unmap.hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -ETIMEDOUT; + goto fail_cmd; + } else if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + goto fail_cmd; + } else if (atomic_read(&ac->unmap_cb_success) == 0) { + pr_err("%s: Error in mem unmap callback of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; + +fail_cmd: + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + list_del(&buf_node->list); + kfree(buf_node); + break; + } + } + return rc; +} + +int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain) +{ + struct asm_volume_ctrl_multichannel_gain multi_ch_gain; + int sz = 0; + int rc = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + memset(&multi_ch_gain, 0, sizeof(multi_ch_gain)); + sz = sizeof(struct asm_volume_ctrl_multichannel_gain); + q6asm_add_hdr_async(ac, &multi_ch_gain.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + multi_ch_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + multi_ch_gain.param.data_payload_addr_lsw = 0; + multi_ch_gain.param.data_payload_addr_msw = 0; + multi_ch_gain.param.mem_map_handle = 0; + multi_ch_gain.param.data_payload_size = sizeof(multi_ch_gain) - + sizeof(multi_ch_gain.hdr) - sizeof(multi_ch_gain.param); + multi_ch_gain.data.module_id = ASM_MODULE_ID_VOL_CTRL; + multi_ch_gain.data.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN; + multi_ch_gain.data.param_size = multi_ch_gain.param.data_payload_size - + sizeof(multi_ch_gain.data); + multi_ch_gain.data.reserved = 0; + multi_ch_gain.gain_data[0].channeltype = PCM_CHANNEL_FL; + multi_ch_gain.gain_data[0].gain = left_gain << 15; + multi_ch_gain.gain_data[1].channeltype = PCM_CHANNEL_FR; + multi_ch_gain.gain_data[1].gain = right_gain << 15; + multi_ch_gain.num_channels = 2; + rc = apr_send_pkt(ac->apr, (uint32_t *) &multi_ch_gain); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, multi_ch_gain.data.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + multi_ch_gain.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] , set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + multi_ch_gain.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +/* + * q6asm_set_multich_gain: set multiple channel gains on an ASM session + * @ac: audio client handle + * @channels: number of channels caller intends to set gains + * @gains: list of gains of audio channels + * @ch_map: list of channel mapping. Only valid if use_default is false + * @use_default: flag to indicate whether to use default mapping + */ +int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, + uint32_t *gains, uint8_t *ch_map, bool use_default) +{ + struct asm_volume_ctrl_multichannel_gain multich_gain; + int sz = 0; + int rc = 0; + int i; + u8 default_chmap[VOLUME_CONTROL_MAX_CHANNELS]; + + if (ac == NULL) { + pr_err("%s: ac is NULL\n", __func__); + rc = -EINVAL; + goto done; + } + if (ac->apr == NULL) { + dev_err(ac->dev, "%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto done; + } + if (gains == NULL) { + dev_err(ac->dev, "%s: gain_list is NULL\n", __func__); + rc = -EINVAL; + goto done; + } + if (channels > VOLUME_CONTROL_MAX_CHANNELS) { + dev_err(ac->dev, "%s: Invalid channel count %d\n", + __func__, channels); + rc = -EINVAL; + goto done; + } + if (!use_default && ch_map == NULL) { + dev_err(ac->dev, "%s: NULL channel map\n", __func__); + rc = -EINVAL; + goto done; + } + + memset(&multich_gain, 0, sizeof(multich_gain)); + sz = sizeof(struct asm_volume_ctrl_multichannel_gain); + q6asm_add_hdr_async(ac, &multich_gain.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, 1); + multich_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + multich_gain.param.data_payload_addr_lsw = 0; + multich_gain.param.data_payload_addr_msw = 0; + multich_gain.param.mem_map_handle = 0; + multich_gain.param.data_payload_size = sizeof(multich_gain) - + sizeof(multich_gain.hdr) - sizeof(multich_gain.param); + multich_gain.data.module_id = ASM_MODULE_ID_VOL_CTRL; + multich_gain.data.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN; + multich_gain.data.param_size = multich_gain.param.data_payload_size - + sizeof(multich_gain.data); + multich_gain.data.reserved = 0; + + if (use_default) { + rc = q6asm_map_channels(default_chmap, channels, false); + if (rc < 0) + goto done; + for (i = 0; i < channels; i++) { + multich_gain.gain_data[i].channeltype = + default_chmap[i]; + multich_gain.gain_data[i].gain = gains[i] << 15; + } + } else { + for (i = 0; i < channels; i++) { + multich_gain.gain_data[i].channeltype = ch_map[i]; + multich_gain.gain_data[i].gain = gains[i] << 15; + } + } + multich_gain.num_channels = channels; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &multich_gain); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, multich_gain.data.param_id, rc); + goto done; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) <= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + multich_gain.data.param_id); + rc = -EINVAL; + goto done; + } + if (atomic_read(&ac->cmd_state) < 0) { + pr_err("%s: DSP returned error[%d] , set-params paramid[0x%x]\n", + __func__, atomic_read(&ac->cmd_state), + multich_gain.data.param_id); + rc = -EINVAL; + goto done; + } + rc = 0; +done: + return rc; +} + +int q6asm_set_mute(struct audio_client *ac, int muteflag) +{ + struct asm_volume_ctrl_mute_config mute; + int sz = 0; + int rc = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct asm_volume_ctrl_mute_config); + q6asm_add_hdr_async(ac, &mute.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + mute.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + mute.param.data_payload_addr_lsw = 0; + mute.param.data_payload_addr_msw = 0; + mute.param.mem_map_handle = 0; + mute.param.data_payload_size = sizeof(mute) - + sizeof(mute.hdr) - sizeof(mute.param); + mute.data.module_id = ASM_MODULE_ID_VOL_CTRL; + mute.data.param_id = ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG; + mute.data.param_size = mute.param.data_payload_size - sizeof(mute.data); + mute.data.reserved = 0; + mute.mute_flag = muteflag; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &mute); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, mute.data.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + mute.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + mute.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, + void *data, struct param_outband *po, int m_id) +{ + int rc = 0, *ob_params = NULL; + uint32_t sz = sizeof(struct asm_dts_eagle_param) + (po ? 0 : size); + struct asm_dts_eagle_param *ad; + + if (!ac || ac->apr == NULL || (size == 0) || !data) { + pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK.\n", + __func__, size, data); + return -EINVAL; + } + + ad = kzalloc(sz, GFP_KERNEL); + if (!ad) + return -ENOMEM; + + pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n", + __func__, ac, param_id, size, data, m_id); + q6asm_add_hdr_async(ac, &ad->hdr, sz, 1); + ad->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + ad->param.data_payload_addr_lsw = 0; + ad->param.data_payload_addr_msw = 0; + + ad->param.mem_map_handle = 0; + ad->param.data_payload_size = size + + sizeof(struct asm_stream_param_data_v2); + ad->data.module_id = m_id; + ad->data.param_id = param_id; + ad->data.param_size = size; + ad->data.reserved = 0; + atomic_set(&ac->cmd_state, -1); + + if (po) { + struct list_head *ptr, *next; + struct asm_buffer_node *node; + + pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n", + __func__, po->kvaddr, (long)po->paddr); + ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr); + ad->param.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(po->paddr); + list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) { + node = list_entry(ptr, struct asm_buffer_node, list); + if (node->buf_phys_addr == po->paddr) { + ad->param.mem_map_handle = node->mmap_hdl; + break; + } + } + if (ad->param.mem_map_handle == 0) { + pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + /* check for integer overflow */ + if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) + rc = -EINVAL; + if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) { + pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n", + __func__, po->size, size + APR_CMD_OB_HDR_SZ); + rc = -EINVAL; + goto fail_cmd; + } + ob_params = (int *)po->kvaddr; + *ob_params++ = m_id; + *ob_params++ = param_id; + *ob_params++ = size; + memcpy(ob_params, data, size); + } else { + pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__); + memcpy(((char *)ad) + sizeof(struct asm_dts_eagle_param), + data, size); + } + rc = apr_send_pkt(ac->apr, (uint32_t *)ad); + if (rc < 0) { + pr_err("DTS_EAGLE_ASM - %s: set-params send failed paramid[0x%x]\n", + __func__, ad->data.param_id); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + if (!rc) { + pr_err("DTS_EAGLE_ASM - %s: timeout, set-params paramid[0x%x]\n", + __func__, ad->data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(ad); + return rc; +} + +int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size, + void *data, struct param_outband *po, int m_id) +{ + struct asm_dts_eagle_param_get *ad; + int rc = 0, *ob_params = NULL; + uint32_t sz = sizeof(struct asm_dts_eagle_param) + APR_CMD_GET_HDR_SZ + + (po ? 0 : size); + + if (!ac || ac->apr == NULL || (size == 0) || !data) { + pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK\n", + __func__, size, data); + return -EINVAL; + } + ad = kzalloc(sz, GFP_KERNEL); + if (!ad) + return -ENOMEM; + + pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n", + __func__, ac, param_id, size, data, m_id); + q6asm_add_hdr(ac, &ad->hdr, sz, TRUE); + ad->hdr.opcode = ASM_STREAM_CMD_GET_PP_PARAMS_V2; + ad->param.data_payload_addr_lsw = 0; + ad->param.data_payload_addr_msw = 0; + ad->param.mem_map_handle = 0; + ad->param.module_id = m_id; + ad->param.param_id = param_id; + ad->param.param_max_size = size + APR_CMD_GET_HDR_SZ; + ad->param.reserved = 0; + atomic_set(&ac->cmd_state, -1); + + generic_get_data = kzalloc(size + sizeof(struct generic_get_data_), + GFP_KERNEL); + if (!generic_get_data) { + rc = -ENOMEM; + goto fail_cmd; + } + + if (po) { + struct list_head *ptr, *next; + struct asm_buffer_node *node; + + pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n", + __func__, po->kvaddr, (long)po->paddr); + ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr); + ad->param.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(po->paddr); + list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) { + node = list_entry(ptr, struct asm_buffer_node, list); + if (node->buf_phys_addr == po->paddr) { + ad->param.mem_map_handle = node->mmap_hdl; + break; + } + } + if (ad->param.mem_map_handle == 0) { + pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + /* check for integer overflow */ + if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) + rc = -EINVAL; + if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) { + pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n", + __func__, po->size, size + APR_CMD_OB_HDR_SZ); + rc = -EINVAL; + goto fail_cmd; + } + ob_params = (int *)po->kvaddr; + *ob_params++ = m_id; + *ob_params++ = param_id; + *ob_params++ = size; + generic_get_data->is_inband = 0; + } else { + pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__); + generic_get_data->is_inband = 1; + } + + rc = apr_send_pkt(ac->apr, (uint32_t *)ad); + if (rc < 0) { + pr_err("DTS_EAGLE_ASM - %s: Commmand 0x%x failed\n", __func__, + ad->hdr.opcode); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + if (!rc) { + pr_err("DTS_EAGLE_ASM - %s: timeout in get\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + if (generic_get_data->valid) { + rc = 0; + memcpy(data, po ? ob_params : generic_get_data->ints, size); + } else { + rc = -EINVAL; + pr_err("DTS_EAGLE_ASM - %s: EAGLE get params problem getting data - check callback error value\n", + __func__); + } +fail_cmd: + kfree(ad); + kfree(generic_get_data); + generic_get_data = NULL; + return rc; +} + +static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) +{ + struct asm_volume_ctrl_master_gain vol; + int sz = 0; + int rc = 0; + int module_id; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + switch (instance) { + case SOFT_VOLUME_INSTANCE_2: + module_id = ASM_MODULE_ID_VOL_CTRL2; + break; + case SOFT_VOLUME_INSTANCE_1: + default: + module_id = ASM_MODULE_ID_VOL_CTRL; + break; + } + + sz = sizeof(struct asm_volume_ctrl_master_gain); + q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + vol.param.data_payload_addr_lsw = 0; + vol.param.data_payload_addr_msw = 0; + vol.param.mem_map_handle = 0; + vol.param.data_payload_size = sizeof(vol) - + sizeof(vol.hdr) - sizeof(vol.param); + vol.data.module_id = module_id; + vol.data.param_id = ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN; + vol.data.param_size = vol.param.data_payload_size - sizeof(vol.data); + vol.data.reserved = 0; + vol.master_gain = volume; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &vol); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, vol.data.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + vol.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + vol.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + rc = 0; +fail_cmd: + return rc; +} + +int q6asm_set_volume(struct audio_client *ac, int volume) +{ + return __q6asm_set_volume(ac, volume, SOFT_VOLUME_INSTANCE_1); +} + +int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance) +{ + return __q6asm_set_volume(ac, volume, instance); +} + +int q6asm_set_softpause(struct audio_client *ac, + struct asm_softpause_params *pause_param) +{ + struct asm_soft_pause_params softpause; + int sz = 0; + int rc = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct asm_soft_pause_params); + q6asm_add_hdr_async(ac, &softpause.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + softpause.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + + softpause.param.data_payload_addr_lsw = 0; + softpause.param.data_payload_addr_msw = 0; + softpause.param.mem_map_handle = 0; + softpause.param.data_payload_size = sizeof(softpause) - + sizeof(softpause.hdr) - sizeof(softpause.param); + softpause.data.module_id = ASM_MODULE_ID_VOL_CTRL; + softpause.data.param_id = ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS; + softpause.data.param_size = softpause.param.data_payload_size - + sizeof(softpause.data); + softpause.data.reserved = 0; + softpause.enable_flag = pause_param->enable; + softpause.period = pause_param->period; + softpause.step = pause_param->step; + softpause.ramping_curve = pause_param->rampingcurve; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &softpause); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, softpause.data.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + softpause.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + softpause.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +static int __q6asm_set_softvolume(struct audio_client *ac, + struct asm_softvolume_params *softvol_param, + int instance) +{ + struct asm_soft_step_volume_params softvol; + int sz = 0; + int rc = 0; + int module_id; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + switch (instance) { + case SOFT_VOLUME_INSTANCE_2: + module_id = ASM_MODULE_ID_VOL_CTRL2; + break; + case SOFT_VOLUME_INSTANCE_1: + default: + module_id = ASM_MODULE_ID_VOL_CTRL; + break; + } + + sz = sizeof(struct asm_soft_step_volume_params); + q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + softvol.param.data_payload_addr_lsw = 0; + softvol.param.data_payload_addr_msw = 0; + softvol.param.mem_map_handle = 0; + softvol.param.data_payload_size = sizeof(softvol) - + sizeof(softvol.hdr) - sizeof(softvol.param); + softvol.data.module_id = module_id; + softvol.data.param_id = ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS; + softvol.data.param_size = softvol.param.data_payload_size - + sizeof(softvol.data); + softvol.data.reserved = 0; + softvol.period = softvol_param->period; + softvol.step = softvol_param->step; + softvol.ramping_curve = softvol_param->rampingcurve; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &softvol); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, softvol.data.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + softvol.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + softvol.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +int q6asm_set_softvolume(struct audio_client *ac, + struct asm_softvolume_params *softvol_param) +{ + return __q6asm_set_softvolume(ac, softvol_param, + SOFT_VOLUME_INSTANCE_1); +} + +int q6asm_set_softvolume_v2(struct audio_client *ac, + struct asm_softvolume_params *softvol_param, + int instance) +{ + return __q6asm_set_softvolume(ac, softvol_param, instance); +} + +int q6asm_equalizer(struct audio_client *ac, void *eq_p) +{ + struct asm_eq_params eq; + struct msm_audio_eq_stream_config *eq_params = NULL; + int i = 0; + int sz = 0; + int rc = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (eq_p == NULL) { + pr_err("%s: [%d]: Invalid Eq param\n", __func__, ac->session); + rc = -EINVAL; + goto fail_cmd; + } + sz = sizeof(struct asm_eq_params); + eq_params = (struct msm_audio_eq_stream_config *) eq_p; + q6asm_add_hdr(ac, &eq.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + + eq.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + eq.param.data_payload_addr_lsw = 0; + eq.param.data_payload_addr_msw = 0; + eq.param.mem_map_handle = 0; + eq.param.data_payload_size = sizeof(eq) - + sizeof(eq.hdr) - sizeof(eq.param); + eq.data.module_id = ASM_MODULE_ID_EQUALIZER; + eq.data.param_id = ASM_PARAM_ID_EQUALIZER_PARAMETERS; + eq.data.param_size = eq.param.data_payload_size - sizeof(eq.data); + eq.enable_flag = eq_params->enable; + eq.num_bands = eq_params->num_bands; + + pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable, + eq_params->num_bands); + for (i = 0; i < eq_params->num_bands; i++) { + eq.eq_bands[i].band_idx = + eq_params->eq_bands[i].band_idx; + eq.eq_bands[i].filterype = + eq_params->eq_bands[i].filter_type; + eq.eq_bands[i].center_freq_hz = + eq_params->eq_bands[i].center_freq_hz; + eq.eq_bands[i].filter_gain = + eq_params->eq_bands[i].filter_gain; + eq.eq_bands[i].q_factor = + eq_params->eq_bands[i].q_factor; + pr_debug("%s: filter_type:%u bandnum:%d\n", __func__, + eq_params->eq_bands[i].filter_type, i); + pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__, + eq_params->eq_bands[i].center_freq_hz, i); + pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__, + eq_params->eq_bands[i].filter_gain, i); + pr_debug("%s: q_factor:%d bandnum:%d\n", __func__, + eq_params->eq_bands[i].q_factor, i); + } + rc = apr_send_pkt(ac->apr, (uint32_t *)&eq); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, eq.data.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + eq.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + eq.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +static int __q6asm_read(struct audio_client *ac, bool is_custom_len_reqd, + int len) +{ + struct asm_data_cmd_read_v2 read; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + struct audio_buffer *ab; + int dsp_buf; + struct audio_port_data *port; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[OUT]; + + q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE); + + mutex_lock(&port->lock); + + dsp_buf = port->dsp_buf; + if (port->buf == NULL) { + pr_err("%s: buf is NULL\n", __func__); + mutex_unlock(&port->lock); + return -EINVAL; + } + ab = &port->buf[dsp_buf]; + + dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n", + __func__, + ac->session, + dsp_buf, + port->buf[dsp_buf].data, + port->cpu_buf, + &port->buf[port->cpu_buf].phys); + + read.hdr.opcode = ASM_DATA_CMD_READ_V2; + read.buf_addr_lsw = lower_32_bits(ab->phys); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + + list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == ab->phys) + read.mem_map_handle = buf_node->mmap_hdl; + } + dev_vdbg(ac->dev, "memory_map handle in q6asm_read: [%0x]:", + read.mem_map_handle); + read.buf_size = is_custom_len_reqd ? len : ab->size; + read.seq_id = port->dsp_buf; + q6asm_update_token(&read.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + WAIT_CMD); + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + mutex_unlock(&port->lock); + dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n", + __func__, &ab->phys, read.hdr.token, + read.seq_id); + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_err("%s: read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +int q6asm_read(struct audio_client *ac) +{ + return __q6asm_read(ac, false/*is_custom_len_reqd*/, 0); +} +int q6asm_read_v2(struct audio_client *ac, uint32_t len) +{ + return __q6asm_read(ac, true /*is_custom_len_reqd*/, len); +} + +int q6asm_read_nolock(struct audio_client *ac) +{ + struct asm_data_cmd_read_v2 read; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + struct audio_buffer *ab; + int dsp_buf; + struct audio_port_data *port; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[OUT]; + + q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); + + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n", + __func__, + ac->session, + dsp_buf, + port->buf[dsp_buf].data, + port->cpu_buf, + &port->buf[port->cpu_buf].phys); + + read.hdr.opcode = ASM_DATA_CMD_READ_V2; + read.buf_addr_lsw = lower_32_bits(ab->phys); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + read.buf_size = ab->size; + read.seq_id = port->dsp_buf; + q6asm_update_token(&read.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + WAIT_CMD); + + list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == ab->phys) { + read.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n", + __func__, &ab->phys, read.hdr.token, + read.seq_id); + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_err("%s: read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +int q6asm_async_write(struct audio_client *ac, + struct audio_aio_write_param *param) +{ + int rc = 0; + struct asm_data_cmd_write_v2 write; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + struct audio_buffer *ab; + struct audio_port_data *port; + phys_addr_t lbuf_phys_addr; + u32 liomode; + u32 io_compressed; + u32 io_compressed_stream; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + q6asm_stream_add_hdr_async( + ac, &write.hdr, sizeof(write), FALSE, ac->stream_id); + port = &ac->port[IN]; + ab = &port->buf[port->dsp_buf]; + + /* Pass session id as token for AIO scheme */ + write.hdr.token = param->uid; + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(param->paddr); + write.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr); + write.buf_size = param->len; + write.timestamp_msw = param->msw_ts; + write.timestamp_lsw = param->lsw_ts; + liomode = (ASYNC_IO_MODE | NT_MODE); + io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO); + io_compressed_stream = (ASYNC_IO_MODE | COMPRESSED_STREAM_IO); + + if (ac->io_mode == liomode) + lbuf_phys_addr = (param->paddr - 32); + else if (ac->io_mode == io_compressed || + ac->io_mode == io_compressed_stream) + lbuf_phys_addr = (param->paddr - param->metadata_len); + else + lbuf_phys_addr = param->paddr; + + dev_vdbg(ac->dev, "%s: token[0x%x], buf_addr[%pK], buf_size[0x%x], ts_msw[0x%x], ts_lsw[0x%x], lbuf_phys_addr: 0x[%pK]\n", + __func__, + write.hdr.token, ¶m->paddr, + write.buf_size, write.timestamp_msw, + write.timestamp_lsw, &lbuf_phys_addr); + + /* Use 0xFF00 for disabling timestamps */ + if (param->flags == 0xFF00) + write.flags = (0x00000000 | (param->flags & 0x800000FF)); + else + write.flags = (0x80000000 | param->flags); + write.flags |= param->last_buffer << ASM_SHIFT_LAST_BUFFER_FLAG; + write.seq_id = param->uid; + list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == lbuf_phys_addr) { + write.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_err("%s: write op[0x%x]rc[%d]\n", __func__, + write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_async_read(struct audio_client *ac, + struct audio_aio_read_param *param) +{ + int rc = 0; + struct asm_data_cmd_read_v2 read; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + phys_addr_t lbuf_phys_addr; + u32 liomode; + u32 io_compressed; + int dir = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); + + /* Pass session id as token for AIO scheme */ + read.hdr.token = param->uid; + read.hdr.opcode = ASM_DATA_CMD_READ_V2; + read.buf_addr_lsw = lower_32_bits(param->paddr); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr); + read.buf_size = param->len; + read.seq_id = param->uid; + liomode = (NT_MODE | ASYNC_IO_MODE); + io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO); + if (ac->io_mode == liomode) { + lbuf_phys_addr = (param->paddr - 32); + /*legacy wma driver case*/ + dir = IN; + } else if (ac->io_mode == io_compressed) { + lbuf_phys_addr = (param->paddr - 64); + dir = OUT; + } else { + if (param->flags & COMPRESSED_TIMESTAMP_FLAG) + lbuf_phys_addr = param->paddr - + sizeof(struct snd_codec_metadata); + else + lbuf_phys_addr = param->paddr; + dir = OUT; + } + + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == lbuf_phys_addr) { + read.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_err("%s: read op[0x%x]rc[%d]\n", __func__, + read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + int rc = 0; + struct asm_data_cmd_write_v2 write; + struct asm_buffer_node *buf_node = NULL; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + dev_vdbg(ac->dev, "%s: session[%d] len=%d\n", + __func__, ac->session, len); + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[IN]; + + q6asm_add_hdr(ac, &write.hdr, sizeof(write), + FALSE); + mutex_lock(&port->lock); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + q6asm_update_token(&write.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + WAIT_CMD); + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(ab->phys); + write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + write.buf_size = len; + write.seq_id = port->dsp_buf; + write.timestamp_lsw = lsw_ts; + write.timestamp_msw = msw_ts; + /* Use 0xFF00 for disabling timestamps */ + if (flags == 0xFF00) + write.flags = (0x00000000 | (flags & 0x800000FF)); + else + write.flags = (0x80000000 | flags); + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + buf_node = list_first_entry(&ac->port[IN].mem_map_handle, + struct asm_buffer_node, + list); + write.mem_map_handle = buf_node->mmap_hdl; + + dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x] token[0x%x]buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]" + , __func__, + &ab->phys, + write.buf_addr_lsw, + write.hdr.token, + write.seq_id, + write.buf_size, + write.mem_map_handle); + mutex_unlock(&port->lock); + + config_debug_fs_write(ab); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_err("%s: write op[0x%x]rc[%d]\n", + __func__, write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + int rc = 0; + struct asm_data_cmd_write_v2 write; + struct asm_buffer_node *buf_node = NULL; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + dev_vdbg(ac->dev, "%s: session[%d] len=%d\n", + __func__, ac->session, len); + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[IN]; + + q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), + FALSE); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + q6asm_update_token(&write.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + WAIT_CMD); + + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(ab->phys); + write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + write.buf_size = len; + write.seq_id = port->dsp_buf; + write.timestamp_lsw = lsw_ts; + write.timestamp_msw = msw_ts; + buf_node = list_first_entry(&ac->port[IN].mem_map_handle, + struct asm_buffer_node, + list); + write.mem_map_handle = buf_node->mmap_hdl; + /* Use 0xFF00 for disabling timestamps */ + if (flags == 0xFF00) + write.flags = (0x00000000 | (flags & 0x800000FF)); + else + write.flags = (0x80000000 | flags); + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + + dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x]token[0x%x] buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]" + , __func__, + &ab->phys, + write.buf_addr_lsw, + write.hdr.token, + write.seq_id, + write.buf_size, + write.mem_map_handle); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_err("%s: write op[0x%x]rc[%d]\n", + __func__, write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp) +{ + struct asm_mtmx_strtr_get_params mtmx_params; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (tstamp == NULL) { + pr_err("%s: tstamp NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr(ac, &mtmx_params.hdr, sizeof(mtmx_params), TRUE); + mtmx_params.hdr.opcode = ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2; + mtmx_params.param_info.data_payload_addr_lsw = 0; + mtmx_params.param_info.data_payload_addr_msw = 0; + mtmx_params.param_info.mem_map_handle = 0; + mtmx_params.param_info.direction = (ac->io_mode & TUN_READ_IO_MODE + ? 1 : 0); + mtmx_params.param_info.module_id = + ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + mtmx_params.param_info.param_id = + ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3; + mtmx_params.param_info.param_max_size = + sizeof(struct asm_stream_param_data_v2) + + sizeof(struct asm_session_mtmx_strtr_param_session_time_v3_t); + atomic_set(&ac->time_flag, 1); + + dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, mtmx_params.hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &mtmx_params); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", __func__, + mtmx_params.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->time_wait, + (atomic_read(&ac->time_flag) == 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout in getting session time from DSP\n", + __func__); + goto fail_cmd; + } + + *tstamp = ac->time_stamp; + return 0; + +fail_cmd: + return -EINVAL; +} + +int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp) +{ + struct apr_hdr hdr; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (tstamp == NULL) { + pr_err("%s: tstamp NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE); + hdr.opcode = ASM_SESSION_CMD_GET_SESSIONTIME_V3; + atomic_set(&ac->time_flag, 1); + + dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", + __func__, hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->time_wait, + (atomic_read(&ac->time_flag) == 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout in getting session time from DSP\n", + __func__); + goto fail_cmd; + } + + *tstamp = ac->time_stamp; + return 0; + +fail_cmd: + return -EINVAL; +} + + +int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, + uint32_t params_length) +{ + char *asm_params = NULL; + struct apr_hdr hdr; + struct asm_stream_cmd_set_pp_params_v2 payload_params; + int sz, rc; + + pr_debug("%s:\n", __func__); + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (params == NULL) { + pr_err("%s: params NULL\n", __func__); + return -EINVAL; + } + sz = sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_pp_params_v2) + + params_length; + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + pr_err("%s, asm params memory alloc failed", __func__); + return -ENOMEM; + } + q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_pp_params_v2) + + params_length), TRUE); + atomic_set(&ac->cmd_state, -1); + hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + payload_params.data_payload_addr_lsw = 0; + payload_params.data_payload_addr_msw = 0; + payload_params.mem_map_handle = 0; + payload_params.data_payload_size = params_length; + memcpy(((u8 *)asm_params), &hdr, sizeof(struct apr_hdr)); + memcpy(((u8 *)asm_params + sizeof(struct apr_hdr)), &payload_params, + sizeof(struct asm_stream_cmd_set_pp_params_v2)); + memcpy(((u8 *)asm_params + sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_pp_params_v2)), + params, params_length); + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: audio effects set-params send failed\n", __func__); + rc = -EINVAL; + goto fail_send_param; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + if (!rc) { + pr_err("%s: timeout, audio effects set-params\n", __func__); + rc = -ETIMEDOUT; + goto fail_send_param; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_send_param; + } + + rc = 0; +fail_send_param: + kfree(asm_params); + return rc; +} + +int q6asm_send_mtmx_strtr_window(struct audio_client *ac, + struct asm_session_mtmx_strtr_param_window_v2_t *window_param, + uint32_t param_id) +{ + struct asm_mtmx_strtr_params matrix; + int sz = 0; + int rc = 0; + + pr_debug("%s: Window lsw is %d, window msw is %d\n", __func__, + window_param->window_lsw, window_param->window_msw); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct asm_mtmx_strtr_params); + q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2; + + matrix.param.data_payload_addr_lsw = 0; + matrix.param.data_payload_addr_msw = 0; + matrix.param.mem_map_handle = 0; + matrix.param.data_payload_size = sizeof(matrix) - + sizeof(matrix.hdr) - sizeof(matrix.param); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = param_id; + matrix.data.param_size = matrix.param.data_payload_size - + sizeof(matrix.data); + matrix.data.reserved = 0; + matrix.window_lsw = window_param->window_lsw; + matrix.window_msw = window_param->window_msw; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: Render window start send failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, Render window start paramid[0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +}; + +static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id) +{ + struct apr_hdr hdr; + int rc; + atomic_t *state; + int cnt = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, hdr.token, stream_id, ac->session); + switch (cmd) { + case CMD_PAUSE: + pr_debug("%s: CMD_PAUSE\n", __func__); + hdr.opcode = ASM_SESSION_CMD_PAUSE; + state = &ac->cmd_state; + break; + case CMD_SUSPEND: + pr_debug("%s: CMD_SUSPEND\n", __func__); + hdr.opcode = ASM_SESSION_CMD_SUSPEND; + state = &ac->cmd_state; + break; + case CMD_FLUSH: + pr_debug("%s: CMD_FLUSH\n", __func__); + hdr.opcode = ASM_STREAM_CMD_FLUSH; + state = &ac->cmd_state; + break; + case CMD_OUT_FLUSH: + pr_debug("%s: CMD_OUT_FLUSH\n", __func__); + hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS; + state = &ac->cmd_state; + break; + case CMD_EOS: + pr_debug("%s: CMD_EOS\n", __func__); + hdr.opcode = ASM_DATA_CMD_EOS; + atomic_set(&ac->cmd_state, 0); + state = &ac->cmd_state; + break; + case CMD_CLOSE: + pr_debug("%s: CMD_CLOSE\n", __func__); + hdr.opcode = ASM_STREAM_CMD_CLOSE; + state = &ac->cmd_state; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, cmd); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", + __func__, hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(state) > 0) { + pr_err("%s: DSP returned error[%s] opcode %d\n", + __func__, adsp_err_get_err_str( + atomic_read(state)), + hdr.opcode); + rc = adsp_err_get_lnx_err_code(atomic_read(state)); + goto fail_cmd; + } + + if (cmd == CMD_FLUSH) + q6asm_reset_buf_state(ac); + if (cmd == CMD_CLOSE) { + /* check if DSP return all buffers */ + if (ac->port[IN].buf) { + for (cnt = 0; cnt < ac->port[IN].max_buf_cnt; + cnt++) { + if (ac->port[IN].buf[cnt].used == IN) { + dev_vdbg(ac->dev, "Write Buf[%d] not returned\n", + cnt); + } + } + } + if (ac->port[OUT].buf) { + for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) { + if (ac->port[OUT].buf[cnt].used == OUT) { + dev_vdbg(ac->dev, "Read Buf[%d] not returned\n", + cnt); + } + } + } + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_cmd(struct audio_client *ac, int cmd) +{ + return __q6asm_cmd(ac, cmd, ac->stream_id); +} + +int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id) +{ + return __q6asm_cmd(ac, cmd, stream_id); +} + +static int __q6asm_cmd_nowait(struct audio_client *ac, int cmd, + uint32_t stream_id) +{ + struct apr_hdr hdr; + int rc; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id); + atomic_set(&ac->cmd_state, 1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, hdr.token, stream_id, ac->session); + switch (cmd) { + case CMD_PAUSE: + pr_debug("%s: CMD_PAUSE\n", __func__); + hdr.opcode = ASM_SESSION_CMD_PAUSE; + break; + case CMD_EOS: + pr_debug("%s: CMD_EOS\n", __func__); + hdr.opcode = ASM_DATA_CMD_EOS; + break; + case CMD_CLOSE: + pr_debug("%s: CMD_CLOSE\n", __func__); + hdr.opcode = ASM_STREAM_CMD_CLOSE; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, cmd); + goto fail_cmd; + } + pr_debug("%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", + __func__, hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +{ + pr_debug("%s: stream_id: %d\n", __func__, ac->stream_id); + return __q6asm_cmd_nowait(ac, cmd, ac->stream_id); +} + +int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd, + uint32_t stream_id) +{ + pr_debug("%s: stream_id: %d\n", __func__, stream_id); + return __q6asm_cmd_nowait(ac, cmd, stream_id); +} + +int __q6asm_send_meta_data(struct audio_client *ac, uint32_t stream_id, + uint32_t initial_samples, uint32_t trailing_samples) +{ + struct asm_data_cmd_remove_silence silence; + int rc = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + q6asm_stream_add_hdr_async(ac, &silence.hdr, sizeof(silence), FALSE, + stream_id); + + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&silence.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, silence.hdr.token, stream_id, ac->session); + + silence.hdr.opcode = ASM_DATA_CMD_REMOVE_INITIAL_SILENCE; + silence.num_samples_to_remove = initial_samples; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &silence); + if (rc < 0) { + pr_err("%s: Commmand silence failed[%d]", __func__, rc); + + goto fail_cmd; + } + + silence.hdr.opcode = ASM_DATA_CMD_REMOVE_TRAILING_SILENCE; + silence.num_samples_to_remove = trailing_samples; + + + rc = apr_send_pkt(ac->apr, (uint32_t *) &silence); + if (rc < 0) { + pr_err("%s: Commmand silence failed[%d]", __func__, rc); + goto fail_cmd; + } + + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id, + uint32_t initial_samples, uint32_t trailing_samples) +{ + return __q6asm_send_meta_data(ac, stream_id, initial_samples, + trailing_samples); +} + +int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples, + uint32_t trailing_samples) +{ + return __q6asm_send_meta_data(ac, ac->stream_id, initial_samples, + trailing_samples); +} + +static void q6asm_reset_buf_state(struct audio_client *ac) +{ + int cnt = 0; + int loopcnt = 0; + int used; + struct audio_port_data *port = NULL; + + if (ac->io_mode & SYNC_IO_MODE) { + used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0); + mutex_lock(&ac->cmd_lock); + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + cnt = port->max_buf_cnt - 1; + port->dsp_buf = 0; + port->cpu_buf = 0; + while (cnt >= 0) { + if (!port->buf) + continue; + port->buf[cnt].used = used; + cnt--; + } + } + mutex_unlock(&ac->cmd_lock); + } +} + +int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable) +{ + struct asm_session_cmd_regx_overflow tx_overflow; + int rc; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]enable[%d]\n", __func__, + ac->session, enable); + q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE); + atomic_set(&ac->cmd_state, -1); + + tx_overflow.hdr.opcode = + ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS; + /* tx overflow event: enable */ + tx_overflow.enable_flag = enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow); + if (rc < 0) { + pr_err("%s: tx overflow op[0x%x]rc[%d]\n", + __func__, tx_overflow.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for tx overflow\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} + +int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable) +{ + struct asm_session_cmd_rgstr_rx_underflow rx_underflow; + int rc; + + if (!ac) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]enable[%d]\n", __func__, + ac->session, enable); + q6asm_add_hdr_async(ac, &rx_underflow.hdr, sizeof(rx_underflow), FALSE); + + rx_underflow.hdr.opcode = + ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS; + /* tx overflow event: enable */ + rx_underflow.enable_flag = enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &rx_underflow); + if (rc < 0) { + pr_err("%s: tx overflow op[0x%x]rc[%d]\n", + __func__, rx_underflow.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +/* + * q6asm_get_path_delay() - get the path delay for an audio session + * @ac: audio client handle + * + * Retrieves the current audio DSP path delay for the given audio session. + * + * Return: 0 on success, error code otherwise + */ +int q6asm_get_path_delay(struct audio_client *ac) +{ + int rc = 0; + struct apr_hdr hdr; + + if (!ac || ac->apr == NULL) { + pr_err("%s: invalid audio client\n", __func__); + return -EINVAL; + } + + hdr.opcode = ASM_SESSION_CMD_GET_PATH_DELAY_V2; + q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE); + atomic_set(&ac->cmd_state, -1); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", __func__, + hdr.opcode, rc); + return rc; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5 * HZ); + if (!rc) { + pr_err("%s: timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + return -ETIMEDOUT; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + return rc; + } + + return 0; +} + +int q6asm_get_apr_service_id(int session_id) +{ + pr_debug("%s:\n", __func__); + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + return -EINVAL; + } + + return ((struct apr_svc *)session[session_id]->apr)->id; +} + +int q6asm_get_asm_topology(int session_id) +{ + int topology; + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + topology = -EINVAL; + goto done; + } + + topology = session[session_id]->topology; +done: + return topology; +} + +int q6asm_get_asm_app_type(int session_id) +{ + int app_type; + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + app_type = -EINVAL; + goto done; + } + + app_type = session[session_id]->app_type; +done: + return app_type; +} + +static int q6asm_get_asm_topology_cal(void) +{ + int topology = DEFAULT_POPP_TOPOLOGY; + struct cal_block_data *cal_block = NULL; + + if (cal_data[ASM_TOPOLOGY_CAL] == NULL) + goto done; + + mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock); + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]); + if (cal_block == NULL) + goto unlock; + + topology = ((struct audio_cal_info_asm_top *) + cal_block->cal_info)->topology; +unlock: + mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock); +done: + pr_debug("%s: Using topology %d\n", __func__, topology); + return topology; +} + +static int q6asm_get_asm_app_type_cal(void) +{ + int app_type = DEFAULT_APP_TYPE; + struct cal_block_data *cal_block = NULL; + + if (cal_data[ASM_TOPOLOGY_CAL] == NULL) + goto done; + + mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock); + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]); + if (cal_block == NULL) + goto unlock; + + app_type = ((struct audio_cal_info_asm_top *) + cal_block->cal_info)->app_type; + + if (app_type == 0) + app_type = DEFAULT_APP_TYPE; +unlock: + mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock); +done: + pr_debug("%s: Using app_type %d\n", __func__, app_type); + return app_type; +} + +int q6asm_send_cal(struct audio_client *ac) +{ + struct cal_block_data *cal_block = NULL; + struct apr_hdr hdr; + char *asm_params = NULL; + struct asm_stream_cmd_set_pp_params_v2 payload_params; + int sz, rc = -EINVAL; + + pr_debug("%s:\n", __func__); + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + goto done; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + goto done; + } + if (ac->io_mode & NT_MODE) { + pr_debug("%s: called for NT MODE, exiting\n", __func__); + goto done; + } + + if (cal_data[ASM_AUDSTRM_CAL] == NULL) + goto done; + + if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { + rc = 0; /* no cal is required, not error case */ + goto done; + } + + mutex_lock(&cal_data[ASM_AUDSTRM_CAL]->lock); + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_AUDSTRM_CAL]); + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL\n", + __func__); + goto unlock; + } + + if (cal_block->cal_data.size == 0) { + rc = 0; /* not error case */ + pr_debug("%s: cal_data.size is 0, don't send cal data\n", + __func__); + goto unlock; + } + + rc = remap_cal_data(ASM_AUDSTRM_CAL_TYPE, cal_block); + if (rc) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, ASM_AUDSTRM_CAL); + goto unlock; + } + + sz = sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_pp_params_v2); + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + pr_err("%s, asm params memory alloc failed", __func__); + rc = -ENOMEM; + goto unlock; + } + + /* asm_stream_cmd_set_pp_params_v2 has no APR header in it */ + q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_pp_params_v2)), TRUE); + + atomic_set(&ac->cmd_state, 1); + hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + payload_params.data_payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + payload_params.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + payload_params.mem_map_handle = cal_block->map_data.q6map_handle; + payload_params.data_payload_size = cal_block->cal_data.size; + memcpy(((u8 *)asm_params), &hdr, sizeof(struct apr_hdr)); + memcpy(((u8 *)asm_params + sizeof(struct apr_hdr)), &payload_params, + sizeof(struct asm_stream_cmd_set_pp_params_v2)); + + pr_debug("%s: phyaddr lsw = %x msw = %x, maphdl = %x calsize = %d\n", + __func__, payload_params.data_payload_addr_lsw, + payload_params.data_payload_addr_msw, + payload_params.mem_map_handle, + payload_params.data_payload_size); + + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: audio audstrm cal send failed\n", __func__); + rc = -EINVAL; + goto free; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) <= 0), 5 * HZ); + if (!rc) { + pr_err("%s: timeout, audio audstrm cal send\n", __func__); + rc = -ETIMEDOUT; + goto free; + } + if (atomic_read(&ac->cmd_state) < 0) { + pr_err("%s: DSP returned error[%d] audio audstrm cal send\n", + __func__, atomic_read(&ac->cmd_state)); + rc = -EINVAL; + goto free; + } + + rc = 0; + +free: + kfree(asm_params); +unlock: + mutex_unlock(&cal_data[ASM_AUDSTRM_CAL]->lock); +done: + return rc; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case ASM_TOPOLOGY_CAL_TYPE: + ret = ASM_TOPOLOGY_CAL; + break; + case ASM_CUST_TOPOLOGY_CAL_TYPE: + ret = ASM_CUSTOM_TOP_CAL; + break; + case ASM_AUDSTRM_CAL_TYPE: + ret = ASM_AUDSTRM_CAL; + break; + case ASM_RTAC_APR_CAL_TYPE: + ret = ASM_RTAC_APR_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int q6asm_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6asm_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6asm_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == ASM_CUSTOM_TOP_CAL) { + mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + set_custom_topology = 1; + mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + } +done: + return ret; +} + +static void q6asm_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + cal_utils_destroy_cal_types(ASM_MAX_CAL_TYPES, cal_data); +} + +static int q6asm_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{ASM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + q6asm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ASM_CUST_TOPOLOGY_CAL_TYPE, + {q6asm_alloc_cal, q6asm_dealloc_cal, NULL, + q6asm_set_cal, NULL, NULL} }, + {NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} }, + + {{ASM_AUDSTRM_CAL_TYPE, + {q6asm_alloc_cal, q6asm_dealloc_cal, NULL, + q6asm_set_cal, NULL, NULL} }, + {NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} }, + + {{ASM_RTAC_APR_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} } + }; + pr_debug("%s\n", __func__); + + ret = cal_utils_create_cal_types(ASM_MAX_CAL_TYPES, cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type! %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + return ret; +err: + q6asm_delete_cal_data(); + return ret; +} + +static int q6asm_is_valid_session(struct apr_client_data *data, void *priv) +{ + struct audio_client *ac = (struct audio_client *)priv; + union asm_token_struct asm_token; + + asm_token.token = data->token; + if (asm_token._token.session_id != ac->session) { + pr_err("%s: Invalid session[%d] rxed expected[%d]", + __func__, asm_token._token.session_id, ac->session); + return -EINVAL; + } + return 0; +} + +static int __init q6asm_init(void) +{ + int lcnt, ret; + + pr_debug("%s:\n", __func__); + + memset(session, 0, sizeof(session)); + set_custom_topology = 1; + + /*setup common client used for cal mem map */ + common_client.session = ASM_CONTROL_SESSION; + common_client.port[0].buf = &common_buf[0]; + common_client.port[1].buf = &common_buf[1]; + init_waitqueue_head(&common_client.cmd_wait); + init_waitqueue_head(&common_client.time_wait); + init_waitqueue_head(&common_client.mem_wait); + atomic_set(&common_client.time_flag, 1); + INIT_LIST_HEAD(&common_client.port[0].mem_map_handle); + INIT_LIST_HEAD(&common_client.port[1].mem_map_handle); + mutex_init(&common_client.cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&common_client.port[lcnt].lock); + spin_lock_init(&common_client.port[lcnt].dsp_lock); + } + atomic_set(&common_client.cmd_state, 0); + atomic_set(&common_client.mem_state, 0); + + ret = q6asm_init_cal_data(); + if (ret) + pr_err("%s: could not init cal data! ret %d\n", + __func__, ret); + + config_debug_fs_init(); + + return 0; +} + +static void __exit q6asm_exit(void) +{ + q6asm_delete_cal_data(); +} + +device_initcall(q6asm_init); +__exitcall(q6asm_exit); diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c new file mode 100644 index 000000000000..ea78e6a6538e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c @@ -0,0 +1,807 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int q6audio_get_port_index(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX; + case AFE_PORT_ID_PRIMARY_PCM_RX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_RX; + case AFE_PORT_ID_PRIMARY_PCM_TX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_TX; + case AFE_PORT_ID_SECONDARY_PCM_RX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_RX; + case AFE_PORT_ID_SECONDARY_PCM_TX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_TX; + case AFE_PORT_ID_TERTIARY_PCM_RX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_RX; + case AFE_PORT_ID_TERTIARY_PCM_TX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_TX; + case AFE_PORT_ID_QUATERNARY_PCM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX; + case AFE_PORT_ID_QUATERNARY_PCM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX; + case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX; + case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX; + case MI2S_RX: return IDX_MI2S_RX; + case MI2S_TX: return IDX_MI2S_TX; + case HDMI_RX: return IDX_HDMI_RX; + case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX; + case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return IDX_DIGI_MIC_TX; + case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX; + case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX; + case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX; + case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX; + case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX; + case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX; + case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX; + case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX; + case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX; + case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX; + case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX; + case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX; + case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX; + case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX; + case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX; + case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX; + case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX; + case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX; + case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX; + case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX; + case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX; + case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX; + case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX; + case INT_FM_RX: return IDX_INT_FM_RX; + case INT_FM_TX: return IDX_INT_FM_TX; + case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX; + case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX; + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX; + case AFE_PORT_ID_PRIMARY_MI2S_TX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX; + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_TX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX; + case AFE_PORT_ID_TERTIARY_MI2S_RX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX; + case AUDIO_PORT_ID_I2S_RX: + return IDX_AUDIO_PORT_ID_I2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + case AFE_PORT_ID_PRIMARY_TDM_RX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_SENARY_MI2S_TX: + return IDX_AFE_PORT_ID_SENARY_MI2S_TX; + case AFE_PORT_ID_USB_RX: + return IDX_AFE_PORT_ID_USB_RX; + case AFE_PORT_ID_USB_TX: + return IDX_AFE_PORT_ID_USB_TX; + case AFE_PORT_ID_INT0_MI2S_RX: + return IDX_AFE_PORT_ID_INT0_MI2S_RX; + case AFE_PORT_ID_INT0_MI2S_TX: + return IDX_AFE_PORT_ID_INT0_MI2S_TX; + case AFE_PORT_ID_INT1_MI2S_RX: + return IDX_AFE_PORT_ID_INT1_MI2S_RX; + case AFE_PORT_ID_INT1_MI2S_TX: + return IDX_AFE_PORT_ID_INT1_MI2S_TX; + case AFE_PORT_ID_INT2_MI2S_RX: + return IDX_AFE_PORT_ID_INT2_MI2S_RX; + case AFE_PORT_ID_INT2_MI2S_TX: + return IDX_AFE_PORT_ID_INT2_MI2S_TX; + case AFE_PORT_ID_INT3_MI2S_RX: + return IDX_AFE_PORT_ID_INT3_MI2S_RX; + case AFE_PORT_ID_INT3_MI2S_TX: + return IDX_AFE_PORT_ID_INT3_MI2S_TX; + case AFE_PORT_ID_INT4_MI2S_RX: + return IDX_AFE_PORT_ID_INT4_MI2S_RX; + case AFE_PORT_ID_INT4_MI2S_TX: + return IDX_AFE_PORT_ID_INT4_MI2S_TX; + case AFE_PORT_ID_INT5_MI2S_RX: + return IDX_AFE_PORT_ID_INT5_MI2S_RX; + case AFE_PORT_ID_INT5_MI2S_TX: + return IDX_AFE_PORT_ID_INT5_MI2S_TX; + case AFE_PORT_ID_INT6_MI2S_RX: + return IDX_AFE_PORT_ID_INT6_MI2S_RX; + case AFE_PORT_ID_INT6_MI2S_TX: + return IDX_AFE_PORT_ID_INT6_MI2S_TX; + default: return -EINVAL; + } +} + +int q6audio_get_port_id(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return PRIMARY_I2S_TX; + case AFE_PORT_ID_PRIMARY_PCM_RX: + return AFE_PORT_ID_PRIMARY_PCM_RX; + case AFE_PORT_ID_PRIMARY_PCM_TX: + return AFE_PORT_ID_PRIMARY_PCM_TX; + case AFE_PORT_ID_SECONDARY_PCM_RX: + return AFE_PORT_ID_SECONDARY_PCM_RX; + case AFE_PORT_ID_SECONDARY_PCM_TX: + return AFE_PORT_ID_SECONDARY_PCM_TX; + case AFE_PORT_ID_TERTIARY_PCM_RX: + return AFE_PORT_ID_TERTIARY_PCM_RX; + case AFE_PORT_ID_TERTIARY_PCM_TX: + return AFE_PORT_ID_TERTIARY_PCM_TX; + case AFE_PORT_ID_QUATERNARY_PCM_RX: + return AFE_PORT_ID_QUATERNARY_PCM_RX; + case AFE_PORT_ID_QUATERNARY_PCM_TX: + return AFE_PORT_ID_QUATERNARY_PCM_TX; + case SECONDARY_I2S_RX: return AFE_PORT_ID_SECONDARY_MI2S_RX; + case SECONDARY_I2S_TX: return AFE_PORT_ID_SECONDARY_MI2S_TX; + case MI2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX; + case MI2S_TX: return AFE_PORT_ID_PRIMARY_MI2S_TX; + case HDMI_RX: return AFE_PORT_ID_MULTICHAN_HDMI_RX; + case DISPLAY_PORT_RX: + return AFE_PORT_ID_HDMI_OVER_DP_RX; + case AFE_PORT_ID_SPDIF_RX: return AFE_PORT_ID_SPDIF_RX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return AFE_PORT_ID_DIGITAL_MIC_TX; + case VOICE_RECORD_RX: return AFE_PORT_ID_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return AFE_PORT_ID_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return AFE_PORT_ID_VOICE_PLAYBACK_TX; + case VOICE2_PLAYBACK_TX: return AFE_PORT_ID_VOICE2_PLAYBACK_TX; + case SLIMBUS_0_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX; + case SLIMBUS_0_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX; + case SLIMBUS_1_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX; + case SLIMBUS_1_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX; + case SLIMBUS_2_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX; + case SLIMBUS_2_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX; + case SLIMBUS_3_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX; + case SLIMBUS_3_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX; + case SLIMBUS_4_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX; + case SLIMBUS_4_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX; + case SLIMBUS_5_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX; + case SLIMBUS_5_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX; + case SLIMBUS_6_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX; + case SLIMBUS_6_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX; + case SLIMBUS_7_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX; + case SLIMBUS_7_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX; + case SLIMBUS_8_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX; + case SLIMBUS_8_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX; + case INT_BT_SCO_RX: return AFE_PORT_ID_INTERNAL_BT_SCO_RX; + case INT_BT_SCO_TX: return AFE_PORT_ID_INTERNAL_BT_SCO_TX; + case INT_BT_A2DP_RX: return AFE_PORT_ID_INTERNAL_BT_A2DP_RX; + case INT_FM_RX: return AFE_PORT_ID_INTERNAL_FM_RX; + case INT_FM_TX: return AFE_PORT_ID_INTERNAL_FM_TX; + case RT_PROXY_PORT_001_RX: return AFE_PORT_ID_RT_PROXY_PORT_001_RX; + case RT_PROXY_PORT_001_TX: return AFE_PORT_ID_RT_PROXY_PORT_001_TX; + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return AFE_PORT_ID_PRIMARY_MI2S_RX; + case AFE_PORT_ID_PRIMARY_MI2S_TX: + return AFE_PORT_ID_PRIMARY_MI2S_TX; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + return AFE_PORT_ID_QUATERNARY_MI2S_RX; + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return AFE_PORT_ID_QUATERNARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return AFE_PORT_ID_SECONDARY_MI2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_TX: + return AFE_PORT_ID_SECONDARY_MI2S_TX; + case AFE_PORT_ID_TERTIARY_MI2S_RX: + return AFE_PORT_ID_TERTIARY_MI2S_RX; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return AFE_PORT_ID_TERTIARY_MI2S_TX; + case AUDIO_PORT_ID_I2S_RX: + return AUDIO_PORT_ID_I2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + return AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + case AFE_PORT_ID_PRIMARY_TDM_RX: + return AFE_PORT_ID_PRIMARY_TDM_RX; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return AFE_PORT_ID_PRIMARY_TDM_TX; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return AFE_PORT_ID_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return AFE_PORT_ID_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return AFE_PORT_ID_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return AFE_PORT_ID_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return AFE_PORT_ID_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return AFE_PORT_ID_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return AFE_PORT_ID_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return AFE_PORT_ID_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return AFE_PORT_ID_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return AFE_PORT_ID_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return AFE_PORT_ID_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return AFE_PORT_ID_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return AFE_PORT_ID_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return AFE_PORT_ID_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return AFE_PORT_ID_SECONDARY_TDM_RX; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return AFE_PORT_ID_SECONDARY_TDM_TX; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return AFE_PORT_ID_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return AFE_PORT_ID_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return AFE_PORT_ID_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return AFE_PORT_ID_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return AFE_PORT_ID_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return AFE_PORT_ID_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return AFE_PORT_ID_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return AFE_PORT_ID_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return AFE_PORT_ID_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return AFE_PORT_ID_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return AFE_PORT_ID_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return AFE_PORT_ID_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return AFE_PORT_ID_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return AFE_PORT_ID_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return AFE_PORT_ID_TERTIARY_TDM_RX; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return AFE_PORT_ID_TERTIARY_TDM_TX; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return AFE_PORT_ID_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return AFE_PORT_ID_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return AFE_PORT_ID_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return AFE_PORT_ID_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return AFE_PORT_ID_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return AFE_PORT_ID_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return AFE_PORT_ID_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return AFE_PORT_ID_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return AFE_PORT_ID_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return AFE_PORT_ID_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return AFE_PORT_ID_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return AFE_PORT_ID_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return AFE_PORT_ID_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return AFE_PORT_ID_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return AFE_PORT_ID_QUATERNARY_TDM_RX; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return AFE_PORT_ID_QUATERNARY_TDM_TX; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return AFE_PORT_ID_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return AFE_PORT_ID_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return AFE_PORT_ID_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return AFE_PORT_ID_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return AFE_PORT_ID_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return AFE_PORT_ID_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return AFE_PORT_ID_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return AFE_PORT_ID_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return AFE_PORT_ID_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return AFE_PORT_ID_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return AFE_PORT_ID_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return AFE_PORT_ID_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return AFE_PORT_ID_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return AFE_PORT_ID_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_SENARY_MI2S_TX: + return AFE_PORT_ID_SENARY_MI2S_TX; + case AFE_PORT_ID_USB_RX: + return AFE_PORT_ID_USB_RX; + case AFE_PORT_ID_USB_TX: + return AFE_PORT_ID_USB_TX; + case AFE_PORT_ID_INT0_MI2S_RX: + return AFE_PORT_ID_INT0_MI2S_RX; + case AFE_PORT_ID_INT0_MI2S_TX: + return AFE_PORT_ID_INT0_MI2S_TX; + case AFE_PORT_ID_INT1_MI2S_RX: + return AFE_PORT_ID_INT1_MI2S_RX; + case AFE_PORT_ID_INT1_MI2S_TX: + return AFE_PORT_ID_INT1_MI2S_TX; + case AFE_PORT_ID_INT2_MI2S_RX: + return AFE_PORT_ID_INT2_MI2S_RX; + case AFE_PORT_ID_INT2_MI2S_TX: + return AFE_PORT_ID_INT2_MI2S_TX; + case AFE_PORT_ID_INT3_MI2S_RX: + return AFE_PORT_ID_INT3_MI2S_RX; + case AFE_PORT_ID_INT3_MI2S_TX: + return AFE_PORT_ID_INT3_MI2S_TX; + case AFE_PORT_ID_INT4_MI2S_RX: + return AFE_PORT_ID_INT4_MI2S_RX; + case AFE_PORT_ID_INT4_MI2S_TX: + return AFE_PORT_ID_INT4_MI2S_TX; + case AFE_PORT_ID_INT5_MI2S_RX: + return AFE_PORT_ID_INT5_MI2S_RX; + case AFE_PORT_ID_INT5_MI2S_TX: + return AFE_PORT_ID_INT5_MI2S_TX; + case AFE_PORT_ID_INT6_MI2S_RX: + return AFE_PORT_ID_INT6_MI2S_RX; + case AFE_PORT_ID_INT6_MI2S_TX: + return AFE_PORT_ID_INT6_MI2S_TX; + default: + pr_warn("%s: Invalid port_id %d\n", __func__, port_id); + return -EINVAL; + } +} +int q6audio_convert_virtual_to_portid(u16 port_id) +{ + int ret; + + /* if port_id is virtual, convert to physical.. + * if port_id is already physical, return physical + */ + if (q6audio_validate_port(port_id) < 0) { + if (port_id == RT_PROXY_DAI_001_RX || + port_id == RT_PROXY_DAI_001_TX || + port_id == RT_PROXY_DAI_002_RX || + port_id == RT_PROXY_DAI_002_TX) + ret = VIRTUAL_ID_TO_PORTID(port_id); + else + ret = -EINVAL; + } else + ret = port_id; + + return ret; +} + +int q6audio_is_digital_pcm_interface(u16 port_id) +{ + int ret = 0; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AUDIO_PORT_ID_I2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_TX: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +int q6audio_validate_port(u16 port_id) +{ + int ret; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case HDMI_RX: + case DISPLAY_PORT_RX: + case RSVD_2: + case RSVD_3: + case DIGI_MIC_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_5_TX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case INT_BT_SCO_RX: + case INT_BT_SCO_TX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case INT_FM_TX: + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_SPDIF_RX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_TX: + { + ret = 0; + break; + } + + default: + ret = -EINVAL; + } + + return ret; +} diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c new file mode 100644 index 000000000000..ac3dd0ef710a --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -0,0 +1,951 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_MS 1000 +/* + * AVS bring up in the modem is optimitized for the new + * Sub System Restart design and 100 milliseconds timeout + * is sufficient to make sure the Q6 will be ready. + */ +#define Q6_READY_TIMEOUT_MS 100 + +enum { + META_CAL, + CUST_TOP_CAL, + CORE_MAX_CAL +}; + +struct q6core_str { + struct apr_svc *core_handle_q; + wait_queue_head_t bus_bw_req_wait; + wait_queue_head_t cmd_req_wait; + u32 bus_bw_resp_received; + enum cmd_flags { + FLAG_NONE, + FLAG_CMDRSP_LICENSE_RESULT + } cmd_resp_received_flag; + struct mutex cmd_lock; + union { + struct avcs_cmdrsp_get_license_validation_result + cmdrsp_license_result; + } cmd_resp_payload; + u32 param; + struct cal_type_data *cal_data[CORE_MAX_CAL]; + uint32_t mem_map_cal_handle; + int32_t adsp_status; +}; + +static struct q6core_str q6core_lcl; + +struct generic_get_data_ { + int valid; + int size_in_ints; + int ints[]; +}; +static struct generic_get_data_ *generic_get_data; + +static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) +{ + uint32_t *payload1; + + if (data == NULL) { + pr_err("%s: data argument is null\n", __func__); + return -EINVAL; + } + + pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%x\n", + __func__, + data->payload_size, data->opcode); + + switch (data->opcode) { + + case APR_BASIC_RSP_RESULT:{ + + if (data->payload_size == 0) { + pr_err("%s: APR_BASIC_RSP_RESULT No Payload ", + __func__); + return 0; + } + + payload1 = data->payload; + + switch (payload1[0]) { + + case AVCS_CMD_SHARED_MEM_UNMAP_REGIONS: + pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS status[0x%x]\n", + __func__, payload1[1]); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_SHARED_MEM_MAP_REGIONS: + pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_MAP_REGIONS status[0x%x]\n", + __func__, payload1[1]); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_REGISTER_TOPOLOGIES: + pr_debug("%s: Cmd = AVCS_CMD_REGISTER_TOPOLOGIES status[0x%x]\n", + __func__, payload1[1]); + /* -ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_DEREGISTER_TOPOLOGIES: + pr_debug("%s: Cmd = AVCS_CMD_DEREGISTER_TOPOLOGIES status[0x%x]\n", + __func__, payload1[1]); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + default: + pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n", + __func__, + payload1[0], payload1[1], data->opcode); + break; + } + break; + } + + case RESET_EVENTS:{ + pr_debug("%s: Reset event received in Core service\n", + __func__); + apr_reset(q6core_lcl.core_handle_q); + q6core_lcl.core_handle_q = NULL; + break; + } + case AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS: + payload1 = data->payload; + pr_debug("%s: AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS handle %d\n", + __func__, payload1[0]); + q6core_lcl.mem_map_cal_handle = payload1[0]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMDRSP_ADSP_EVENT_GET_STATE: + payload1 = data->payload; + q6core_lcl.param = payload1[0]; + pr_debug("%s: Received ADSP get state response 0x%x\n", + __func__, q6core_lcl.param); + /* ensure .param is updated prior to .bus_bw_resp_received */ + wmb(); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT: + payload1 = data->payload; + pr_debug("%s: cmd = LICENSE_VALIDATION_RESULT, result = 0x%x\n", + __func__, payload1[0]); + q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result + = payload1[0]; + q6core_lcl.cmd_resp_received_flag = FLAG_CMDRSP_LICENSE_RESULT; + wake_up(&q6core_lcl.cmd_req_wait); + break; + default: + pr_err("%s: Message id from adsp core svc: 0x%x\n", + __func__, data->opcode); + if (generic_get_data) { + generic_get_data->valid = 1; + generic_get_data->size_in_ints = + data->payload_size/sizeof(int); + pr_debug("DTS_EAGLE_CORE callback size = %i\n", + data->payload_size); + memcpy(generic_get_data->ints, data->payload, + data->payload_size); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + } + break; + } + + return 0; +} + +void ocm_core_open(void) +{ + if (q6core_lcl.core_handle_q == NULL) + q6core_lcl.core_handle_q = apr_register("ADSP", "CORE", + aprv2_core_fn_q, 0xFFFFFFFF, NULL); + pr_debug("%s: Open_q %pK\n", __func__, q6core_lcl.core_handle_q); + if (q6core_lcl.core_handle_q == NULL) + pr_err("%s: Unable to register CORE\n", __func__); +} + +int32_t core_set_license(uint32_t key, uint32_t module_id) +{ + struct avcs_cmd_set_license *cmd_setl = NULL; + struct audio_cal_info_metainfo *metainfo = NULL; + struct cal_block_data *cal_block = NULL; + int rc = 0, packet_size = 0; + + pr_debug("%s: key:0x%x, id:0x%x\n", __func__, key, module_id); + + mutex_lock(&(q6core_lcl.cmd_lock)); + if (q6core_lcl.cal_data[META_CAL] == NULL) { + pr_err("%s: cal_data not initialized yet!!\n", __func__); + rc = -EINVAL; + goto cmd_unlock; + } + + mutex_lock(&((q6core_lcl.cal_data[META_CAL])->lock)); + cal_block = + cal_utils_get_only_cal_block(q6core_lcl.cal_data[META_CAL]); + if (cal_block == NULL || + cal_block->cal_data.kvaddr == NULL || + cal_block->cal_data.size <= 0) { + pr_err("%s: Invalid cal block to send", __func__); + rc = -EINVAL; + goto cal_data_unlock; + } + metainfo = (struct audio_cal_info_metainfo *)cal_block->cal_info; + if (metainfo == NULL) { + pr_err("%s: No metainfo!!!", __func__); + rc = -EINVAL; + goto cal_data_unlock; + } + if (metainfo->nKey != key) { + pr_err("%s: metainfo key mismatch!!! found:%x, needed:%x\n", + __func__, metainfo->nKey, key); + rc = -EINVAL; + goto cal_data_unlock; + } else if (key == 0) { + pr_err("%s: metainfo key is %d a invalid key", __func__, key); + goto cal_data_unlock; + } + + packet_size = sizeof(struct avcs_cmd_set_license) + + cal_block->cal_data.size; + /*round up total packet_size to next 4 byte boundary*/ + packet_size = ((packet_size + 0x3)>>2)<<2; + + cmd_setl = kzalloc(packet_size, GFP_KERNEL); + if (cmd_setl == NULL) { + rc = -ENOMEM; + goto cal_data_unlock; + } + + ocm_core_open(); + if (q6core_lcl.core_handle_q == NULL) { + pr_err("%s: apr registration for CORE failed\n", __func__); + rc = -ENODEV; + goto fail_cmd; + } + + cmd_setl->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd_setl->hdr.pkt_size = packet_size; + cmd_setl->hdr.src_port = 0; + cmd_setl->hdr.dest_port = 0; + cmd_setl->hdr.token = 0; + cmd_setl->hdr.opcode = AVCS_CMD_SET_LICENSE; + cmd_setl->id = module_id; + cmd_setl->overwrite = 1; + cmd_setl->size = cal_block->cal_data.size; + memcpy((uint8_t *)cmd_setl + sizeof(struct avcs_cmd_set_license), + cal_block->cal_data.kvaddr, + cal_block->cal_data.size); + pr_info("%s: Set license opcode=0x%x ,key=0x%x, id =0x%x, size = %d\n", + __func__, cmd_setl->hdr.opcode, + metainfo->nKey, cmd_setl->id, cmd_setl->size); + rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)cmd_setl); + if (rc < 0) + pr_err("%s: SET_LICENSE failed op[0x%x]rc[%d]\n", + __func__, cmd_setl->hdr.opcode, rc); + +fail_cmd: + kfree(cmd_setl); +cal_data_unlock: + mutex_unlock(&((q6core_lcl.cal_data[META_CAL])->lock)); +cmd_unlock: + mutex_unlock(&(q6core_lcl.cmd_lock)); + + return rc; +} + +int32_t core_get_license_status(uint32_t module_id) +{ + struct avcs_cmd_get_license_validation_result get_lvr_cmd; + int ret = 0; + + pr_debug("%s: module_id 0x%x", __func__, module_id); + + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q == NULL) { + pr_err("%s: apr registration for CORE failed\n", __func__); + ret = -ENODEV; + goto fail_cmd; + } + + get_lvr_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + get_lvr_cmd.hdr.pkt_size = + sizeof(struct avcs_cmd_get_license_validation_result); + + get_lvr_cmd.hdr.src_port = 0; + get_lvr_cmd.hdr.dest_port = 0; + get_lvr_cmd.hdr.token = 0; + get_lvr_cmd.hdr.opcode = AVCS_CMD_GET_LICENSE_VALIDATION_RESULT; + get_lvr_cmd.id = module_id; + + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &get_lvr_cmd); + if (ret < 0) { + pr_err("%s: license_validation request failed, err %d\n", + __func__, ret); + ret = -EREMOTE; + goto fail_cmd; + } + + q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT); + mutex_unlock(&(q6core_lcl.cmd_lock)); + ret = wait_event_timeout(q6core_lcl.cmd_req_wait, + (q6core_lcl.cmd_resp_received_flag == + FLAG_CMDRSP_LICENSE_RESULT), + msecs_to_jiffies(TIMEOUT_MS)); + mutex_lock(&(q6core_lcl.cmd_lock)); + if (!ret) { + pr_err("%s: wait_event timeout for CMDRSP_LICENSE_RESULT\n", + __func__); + ret = -ETIME; + goto fail_cmd; + } + q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT); + ret = q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result; + +fail_cmd: + mutex_unlock(&(q6core_lcl.cmd_lock)); + pr_info("%s: cmdrsp_license_result.result = 0x%x for module 0x%x\n", + __func__, ret, module_id); + return ret; +} + +int core_dts_eagle_set(int size, char *data) +{ + struct adsp_dts_eagle *payload = NULL; + int rc = 0, size_aligned4byte; + + pr_debug("DTS_EAGLE_CORE - %s\n", __func__); + if (size <= 0 || !data) { + pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n", + __func__, size, data); + return -EINVAL; + } + + size_aligned4byte = (size+3) & 0xFFFFFFFC; + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q) { + payload = kzalloc(sizeof(struct adsp_dts_eagle) + + size_aligned4byte, GFP_KERNEL); + if (!payload) { + rc = -ENOMEM; + goto exit; + } + payload->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + payload->hdr.pkt_size = sizeof(struct adsp_dts_eagle) + + size_aligned4byte; + payload->hdr.src_port = 0; + payload->hdr.dest_port = 0; + payload->hdr.token = 0; + payload->hdr.opcode = ADSP_CMD_SET_DTS_EAGLE_DATA_ID; + payload->id = DTS_EAGLE_LICENSE_ID; + payload->overwrite = 1; + payload->size = size; + memcpy(payload->data, data, size); + rc = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *)payload); + if (rc < 0) { + pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n", + __func__, payload->hdr.opcode, rc); + } + kfree(payload); + } + +exit: + mutex_unlock(&(q6core_lcl.cmd_lock)); + return rc; +} + +int core_dts_eagle_get(int id, int size, char *data) +{ + struct apr_hdr ah; + int rc = 0; + + pr_debug("DTS_EAGLE_CORE - %s\n", __func__); + if (size <= 0 || !data) { + pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n", + __func__, size, data); + return -EINVAL; + } + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q) { + ah.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + ah.pkt_size = sizeof(struct apr_hdr); + ah.src_port = 0; + ah.dest_port = 0; + ah.token = 0; + ah.opcode = id; + + q6core_lcl.bus_bw_resp_received = 0; + generic_get_data = kzalloc(sizeof(struct generic_get_data_) + + size, GFP_KERNEL); + if (!generic_get_data) { + rc = -ENOMEM; + goto exit; + } + + rc = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *)&ah); + if (rc < 0) { + pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n", + __func__, ah.opcode, rc); + goto exit; + } + + rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("DTS_EAGLE_CORE - %s: EAGLE get params timed out\n", + __func__); + rc = -EINVAL; + goto exit; + } + if (generic_get_data->valid) { + rc = 0; + memcpy(data, generic_get_data->ints, size); + } else { + rc = -EINVAL; + pr_err("DTS_EAGLE_CORE - %s: EAGLE get params problem getting data - check callback error value\n", + __func__); + } + } + +exit: + kfree(generic_get_data); + generic_get_data = NULL; + mutex_unlock(&(q6core_lcl.cmd_lock)); + return rc; +} + +uint32_t core_set_dolby_manufacturer_id(int manufacturer_id) +{ + struct adsp_dolby_manufacturer_id payload; + int rc = 0; + + pr_debug("%s: manufacturer_id :%d\n", __func__, manufacturer_id); + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q) { + payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + payload.hdr.pkt_size = + sizeof(struct adsp_dolby_manufacturer_id); + payload.hdr.src_port = 0; + payload.hdr.dest_port = 0; + payload.hdr.token = 0; + payload.hdr.opcode = ADSP_CMD_SET_DOLBY_MANUFACTURER_ID; + payload.manufacturer_id = manufacturer_id; + pr_debug("%s: Send Dolby security opcode=0x%x manufacturer ID = %d\n", + __func__, + payload.hdr.opcode, payload.manufacturer_id); + rc = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *)&payload); + if (rc < 0) + pr_err("%s: SET_DOLBY_MANUFACTURER_ID failed op[0x%x]rc[%d]\n", + __func__, payload.hdr.opcode, rc); + } + mutex_unlock(&(q6core_lcl.cmd_lock)); + return rc; +} + +bool q6core_is_adsp_ready(void) +{ + int rc; + bool ret = false; + struct apr_hdr hdr; + + pr_debug("%s: enter\n", __func__); + memset(&hdr, 0, sizeof(hdr)); + hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); + hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE; + + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + q6core_lcl.bus_bw_resp_received = 0; + rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr); + if (rc < 0) { + pr_err("%s: Get ADSP state APR packet send event %d\n", + __func__, rc); + goto bail; + } + + rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && q6core_lcl.bus_bw_resp_received) { + /* ensure to read updated param by callback thread */ + rmb(); + ret = !!q6core_lcl.param; + } +bail: + pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret); + mutex_unlock(&(q6core_lcl.cmd_lock)); + return ret; +} + + +static int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload) + * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; + mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mmap_regions->hdr.pkt_size = cmd_size; + mmap_regions->hdr.src_port = 0; + mmap_regions->hdr.dest_port = 0; + mmap_regions->hdr.token = 0; + mmap_regions->hdr.opcode = AVCS_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff; + mmap_regions->num_regions = bufcnt & 0x00ff; + mmap_regions->property_flag = 0x00; + + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->shm_addr_lsw = lower_32_bits(buf_add[i]); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add[i]); + mregions->mem_size_bytes = bufsz[i]; + ++mregions; + } + + pr_debug("%s: sending memory map, addr %pK, size %d, bufcnt = %d\n", + __func__, buf_add, bufsz[0], mmap_regions->num_regions); + + *map_handle = 0; + q6core_lcl.bus_bw_resp_received = 0; + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) + mmap_regions); + if (ret < 0) { + pr_err("%s: mmap regions failed %d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for memory map\n", __func__); + ret = -ETIME; + goto done; + } + + *map_handle = q6core_lcl.mem_map_cal_handle; +done: + kfree(mmap_region_cmd); + return ret; +} + +static int q6core_memory_unmap_regions(uint32_t mem_map_handle) +{ + struct avs_cmd_shared_mem_unmap_regions unmap_regions; + int ret = 0; + + memset(&unmap_regions, 0, sizeof(unmap_regions)); + unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + unmap_regions.hdr.pkt_size = sizeof(unmap_regions); + unmap_regions.hdr.src_svc = APR_SVC_ADSP_CORE; + unmap_regions.hdr.src_domain = APR_DOMAIN_APPS; + unmap_regions.hdr.src_port = 0; + unmap_regions.hdr.dest_svc = APR_SVC_ADSP_CORE; + unmap_regions.hdr.dest_domain = APR_DOMAIN_ADSP; + unmap_regions.hdr.dest_port = 0; + unmap_regions.hdr.token = 0; + unmap_regions.hdr.opcode = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS; + unmap_regions.mem_map_handle = mem_map_handle; + + q6core_lcl.bus_bw_resp_received = 0; + + pr_debug("%s: unmap regions map handle %d\n", + __func__, mem_map_handle); + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) + &unmap_regions); + if (ret < 0) { + pr_err("%s: unmap regions failed %d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for memory_unmap\n", + __func__); + ret = -ETIME; + goto done; + } +done: + return ret; +} + +static int q6core_dereg_all_custom_topologies(void) +{ + int ret = 0; + struct avcs_cmd_deregister_topologies dereg_top; + + memset(&dereg_top, 0, sizeof(dereg_top)); + dereg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + dereg_top.hdr.pkt_size = sizeof(dereg_top); + dereg_top.hdr.src_svc = APR_SVC_ADSP_CORE; + dereg_top.hdr.src_domain = APR_DOMAIN_APPS; + dereg_top.hdr.src_port = 0; + dereg_top.hdr.dest_svc = APR_SVC_ADSP_CORE; + dereg_top.hdr.dest_domain = APR_DOMAIN_ADSP; + dereg_top.hdr.dest_port = 0; + dereg_top.hdr.token = 0; + dereg_top.hdr.opcode = AVCS_CMD_DEREGISTER_TOPOLOGIES; + dereg_top.payload_addr_lsw = 0; + dereg_top.payload_addr_msw = 0; + dereg_top.mem_map_handle = 0; + dereg_top.payload_size = 0; + dereg_top.mode = AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES; + + q6core_lcl.bus_bw_resp_received = 0; + + pr_debug("%s: Deregister topologies mode %d\n", + __func__, dereg_top.mode); + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &dereg_top); + if (ret < 0) { + pr_err("%s: Deregister topologies failed %d\n", + __func__, ret); + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for Deregister topologies\n", + __func__); + goto done; + } +done: + return ret; +} + +static int q6core_send_custom_topologies(void) +{ + int ret = 0; + int ret2 = 0; + struct cal_block_data *cal_block = NULL; + struct avcs_cmd_register_topologies reg_top; + + if (!q6core_is_adsp_ready()) { + pr_err("%s: ADSP is not ready!\n", __func__); + return -ENODEV; + } + + memset(®_top, 0, sizeof(reg_top)); + mutex_lock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock); + mutex_lock(&q6core_lcl.cmd_lock); + + cal_block = cal_utils_get_only_cal_block( + q6core_lcl.cal_data[CUST_TOP_CAL]); + if (cal_block == NULL) { + pr_debug("%s: cal block is NULL!\n", __func__); + goto unlock; + } + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: cal size is %zd not sending\n", + __func__, cal_block->cal_data.size); + goto unlock; + } + + q6core_dereg_all_custom_topologies(); + + ret = q6core_map_memory_regions(&cal_block->cal_data.paddr, 0, + (uint32_t *)&cal_block->map_data.map_size, 1, + &cal_block->map_data.q6map_handle); + if (!ret) { + pr_err("%s: q6core_map_memory_regions failed\n", __func__); + goto unlock; + } + + reg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + reg_top.hdr.pkt_size = sizeof(reg_top); + reg_top.hdr.src_svc = APR_SVC_ADSP_CORE; + reg_top.hdr.src_domain = APR_DOMAIN_APPS; + reg_top.hdr.src_port = 0; + reg_top.hdr.dest_svc = APR_SVC_ADSP_CORE; + reg_top.hdr.dest_domain = APR_DOMAIN_ADSP; + reg_top.hdr.dest_port = 0; + reg_top.hdr.token = 0; + reg_top.hdr.opcode = AVCS_CMD_REGISTER_TOPOLOGIES; + reg_top.payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + reg_top.payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + reg_top.mem_map_handle = cal_block->map_data.q6map_handle; + reg_top.payload_size = cal_block->cal_data.size; + + q6core_lcl.adsp_status = 0; + q6core_lcl.bus_bw_resp_received = 0; + + pr_debug("%s: Register topologies addr %pK, size %zd, map handle %d\n", + __func__, &cal_block->cal_data.paddr, cal_block->cal_data.size, + cal_block->map_data.q6map_handle); + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) ®_top); + if (ret < 0) { + pr_err("%s: Register topologies failed %d\n", + __func__, ret); + goto unmap; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for Register topologies\n", + __func__); + goto unmap; + } + + if (q6core_lcl.adsp_status < 0) + ret = q6core_lcl.adsp_status; +unmap: + ret2 = q6core_memory_unmap_regions(cal_block->map_data.q6map_handle); + if (!ret2) { + pr_err("%s: q6core_memory_unmap_regions failed for map handle %d\n", + __func__, cal_block->map_data.q6map_handle); + ret = ret2; + goto unlock; + } + +unlock: + mutex_unlock(&q6core_lcl.cmd_lock); + mutex_unlock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock); + + return ret; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case AUDIO_CORE_METAINFO_CAL_TYPE: + ret = META_CAL; + break; + case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: + ret = CUST_TOP_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int q6core_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + ret = cal_utils_alloc_cal(data_size, data, + q6core_lcl.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + goto done; + } +done: + return ret; +} + +static int q6core_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + ret = cal_utils_dealloc_cal(data_size, data, + q6core_lcl.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + goto done; + } +done: + return ret; +} + +static int q6core_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + ret = cal_utils_set_cal(data_size, data, + q6core_lcl.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + goto done; + } + + if (cal_index == CUST_TOP_CAL) + ret = q6core_send_custom_topologies(); +done: + return ret; +} + +static void q6core_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(CORE_MAX_CAL, q6core_lcl.cal_data); +} + + +static int q6core_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{AUDIO_CORE_METAINFO_CAL_TYPE, + {q6core_alloc_cal, q6core_dealloc_cal, NULL, + q6core_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CORE_CUSTOM_TOPOLOGIES_CAL_TYPE, + {q6core_alloc_cal, q6core_dealloc_cal, NULL, + q6core_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} } + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(CORE_MAX_CAL, + q6core_lcl.cal_data, cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type!\n", + __func__); + goto err; + } + + return ret; +err: + q6core_delete_cal_data(); + return ret; +} + +static int __init core_init(void) +{ + init_waitqueue_head(&q6core_lcl.bus_bw_req_wait); + q6core_lcl.bus_bw_resp_received = 0; + + q6core_lcl.core_handle_q = NULL; + + init_waitqueue_head(&q6core_lcl.cmd_req_wait); + q6core_lcl.cmd_resp_received_flag = FLAG_NONE; + mutex_init(&q6core_lcl.cmd_lock); + q6core_lcl.mem_map_cal_handle = 0; + q6core_lcl.adsp_status = 0; + + q6core_init_cal_data(); + return 0; +} +module_init(core_init); + +static void __exit core_exit(void) +{ + mutex_destroy(&q6core_lcl.cmd_lock); + q6core_delete_cal_data(); +} +module_exit(core_exit); +MODULE_DESCRIPTION("ADSP core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c new file mode 100644 index 000000000000..d761cf561eaa --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6lsm.c @@ -0,0 +1,1950 @@ +/* + * Copyright (c) 2013-2017, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APR_TIMEOUT (5 * HZ) +#define LSM_ALIGN_BOUNDARY 512 +#define LSM_SAMPLE_RATE 16000 +#define QLSM_PARAM_ID_MINOR_VERSION 1 +static int lsm_afe_port; + +enum { + LSM_CUSTOM_TOP_IDX, + LSM_TOP_IDX, + LSM_CAL_IDX, + LSM_MAX_CAL_IDX +}; + +enum { + CMD_STATE_CLEARED = 0, + CMD_STATE_WAIT_RESP = 1, +}; + +enum { + LSM_INVALID_SESSION_ID = 0, + LSM_MIN_SESSION_ID = 1, + LSM_MAX_SESSION_ID = 8, + LSM_CONTROL_SESSION = 0x0F, +}; + +#define CHECK_SESSION(x) (x < LSM_MIN_SESSION_ID || x > LSM_MAX_SESSION_ID) +struct lsm_common { + void *apr; + atomic_t apr_users; + struct lsm_client common_client[LSM_MAX_SESSION_ID + 1]; + + int set_custom_topology; + struct cal_type_data *cal_data[LSM_MAX_CAL_IDX]; + + struct mutex apr_lock; +}; + +struct lsm_module_param_ids { + uint32_t module_id; + uint32_t param_id; +}; + +static struct lsm_common lsm_common; +/* + * mmap_handle_p can point either client->sound_model.mem_map_handle or + * lsm_common.mmap_handle_for_cal. + * mmap_lock must be held while accessing this. + */ +static spinlock_t mmap_lock; +static uint32_t *mmap_handle_p; + +static spinlock_t lsm_session_lock; +static struct lsm_client *lsm_session[LSM_MAX_SESSION_ID + 1]; + +static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv); +static int q6lsm_send_cal(struct lsm_client *client, u32 set_params_opcode); +static int q6lsm_memory_map_regions(struct lsm_client *client, + dma_addr_t dma_addr_p, uint32_t dma_buf_sz, + uint32_t *mmap_p); +static int q6lsm_memory_unmap_regions(struct lsm_client *client, + uint32_t handle); + +static void q6lsm_set_param_hdr_info( + struct lsm_set_params_hdr *param_hdr, + u32 payload_size, u32 addr_lsw, u32 addr_msw, + u32 mmap_handle) +{ + param_hdr->data_payload_size = payload_size; + param_hdr->data_payload_addr_lsw = addr_lsw; + param_hdr->data_payload_addr_msw = addr_msw; + param_hdr->mem_map_handle = mmap_handle; +} + +static void q6lsm_set_param_common( + struct lsm_param_payload_common *common, + struct lsm_module_param_ids *ids, + u32 param_size, u32 set_param_version) +{ + common->module_id = ids->module_id; + common->param_id = ids->param_id; + + switch (set_param_version) { + case LSM_SESSION_CMD_SET_PARAMS_V2: + common->p_size.param_size = param_size; + break; + case LSM_SESSION_CMD_SET_PARAMS: + default: + common->p_size.sr.param_size = + (u16) param_size; + common->p_size.sr.reserved = 0; + break; + } +} + +static int q6lsm_callback(struct apr_client_data *data, void *priv) +{ + struct lsm_client *client = (struct lsm_client *)priv; + uint32_t token; + uint32_t *payload; + + if (!client || !data) { + pr_err("%s: client %pK data %pK\n", + __func__, client, data); + WARN_ON(1); + return -EINVAL; + } + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: SSR event received 0x%x, event 0x%x, proc 0x%x\n", + __func__, data->opcode, data->reset_event, + data->reset_proc); + + cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX, + lsm_common.cal_data); + mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + lsm_common.set_custom_topology = 1; + mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + return 0; + } + + payload = data->payload; + pr_debug("%s: Session %d opcode 0x%x token 0x%x payload size %d\n" + "payload [0] = 0x%x\n", __func__, client->session, + data->opcode, data->token, data->payload_size, payload[0]); + if (data->opcode == LSM_DATA_EVENT_READ_DONE) { + struct lsm_cmd_read_done read_done; + + token = data->token; + if (data->payload_size > sizeof(read_done)) { + pr_err("%s: read done error payload size %d expected size %zd\n", + __func__, data->payload_size, + sizeof(read_done)); + return -EINVAL; + } + pr_debug("%s: opcode %x status %x lsw %x msw %x mem_map handle %x\n", + __func__, data->opcode, payload[0], payload[1], + payload[2], payload[3]); + read_done.status = payload[0]; + read_done.buf_addr_lsw = payload[1]; + read_done.buf_addr_msw = payload[2]; + read_done.mem_map_handle = payload[3]; + read_done.total_size = payload[4]; + read_done.offset = payload[5]; + if (client->cb) + client->cb(data->opcode, data->token, + (void *)&read_done, + client->priv); + return 0; + } else if (data->opcode == APR_BASIC_RSP_RESULT) { + token = data->token; + switch (payload[0]) { + case LSM_SESSION_CMD_START: + case LSM_SESSION_CMD_STOP: + case LSM_SESSION_CMD_SET_PARAMS: + case LSM_SESSION_CMD_OPEN_TX: + case LSM_SESSION_CMD_CLOSE_TX: + case LSM_SESSION_CMD_REGISTER_SOUND_MODEL: + case LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL: + case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS: + case LSM_SESSION_CMD_EOB: + case LSM_SESSION_CMD_READ: + case LSM_SESSION_CMD_OPEN_TX_V2: + case LSM_CMD_ADD_TOPOLOGIES: + case LSM_SESSION_CMD_SET_PARAMS_V2: + if (token != client->session && + payload[0] != + LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL) { + pr_err("%s: Invalid session %d receivced expected %d\n", + __func__, token, client->session); + return -EINVAL; + } + client->cmd_err_code = payload[1]; + if (client->cmd_err_code) + pr_err("%s: cmd 0x%x failed status %d\n", + __func__, payload[0], client->cmd_err_code); + if (atomic_cmpxchg(&client->cmd_state, + CMD_STATE_WAIT_RESP, + CMD_STATE_CLEARED) == + CMD_STATE_WAIT_RESP) + wake_up(&client->cmd_wait); + break; + default: + pr_debug("%s: Unknown command 0x%x\n", + __func__, payload[0]); + break; + } + return 0; + } + + if (client->cb) + client->cb(data->opcode, data->token, data->payload, + client->priv); + + return 0; +} + +static int q6lsm_session_alloc(struct lsm_client *client) +{ + unsigned long flags; + int n, ret = -ENOMEM; + + spin_lock_irqsave(&lsm_session_lock, flags); + for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) { + if (!lsm_session[n]) { + lsm_session[n] = client; + ret = n; + break; + } + } + spin_unlock_irqrestore(&lsm_session_lock, flags); + pr_debug("%s: Alloc Session %d", __func__, n); + return ret; +} + +static void q6lsm_session_free(struct lsm_client *client) +{ + unsigned long flags; + + pr_debug("%s: Freeing session ID %d\n", __func__, client->session); + spin_lock_irqsave(&lsm_session_lock, flags); + lsm_session[client->session] = LSM_INVALID_SESSION_ID; + spin_unlock_irqrestore(&lsm_session_lock, flags); + client->session = LSM_INVALID_SESSION_ID; +} + +static void *q6lsm_mmap_apr_reg(void) +{ + if (atomic_inc_return(&lsm_common.apr_users) == 1) { + lsm_common.apr = + apr_register("ADSP", "LSM", q6lsm_mmapcallback, + 0x0FFFFFFFF, &lsm_common); + if (!lsm_common.apr) { + pr_debug("%s: Unable to register APR LSM common port\n", + __func__); + atomic_dec(&lsm_common.apr_users); + } + } + return lsm_common.apr; +} + +static int q6lsm_mmap_apr_dereg(void) +{ + if (atomic_read(&lsm_common.apr_users) <= 0) { + WARN("%s: APR common port already closed\n", __func__); + } else { + if (atomic_dec_return(&lsm_common.apr_users) == 0) { + apr_deregister(lsm_common.apr); + pr_debug("%s: APR De-Register common port\n", __func__); + } + } + return 0; +} + +struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv) +{ + struct lsm_client *client; + int n; + + client = kzalloc(sizeof(struct lsm_client), GFP_KERNEL); + if (!client) + return NULL; + n = q6lsm_session_alloc(client); + if (n <= 0) { + kfree(client); + return NULL; + } + client->session = n; + client->cb = cb; + client->priv = priv; + if (CHECK_SESSION(client->session)) { + pr_err("%s: Client session %d\n", + __func__, client->session); + kfree(client); + return NULL; + } + pr_debug("%s: Client Session %d\n", __func__, client->session); + client->apr = apr_register("ADSP", "LSM", q6lsm_callback, + ((client->session) << 8 | client->session), + client); + + if (client->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + goto fail; + } + + pr_debug("%s: Registering the common port with APR\n", __func__); + client->mmap_apr = q6lsm_mmap_apr_reg(); + if (!client->mmap_apr) { + pr_err("%s: APR registration failed\n", __func__); + goto fail; + } + + init_waitqueue_head(&client->cmd_wait); + mutex_init(&client->cmd_lock); + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + pr_debug("%s: New client allocated\n", __func__); + return client; +fail: + q6lsm_client_free(client); + return NULL; +} + +void q6lsm_client_free(struct lsm_client *client) +{ + if (!client) + return; + if (CHECK_SESSION(client->session)) { + pr_err("%s: Invalid Session %d\n", __func__, client->session); + return; + } + apr_deregister(client->apr); + client->mmap_apr = NULL; + q6lsm_session_free(client); + q6lsm_mmap_apr_dereg(); + mutex_destroy(&client->cmd_lock); + kfree(client); + client = NULL; +} + +/* + * q6lsm_apr_send_pkt : If wait == true, hold mutex to prevent from preempting + * other thread's wait. + * If mmap_handle_p != NULL, disable irq and spin lock to + * protect mmap_handle_p + */ +static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, + void *data, bool wait, uint32_t *mmap_p) +{ + int ret; + unsigned long flags = 0; + struct apr_hdr *msg_hdr = (struct apr_hdr *) data; + + pr_debug("%s: enter wait %d\n", __func__, wait); + if (wait) + mutex_lock(&lsm_common.apr_lock); + if (mmap_p) { + WARN_ON(!wait); + spin_lock_irqsave(&mmap_lock, flags); + mmap_handle_p = mmap_p; + } + atomic_set(&client->cmd_state, CMD_STATE_WAIT_RESP); + client->cmd_err_code = 0; + ret = apr_send_pkt(handle, data); + if (mmap_p) + spin_unlock_irqrestore(&mmap_lock, flags); + + if (ret < 0) { + pr_err("%s: apr_send_pkt failed %d\n", __func__, ret); + } else if (wait) { + ret = wait_event_timeout(client->cmd_wait, + (atomic_read(&client->cmd_state) == + CMD_STATE_CLEARED), + APR_TIMEOUT); + if (likely(ret)) { + /* q6 returned error */ + if (client->cmd_err_code) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + client->cmd_err_code)); + ret = adsp_err_get_lnx_err_code( + client->cmd_err_code); + } else { + ret = 0; + } + } else { + pr_err("%s: wait timedout, apr_opcode = 0x%x, size = %d\n", + __func__, msg_hdr->opcode, msg_hdr->pkt_size); + /* ret = 0 means wait timed out */ + ret = -ETIMEDOUT; + } + } else { + ret = 0; + } + if (wait) + mutex_unlock(&lsm_common.apr_lock); + + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; +} + +static void q6lsm_add_hdr(struct lsm_client *client, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg) +{ + pr_debug("%s: pkt_size %d cmd_flg %d session %d\n", __func__, + pkt_size, cmd_flg, client->session); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = APR_SVC_LSM; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_LSM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((client->session << 8) & 0xFF00) | client->session; + hdr->dest_port = ((client->session << 8) & 0xFF00) | client->session; + hdr->pkt_size = pkt_size; + if (cmd_flg) + hdr->token = client->session; +} + + +static int q6lsm_send_custom_topologies(struct lsm_client *client) +{ + int rc; + struct cal_block_data *cal_block = NULL; + struct lsm_custom_topologies cstm_top; + + if (lsm_common.cal_data[LSM_CUSTOM_TOP_IDX] == NULL) { + pr_err("%s: LSM_CUSTOM_TOP_IDX invalid\n", __func__); + rc = -EINVAL; + goto done; + } + + lsm_common.set_custom_topology = 0; + + mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + cal_block = cal_utils_get_only_cal_block( + lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]); + if (!cal_block) { + pr_err("%s: Cal block for LSM_CUSTOM_TOP_IDX not found\n", + __func__); + rc = -EINVAL; + goto unlock; + } + + if (cal_block->cal_data.size <= 0) { + pr_err("%s: Invalid size for LSM_CUSTOM_TOP %zd\n", + __func__, cal_block->cal_data.size); + rc = -EINVAL; + goto unlock; + } + + memset(&cstm_top, 0, sizeof(cstm_top)); + /* Map the memory for out-of-band data */ + rc = q6lsm_memory_map_regions(client, cal_block->cal_data.paddr, + cal_block->map_data.map_size, + &cal_block->map_data.q6map_handle); + if (rc < 0) { + pr_err("%s: Failed to map custom topologied, err = %d\n", + __func__, rc); + goto unlock; + } + + q6lsm_add_hdr(client, &cstm_top.hdr, + sizeof(cstm_top), true); + cstm_top.hdr.opcode = LSM_CMD_ADD_TOPOLOGIES; + + /* + * For ADD_TOPOLOGIES, the dest_port should be 0 + * Note that source port cannot be zero as it is used + * to route the response to a specific client registered + * on APR + */ + cstm_top.hdr.dest_port = 0; + + cstm_top.data_payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cstm_top.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + cstm_top.mem_map_handle = cal_block->map_data.q6map_handle; + cstm_top.buffer_size = cal_block->cal_data.size; + + rc = q6lsm_apr_send_pkt(client, client->apr, + &cstm_top, true, NULL); + if (rc) + pr_err("%s: Failed to add custom top, err = %d\n", + __func__, rc); + /* go ahead and unmap even if custom top failed */ + rc = q6lsm_memory_unmap_regions(client, + cal_block->map_data.q6map_handle); + if (rc) { + pr_err("%s: Failed to unmap, err = %d\n", + __func__, rc); + /* Even if mem unmap failed, treat the cmd as success */ + rc = 0; + } + +unlock: + mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); +done: + return rc; +} + +static int q6lsm_do_open_v2(struct lsm_client *client, + uint16_t app_id) +{ + int rc; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_lsm_top *lsm_top; + struct lsm_stream_cmd_open_tx_v2 open_v2; + + if (lsm_common.cal_data[LSM_TOP_IDX] == NULL) { + pr_err("%s: LSM_TOP_IDX invalid\n", __func__); + rc = -EINVAL; + goto done; + } + + mutex_lock(&lsm_common.cal_data[LSM_TOP_IDX]->lock); + cal_block = cal_utils_get_only_cal_block( + lsm_common.cal_data[LSM_TOP_IDX]); + if (!cal_block) { + pr_err("%s: Cal block for LSM_TOP_IDX not found\n", + __func__); + rc = -EINVAL; + goto unlock; + } + + lsm_top = (struct audio_cal_info_lsm_top *) + cal_block->cal_info; + if (!lsm_top) { + pr_err("%s: cal_info for LSM_TOP_IDX not found\n", + __func__); + rc = -EINVAL; + goto unlock; + } + + pr_debug("%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n", + __func__, lsm_top->topology, lsm_top->acdb_id, + lsm_top->app_type); + + if (lsm_top->topology == 0) { + pr_err("%s: toplogy id not sent for app_type 0x%x\n", + __func__, lsm_top->app_type); + rc = -EINVAL; + goto unlock; + } + + client->app_id = lsm_top->app_type; + memset(&open_v2, 0, sizeof(open_v2)); + q6lsm_add_hdr(client, &open_v2.hdr, + sizeof(open_v2), true); + open_v2.topology_id = lsm_top->topology; + open_v2.hdr.opcode = LSM_SESSION_CMD_OPEN_TX_V2; + + rc = q6lsm_apr_send_pkt(client, client->apr, + &open_v2, true, NULL); + if (rc) + pr_err("%s: open_v2 failed, err = %d\n", + __func__, rc); + else + client->use_topology = true; +unlock: + mutex_unlock(&lsm_common.cal_data[LSM_TOP_IDX]->lock); +done: + return rc; + +} + +void q6lsm_sm_set_param_data(struct lsm_client *client, + struct lsm_params_info *p_info, + size_t *offset) +{ + struct lsm_param_payload_common *param; + + param = (struct lsm_param_payload_common *) + client->sound_model.data; + param->module_id = p_info->module_id; + param->param_id = p_info->param_id; + param->p_size.param_size = client->sound_model.size; + *offset = sizeof(*param); +} + +int q6lsm_open(struct lsm_client *client, uint16_t app_id) +{ + int rc = 0; + struct lsm_stream_cmd_open_tx open; + + /* Add Custom topologies if needed */ + if (lsm_common.set_custom_topology) { + rc = q6lsm_send_custom_topologies(client); + if (rc) + pr_err("%s: Failed to send cust_top, err = %d\n", + __func__, rc); + } + + /* Try to open with topology first */ + rc = q6lsm_do_open_v2(client, app_id); + if (!rc) + /* open_v2 was successful */ + goto done; + + pr_debug("%s: try without topology\n", + __func__); + + memset(&open, 0, sizeof(open)); + q6lsm_add_hdr(client, &open.hdr, sizeof(open), true); + switch (client->app_id) { + case LSM_VOICE_WAKEUP_APP_ID_V2: + open.app_id = client->app_id; + break; + default: + pr_err("%s: default err 0x%x\n", __func__, client->app_id); + rc = -EINVAL; + break; + } + + open.sampling_rate = LSM_SAMPLE_RATE; + open.hdr.opcode = LSM_SESSION_CMD_OPEN_TX; + rc = q6lsm_apr_send_pkt(client, client->apr, + &open, true, NULL); + if (rc) + pr_err("%s: Open failed opcode 0x%x, rc %d\n", + __func__, open.hdr.opcode, rc); + else + client->use_topology = false; +done: + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} + +static int q6lsm_send_confidence_levels( + struct lsm_client *client, + struct lsm_module_param_ids *ids, + u32 set_param_opcode) +{ + u8 *packet; + size_t pkt_size; + struct lsm_cmd_set_params_conf *conf_params; + struct apr_hdr *msg_hdr; + struct lsm_param_min_confidence_levels *cfl; + uint8_t i = 0; + uint8_t padd_size = 0; + u8 *conf_levels; + int rc; + u32 payload_size, param_size; + + padd_size = (4 - (client->num_confidence_levels % 4)) - 1; + pkt_size = sizeof(*conf_params) + padd_size + + client->num_confidence_levels; + + packet = kzalloc(pkt_size, GFP_KERNEL); + if (!packet) + return -ENOMEM; + + conf_params = (struct lsm_cmd_set_params_conf *) packet; + conf_levels = (u8 *) (packet + sizeof(*conf_params)); + msg_hdr = &conf_params->msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + pkt_size, true); + msg_hdr->opcode = set_param_opcode; + payload_size = pkt_size - sizeof(*msg_hdr) - + sizeof(conf_params->params_hdr); + q6lsm_set_param_hdr_info(&conf_params->params_hdr, + payload_size, 0, 0, 0); + cfl = &conf_params->conf_payload; + param_size = ((sizeof(uint8_t) + padd_size + + client->num_confidence_levels)) * + sizeof(uint8_t); + q6lsm_set_param_common(&cfl->common, ids, + param_size, set_param_opcode); + cfl->num_confidence_levels = client->num_confidence_levels; + + pr_debug("%s: CMD PARAM SIZE = %d\n", + __func__, param_size); + pr_debug("%s: Num conf_level = %d\n", + __func__, client->num_confidence_levels); + + memcpy(conf_levels, client->confidence_levels, + client->num_confidence_levels); + for (i = 0; i < client->num_confidence_levels; i++) + pr_debug("%s: Confidence_level[%d] = %d\n", + __func__, i, conf_levels[i]); + + rc = q6lsm_apr_send_pkt(client, client->apr, + packet, true, NULL); + if (rc) + pr_err("%s: confidence_levels cmd failed, err = %d\n", + __func__, rc); + kfree(packet); + return rc; +} + +static int q6lsm_send_params(struct lsm_client *client, + struct lsm_module_param_ids *opmode_ids, + struct lsm_module_param_ids *connectport_ids, + u32 set_param_opcode) +{ + int rc; + struct lsm_cmd_set_opmode_connectport opmode_connectport; + struct apr_hdr *msg_hdr; + struct lsm_param_connect_to_port *connect_to_port; + struct lsm_param_op_mode *op_mode; + u32 data_payload_size, param_size; + + msg_hdr = &opmode_connectport.msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + sizeof(opmode_connectport), true); + msg_hdr->opcode = set_param_opcode; + data_payload_size = sizeof(opmode_connectport) - + sizeof(*msg_hdr) - + sizeof(opmode_connectport.params_hdr); + q6lsm_set_param_hdr_info(&opmode_connectport.params_hdr, + data_payload_size, 0, 0, 0); + connect_to_port = &opmode_connectport.connect_to_port; + op_mode = &opmode_connectport.op_mode; + + param_size = sizeof(struct lsm_param_op_mode) - + sizeof(op_mode->common); + q6lsm_set_param_common(&op_mode->common, + opmode_ids, param_size, + set_param_opcode); + op_mode->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + op_mode->mode = client->mode; + op_mode->reserved = 0; + pr_debug("%s: mode = 0x%x", __func__, op_mode->mode); + + param_size = (sizeof(struct lsm_param_connect_to_port) - + sizeof(connect_to_port->common)); + q6lsm_set_param_common(&connect_to_port->common, + connectport_ids, param_size, + set_param_opcode); + connect_to_port->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + connect_to_port->port_id = client->connect_to_port; + connect_to_port->reserved = 0; + pr_debug("%s: port= %d", __func__, connect_to_port->port_id); + + rc = q6lsm_apr_send_pkt(client, client->apr, + &opmode_connectport, true, NULL); + if (rc) + pr_err("%s: Failed set_params opcode 0x%x, rc %d\n", + __func__, msg_hdr->opcode, rc); + + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} + +void set_lsm_port(int lsm_port) +{ + lsm_afe_port = lsm_port; +} + +int get_lsm_port(void) +{ + return lsm_afe_port; +} + +int q6lsm_set_data(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure) +{ + int rc = 0; + struct lsm_module_param_ids opmode_ids, connectport_ids; + struct lsm_module_param_ids conf_levels_ids; + + if (!client->confidence_levels) { + /* + * It is possible that confidence levels are + * not provided. This is not a error condition. + * Return gracefully without any error + */ + pr_debug("%s: no conf levels to set\n", + __func__); + return rc; + } + + if (mode == LSM_MODE_KEYWORD_ONLY_DETECTION) { + client->mode = 0x01; + } else if (mode == LSM_MODE_USER_KEYWORD_DETECTION) { + client->mode = 0x03; + } else { + pr_err("%s: Incorrect detection mode %d\n", __func__, mode); + rc = -EINVAL; + goto err_ret; + } + client->mode |= detectfailure << 2; + client->connect_to_port = get_lsm_port(); + + opmode_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + opmode_ids.param_id = LSM_PARAM_ID_OPERATION_MODE; + + connectport_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + rc = q6lsm_send_params(client, &opmode_ids, &connectport_ids, + LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Failed to set lsm config params %d\n", + __func__, rc); + goto err_ret; + } + + conf_levels_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + conf_levels_ids.param_id = LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS; + + rc = q6lsm_send_confidence_levels(client, &conf_levels_ids, + LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Failed to send conf_levels, err = %d\n", + __func__, rc); + goto err_ret; + } + + rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Failed to send calibration data %d\n", + __func__, rc); + goto err_ret; + } + +err_ret: + return rc; +} + +int q6lsm_register_sound_model(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure) +{ + int rc; + struct lsm_cmd_reg_snd_model cmd; + + memset(&cmd, 0, sizeof(cmd)); + rc = q6lsm_set_data(client, mode, detectfailure); + if (rc) { + pr_err("%s: Failed to set lsm data, err = %d\n", + __func__, rc); + return rc; + } + + q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd), true); + cmd.hdr.opcode = LSM_SESSION_CMD_REGISTER_SOUND_MODEL; + cmd.model_addr_lsw = lower_32_bits(client->sound_model.phys); + cmd.model_addr_msw = msm_audio_populate_upper_32_bits( + client->sound_model.phys); + cmd.model_size = client->sound_model.size; + /* read updated mem_map_handle by q6lsm_mmapcallback */ + rmb(); + cmd.mem_map_handle = client->sound_model.mem_map_handle; + + pr_debug("%s: addr %pK, size %d, handle 0x%x\n", __func__, + &client->sound_model.phys, cmd.model_size, cmd.mem_map_handle); + rc = q6lsm_apr_send_pkt(client, client->apr, &cmd, true, NULL); + if (rc) + pr_err("%s: Failed cmd op[0x%x]rc[%d]\n", __func__, + cmd.hdr.opcode, rc); + else + pr_debug("%s: Register sound model succeeded\n", __func__); + + return rc; +} + +int q6lsm_deregister_sound_model(struct lsm_client *client) +{ + int rc; + struct lsm_cmd_reg_snd_model cmd; + + if (!client) { + pr_err("APR handle NULL\n"); + return -EINVAL; + } + if (!client->apr) { + pr_err("APR client handle NULL\n"); + return -EINVAL; + } + + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false); + cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL; + + rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL); + if (rc) { + pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__, + cmd.hdr.opcode, rc); + } else { + pr_debug("%s: Deregister sound model succeeded\n", __func__); + } + + q6lsm_snd_model_buf_free(client); + + return rc; +} + +static void q6lsm_add_mmaphdr(struct lsm_client *client, struct apr_hdr *hdr, + u32 pkt_size, u32 cmd_flg, u32 token) +{ + pr_debug("%s: pkt size=%d cmd_flg=%d session=%d\n", __func__, pkt_size, + cmd_flg, client->session); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0x00; + hdr->dest_port = client->session; + if (cmd_flg) + hdr->token = token; + hdr->pkt_size = pkt_size; +} + +static int q6lsm_memory_map_regions(struct lsm_client *client, + dma_addr_t dma_addr_p, uint32_t dma_buf_sz, + uint32_t *mmap_p) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int rc; + int cmd_size = 0; + + pr_debug("%s: dma_addr_p 0x%pK, dma_buf_sz %d, mmap_p 0x%pK, session %d\n", + __func__, &dma_addr_p, dma_buf_sz, mmap_p, + client->session); + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload); + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; + q6lsm_add_mmaphdr(client, &mmap_regions->hdr, cmd_size, true, + (client->session << 8)); + + mmap_regions->hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mmap_regions->num_regions = 1; + mmap_regions->property_flag = 0x00; + payload = ((u8 *)mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + mregions->shm_addr_lsw = lower_32_bits(dma_addr_p); + mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p); + mregions->mem_size_bytes = dma_buf_sz; + + rc = q6lsm_apr_send_pkt(client, client->mmap_apr, mmap_region_cmd, + true, mmap_p); + if (rc) + pr_err("%s: Failed mmap_regions opcode 0x%x, rc %d\n", + __func__, mmap_regions->hdr.opcode, rc); + + pr_debug("%s: leave %d\n", __func__, rc); + kfree(mmap_region_cmd); + return rc; +} + +static int q6lsm_memory_unmap_regions(struct lsm_client *client, + uint32_t handle) +{ + struct avs_cmd_shared_mem_unmap_regions unmap; + int rc = 0; + int cmd_size = 0; + + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions); + q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size, + true, (client->session << 8)); + unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS; + unmap.mem_map_handle = handle; + + pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle); + rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true, + NULL); + if (rc) + pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n", + __func__, unmap.hdr.opcode, rc); + + return rc; +} + +static int q6lsm_send_cal(struct lsm_client *client, + u32 set_params_opcode) +{ + int rc = 0; + struct lsm_cmd_set_params params; + struct lsm_set_params_hdr *params_hdr = ¶ms.param_hdr; + struct apr_hdr *msg_hdr = ¶ms.msg_hdr; + struct cal_block_data *cal_block = NULL; + + pr_debug("%s: Session id %d\n", __func__, client->session); + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + + if (lsm_common.cal_data[LSM_CAL_IDX] == NULL) + goto done; + + mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); + cal_block = cal_utils_get_only_cal_block( + lsm_common.cal_data[LSM_CAL_IDX]); + if (cal_block == NULL) + goto unlock; + + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: No cal to send!\n", __func__); + rc = -EINVAL; + goto unlock; + } + if (cal_block->cal_data.size != client->lsm_cal_size) { + pr_err("%s: Cal size %zd doesn't match lsm cal size %d\n", + __func__, cal_block->cal_data.size, + client->lsm_cal_size); + rc = -EINVAL; + goto unlock; + } + /* Cache mmap address, only map once or if new addr */ + lsm_common.common_client[client->session].session = client->session; + q6lsm_add_hdr(client, msg_hdr, sizeof(params), true); + msg_hdr->opcode = set_params_opcode; + q6lsm_set_param_hdr_info(params_hdr, + cal_block->cal_data.size, + lower_32_bits(client->lsm_cal_phy_addr), + msm_audio_populate_upper_32_bits( + client->lsm_cal_phy_addr), + client->sound_model.mem_map_handle); + + pr_debug("%s: Cal Size = %zd", __func__, + cal_block->cal_data.size); + rc = q6lsm_apr_send_pkt(client, client->apr, ¶ms, true, NULL); + if (rc) + pr_err("%s: Failed set_params opcode 0x%x, rc %d\n", + __func__, msg_hdr->opcode, rc); +unlock: + mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); +done: + return rc; +} + + +int q6lsm_snd_model_buf_free(struct lsm_client *client) +{ + int rc; + + pr_debug("%s: Session id %d\n", __func__, client->session); + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + + mutex_lock(&client->cmd_lock); + rc = q6lsm_memory_unmap_regions(client, + client->sound_model.mem_map_handle); + if (rc) + pr_err("%s: CMD Memory_unmap_regions failed %d\n", + __func__, rc); + + if (client->sound_model.data) { + msm_audio_ion_free(client->sound_model.client, + client->sound_model.handle); + client->sound_model.client = NULL; + client->sound_model.handle = NULL; + client->sound_model.data = NULL; + client->sound_model.phys = 0; + client->lsm_cal_phy_addr = 0; + client->lsm_cal_size = 0; + } + mutex_unlock(&client->cmd_lock); + return rc; +} + +static struct lsm_client *q6lsm_get_lsm_client(int session_id) +{ + unsigned long flags; + struct lsm_client *client = NULL; + + spin_lock_irqsave(&lsm_session_lock, flags); + if (session_id < LSM_MIN_SESSION_ID || session_id > LSM_MAX_SESSION_ID) + pr_err("%s: Invalid session %d\n", __func__, session_id); + else if (!lsm_session[session_id]) + pr_err("%s: Not an active session %d\n", __func__, session_id); + else + client = lsm_session[session_id]; + spin_unlock_irqrestore(&lsm_session_lock, flags); + return client; +} + +/* + * q6lsm_mmapcallback : atomic context + */ +static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) +{ + unsigned long flags; + uint32_t command; + uint32_t retcode; + uint32_t sid; + const uint32_t *payload = data->payload; + struct lsm_client *client = NULL; + + if (data->opcode == RESET_EVENTS) { + sid = (data->token >> 8) & 0x0F; + pr_debug("%s: SSR event received 0x%x, event 0x%x,\n" + "proc 0x%x SID 0x%x\n", __func__, data->opcode, + data->reset_event, data->reset_proc, sid); + lsm_common.common_client[sid].lsm_cal_phy_addr = 0; + cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX, + lsm_common.cal_data); + lsm_common.set_custom_topology = 1; + return 0; + } + + command = payload[0]; + retcode = payload[1]; + sid = (data->token >> 8) & 0x0F; + pr_debug("%s: opcode 0x%x command 0x%x return code 0x%x SID 0x%x\n", + __func__, data->opcode, command, retcode, sid); + client = q6lsm_get_lsm_client(sid); + if (!client) { + pr_debug("%s: Session %d already freed\n", __func__, sid); + return 0; + } + + switch (data->opcode) { + case LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS: + if (atomic_read(&client->cmd_state) == CMD_STATE_WAIT_RESP) { + spin_lock_irqsave(&mmap_lock, flags); + *mmap_handle_p = command; + /* spin_unlock_irqrestore implies barrier */ + spin_unlock_irqrestore(&mmap_lock, flags); + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + } + break; + case APR_BASIC_RSP_RESULT: + switch (command) { + case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS: + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + break; + case LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS: + if (retcode != 0) { + /* error state, signal to stop waiting */ + if (atomic_read(&client->cmd_state) == + CMD_STATE_WAIT_RESP) { + spin_lock_irqsave(&mmap_lock, flags); + /* implies barrier */ + spin_unlock_irqrestore(&mmap_lock, + flags); + atomic_set(&client->cmd_state, + CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + } + } + break; + default: + pr_warn("%s: Unexpected command 0x%x\n", __func__, + command); + } + /* fallthrough */ + default: + pr_debug("%s: command 0x%x return code 0x%x opcode 0x%x\n", + __func__, command, retcode, data->opcode); + break; + } + if (client->cb) + client->cb(data->opcode, data->token, + data->payload, client->priv); + return 0; +} + +int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, + bool allocate_module_data) +{ + int rc = -EINVAL; + struct cal_block_data *cal_block = NULL; + + size_t pad_zero = 0, total_mem = 0; + + if (!client || len <= LSM_ALIGN_BOUNDARY) + return rc; + + mutex_lock(&client->cmd_lock); + + mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); + cal_block = cal_utils_get_only_cal_block( + lsm_common.cal_data[LSM_CAL_IDX]); + if (cal_block == NULL) + goto fail; + + pr_debug("%s:Snd Model len = %zd cal size %zd phys addr %pK", __func__, + len, cal_block->cal_data.size, + &cal_block->cal_data.paddr); + if (!cal_block->cal_data.paddr) { + pr_err("%s: No LSM calibration set for session", __func__); + rc = -EINVAL; + goto fail; + } + if (!client->sound_model.data) { + + /* + * if sound module is sent as set_param + * Then memory needs to be allocated for + * set_param payload as well. + */ + if (allocate_module_data) + len += sizeof(struct lsm_param_payload_common); + + client->sound_model.size = len; + pad_zero = (LSM_ALIGN_BOUNDARY - + (len % LSM_ALIGN_BOUNDARY)); + if ((len > SIZE_MAX - pad_zero) || + (len + pad_zero > + SIZE_MAX - cal_block->cal_data.size)) { + pr_err("%s: invalid allocation size, len = %zd, pad_zero =%zd, cal_size = %zd\n", + __func__, len, pad_zero, + cal_block->cal_data.size); + rc = -EINVAL; + goto fail; + } + + total_mem = PAGE_ALIGN(pad_zero + len + + cal_block->cal_data.size); + pr_debug("%s: Pad zeros sound model %zd Total mem %zd\n", + __func__, pad_zero, total_mem); + rc = msm_audio_ion_alloc("lsm_client", + &client->sound_model.client, + &client->sound_model.handle, + total_mem, + &client->sound_model.phys, + &len, + &client->sound_model.data); + if (rc) { + pr_err("%s: Audio ION alloc is failed, rc = %d\n", + __func__, rc); + goto fail; + } + pr_debug("%s: Length = %zd\n", __func__, len); + client->lsm_cal_phy_addr = (pad_zero + + client->sound_model.phys + + client->sound_model.size); + client->lsm_cal_size = cal_block->cal_data.size; + memcpy((client->sound_model.data + pad_zero + + client->sound_model.size), + (uint32_t *)cal_block->cal_data.kvaddr, client->lsm_cal_size); + pr_debug("%s: Copy cal start virt_addr %pK phy_addr %pK\n" + "Offset cal virtual Addr %pK\n", __func__, + client->sound_model.data, &client->sound_model.phys, + (pad_zero + client->sound_model.data + + client->sound_model.size)); + } else { + pr_err("%s: sound model busy\n", __func__); + rc = -EBUSY; + goto fail; + } + mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); + mutex_unlock(&client->cmd_lock); + + rc = q6lsm_memory_map_regions(client, client->sound_model.phys, + len, + &client->sound_model.mem_map_handle); + if (rc) { + pr_err("%s: CMD Memory_map_regions failed %d\n", __func__, rc); + goto exit; + } + + return 0; +fail: + mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); + mutex_unlock(&client->cmd_lock); +exit: + q6lsm_snd_model_buf_free(client); + return rc; +} + +static int q6lsm_cmd(struct lsm_client *client, int opcode, bool wait) +{ + struct apr_hdr hdr; + int rc; + + pr_debug("%s: enter opcode %x wait %d\n", __func__, opcode, wait); + q6lsm_add_hdr(client, &hdr, sizeof(hdr), true); + switch (opcode) { + case LSM_SESSION_CMD_START: + case LSM_SESSION_CMD_STOP: + case LSM_SESSION_CMD_CLOSE_TX: + case LSM_SESSION_CMD_EOB: + hdr.opcode = opcode; + break; + default: + pr_err("%s: Invalid opcode 0x%x\n", __func__, opcode); + return -EINVAL; + } + rc = q6lsm_apr_send_pkt(client, client->apr, &hdr, wait, NULL); + if (rc) + pr_err("%s: Failed commmand 0x%x\n", __func__, hdr.opcode); + + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} + +static int q6lsm_send_param_epd_thres( + struct lsm_client *client, + void *data, struct lsm_module_param_ids *ids) +{ + struct snd_lsm_ep_det_thres *ep_det_data; + struct lsm_cmd_set_epd_threshold epd_cmd; + struct apr_hdr *msg_hdr = &epd_cmd.msg_hdr; + struct lsm_set_params_hdr *param_hdr = + &epd_cmd.param_hdr; + struct lsm_param_epd_thres *epd_thres = + &epd_cmd.epd_thres; + int rc; + + ep_det_data = (struct snd_lsm_ep_det_thres *) data; + q6lsm_add_hdr(client, msg_hdr, + sizeof(epd_cmd), true); + msg_hdr->opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + q6lsm_set_param_hdr_info(param_hdr, + sizeof(*epd_thres), 0, 0, 0); + q6lsm_set_param_common(&epd_thres->common, ids, + sizeof(*epd_thres) - sizeof(epd_thres->common), + LSM_SESSION_CMD_SET_PARAMS_V2); + epd_thres->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + epd_thres->epd_begin = ep_det_data->epd_begin; + epd_thres->epd_end = ep_det_data->epd_end; + + rc = q6lsm_apr_send_pkt(client, client->apr, + &epd_cmd, true, NULL); + if (unlikely(rc)) + pr_err("%s: EPD_THRESHOLD failed, rc %d\n", + __func__, rc); + return rc; +} + +static int q6lsm_send_param_gain( + struct lsm_client *client, + u16 gain, struct lsm_module_param_ids *ids) +{ + struct lsm_cmd_set_gain lsm_cmd_gain; + struct apr_hdr *msg_hdr = &lsm_cmd_gain.msg_hdr; + struct lsm_param_gain *lsm_gain = &lsm_cmd_gain.lsm_gain; + int rc; + + q6lsm_add_hdr(client, msg_hdr, + sizeof(lsm_cmd_gain), true); + msg_hdr->opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + q6lsm_set_param_hdr_info(&lsm_cmd_gain.param_hdr, + sizeof(*lsm_gain), 0, 0, 0); + q6lsm_set_param_common(&lsm_gain->common, ids, + sizeof(*lsm_gain) - sizeof(lsm_gain->common), + LSM_SESSION_CMD_SET_PARAMS_V2); + lsm_gain->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + lsm_gain->gain = gain; + lsm_gain->reserved = 0; + + rc = q6lsm_apr_send_pkt(client, client->apr, + &lsm_cmd_gain, true, NULL); + if (unlikely(rc)) + pr_err("%s: LSM_GAIN CMD send failed, rc %d\n", + __func__, rc); + return rc; +} + +int q6lsm_set_one_param(struct lsm_client *client, + struct lsm_params_info *p_info, void *data, + enum LSM_PARAM_TYPE param_type) +{ + int rc = 0, pkt_sz; + struct lsm_module_param_ids ids; + u8 *packet; + + memset(&ids, 0, sizeof(ids)); + switch (param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: { + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + rc = q6lsm_send_param_epd_thres(client, data, + &ids); + break; + } + + case LSM_OPERATION_MODE: { + struct snd_lsm_detect_mode *det_mode = data; + struct lsm_module_param_ids opmode_ids; + struct lsm_module_param_ids connectport_ids; + + if (det_mode->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) { + client->mode = 0x01; + } else if (det_mode->mode == LSM_MODE_USER_KEYWORD_DETECTION) { + client->mode = 0x03; + } else { + pr_err("%s: Incorrect detection mode %d\n", + __func__, det_mode->mode); + return -EINVAL; + } + + client->mode |= det_mode->detect_failure << 2; + client->connect_to_port = get_lsm_port(); + + opmode_ids.module_id = p_info->module_id; + opmode_ids.param_id = p_info->param_id; + + connectport_ids.module_id = LSM_MODULE_ID_FRAMEWORK; + connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + rc = q6lsm_send_params(client, &opmode_ids, &connectport_ids, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: OPERATION_MODE failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_GAIN: { + struct snd_lsm_gain *lsm_gain = (struct snd_lsm_gain *) data; + + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + rc = q6lsm_send_param_gain(client, lsm_gain->gain, &ids); + if (rc) + pr_err("%s: LSM_GAIN command failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_MIN_CONFIDENCE_LEVELS: + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + rc = q6lsm_send_confidence_levels(client, &ids, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: CONFIDENCE_LEVELS cmd failed, rc %d\n", + __func__, rc); + break; + case LSM_REG_SND_MODEL: { + struct lsm_cmd_set_params model_param; + u32 payload_size; + + memset(&model_param, 0, sizeof(model_param)); + q6lsm_add_hdr(client, &model_param.msg_hdr, + sizeof(model_param), true); + model_param.msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + payload_size = p_info->param_size + + sizeof(struct lsm_param_payload_common); + q6lsm_set_param_hdr_info(&model_param.param_hdr, + payload_size, + lower_32_bits(client->sound_model.phys), + msm_audio_populate_upper_32_bits( + client->sound_model.phys), + client->sound_model.mem_map_handle); + + rc = q6lsm_apr_send_pkt(client, client->apr, + &model_param, true, NULL); + if (rc) { + pr_err("%s: REG_SND_MODEL failed, rc %d\n", + __func__, rc); + return rc; + } + + rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS); + if (rc) + pr_err("%s: Failed to send lsm cal, err = %d\n", + __func__, rc); + break; + } + + case LSM_DEREG_SND_MODEL: { + struct lsm_param_payload_common *common; + struct lsm_cmd_set_params *param; + + pkt_sz = sizeof(*param) + sizeof(*common); + packet = kzalloc(pkt_sz, GFP_KERNEL); + if (!packet) { + pr_err("%s: No memory for DEREG_SND_MODEL pkt, size = %d\n", + __func__, pkt_sz); + return -ENOMEM; + } + + param = (struct lsm_cmd_set_params *) packet; + common = (struct lsm_param_payload_common *) + (packet + sizeof(*param)); + q6lsm_add_hdr(client, ¶m->msg_hdr, pkt_sz, true); + param->msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + q6lsm_set_param_hdr_info(¶m->param_hdr, + sizeof(*common), + 0, 0, 0); + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + q6lsm_set_param_common(common, &ids, 0, + LSM_SESSION_CMD_SET_PARAMS_V2); + rc = q6lsm_apr_send_pkt(client, client->apr, + packet, true, NULL); + if (rc) + pr_err("%s: DEREG_SND_MODEL failed, rc %d\n", + __func__, rc); + kfree(packet); + break; + } + + case LSM_CUSTOM_PARAMS: { + struct apr_hdr *hdr; + u8 *custom_data; + + if (p_info->param_size < + sizeof(struct lsm_param_payload_common)) { + pr_err("%s: Invalid param_size %d\n", + __func__, p_info->param_size); + return -EINVAL; + } + + pkt_sz = p_info->param_size + sizeof(*hdr); + packet = kzalloc(pkt_sz, GFP_KERNEL); + if (!packet) { + pr_err("%s: no memory for CUSTOM_PARAMS, size = %d\n", + __func__, pkt_sz); + return -ENOMEM; + } + + hdr = (struct apr_hdr *) packet; + custom_data = (u8 *) (packet + sizeof(*hdr)); + q6lsm_add_hdr(client, hdr, pkt_sz, true); + hdr->opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + memcpy(custom_data, data, p_info->param_size); + + rc = q6lsm_apr_send_pkt(client, client->apr, + packet, true, NULL); + if (rc) + pr_err("%s: CUSTOM_PARAMS failed, rc %d\n", + __func__, rc); + kfree(packet); + break; + } + default: + pr_err("%s: wrong param_type 0x%x\n", + __func__, p_info->param_type); + } + + return rc; +} + + +int q6lsm_start(struct lsm_client *client, bool wait) +{ + return q6lsm_cmd(client, LSM_SESSION_CMD_START, wait); +} + +int q6lsm_stop(struct lsm_client *client, bool wait) +{ + return q6lsm_cmd(client, LSM_SESSION_CMD_STOP, wait); +} + +int q6lsm_close(struct lsm_client *client) +{ + return q6lsm_cmd(client, LSM_SESSION_CMD_CLOSE_TX, true); +} + +int q6lsm_lab_control(struct lsm_client *client, u32 enable) +{ + int rc = 0; + struct lsm_params_lab_enable lab_enable; + struct lsm_params_lab_config lab_config; + struct lsm_module_param_ids lab_ids; + u32 param_size; + + if (!client) { + pr_err("%s: invalid param client %pK\n", __func__, client); + return -EINVAL; + } + /* enable/disable lab on dsp */ + q6lsm_add_hdr(client, &lab_enable.msg_hdr, sizeof(lab_enable), true); + lab_enable.msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS; + q6lsm_set_param_hdr_info(&lab_enable.params_hdr, + sizeof(struct lsm_lab_enable), + 0, 0, 0); + param_size = (sizeof(struct lsm_lab_enable) - + sizeof(struct lsm_param_payload_common)); + lab_ids.module_id = LSM_MODULE_ID_LAB; + lab_ids.param_id = LSM_PARAM_ID_LAB_ENABLE; + q6lsm_set_param_common(&lab_enable.lab_enable.common, + &lab_ids, param_size, + LSM_SESSION_CMD_SET_PARAMS); + lab_enable.lab_enable.enable = (enable) ? 1 : 0; + rc = q6lsm_apr_send_pkt(client, client->apr, &lab_enable, true, NULL); + if (rc) { + pr_err("%s: Lab enable failed rc %d\n", __func__, rc); + return rc; + } + if (!enable) + goto exit; + /* lab session is being enabled set the config values */ + q6lsm_add_hdr(client, &lab_config.msg_hdr, sizeof(lab_config), true); + lab_config.msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS; + q6lsm_set_param_hdr_info(&lab_config.params_hdr, + sizeof(struct lsm_lab_config), + 0, 0, 0); + lab_ids.module_id = LSM_MODULE_ID_LAB; + lab_ids.param_id = LSM_PARAM_ID_LAB_CONFIG; + param_size = (sizeof(struct lsm_lab_config) - + sizeof(struct lsm_param_payload_common)); + q6lsm_set_param_common(&lab_config.lab_config.common, + &lab_ids, param_size, + LSM_SESSION_CMD_SET_PARAMS); + lab_config.lab_config.minor_version = 1; + lab_config.lab_config.wake_up_latency_ms = 250; + rc = q6lsm_apr_send_pkt(client, client->apr, &lab_config, true, NULL); + if (rc) { + pr_err("%s: Lab config failed rc %d disable lab\n", + __func__, rc); + /* Lab config failed disable lab */ + lab_enable.lab_enable.enable = 0; + if (q6lsm_apr_send_pkt(client, client->apr, + &lab_enable, true, NULL)) + pr_err("%s: Lab disable failed\n", __func__); + } +exit: + return rc; +} + +int q6lsm_stop_lab(struct lsm_client *client) +{ + int rc = 0; + + if (!client) { + pr_err("%s: invalid param client %pK\n", __func__, client); + return -EINVAL; + } + rc = q6lsm_cmd(client, LSM_SESSION_CMD_EOB, true); + if (rc) + pr_err("%s: Lab stop failed %d\n", __func__, rc); + return rc; +} + +int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read) +{ + int rc = 0; + + if (!client || !read) { + pr_err("%s: Invalid params client %pK read %pK\n", __func__, + client, read); + return -EINVAL; + } + pr_debug("%s: read call memmap handle %x address %x%x size %d\n", + __func__, read->mem_map_handle, read->buf_addr_msw, + read->buf_addr_lsw, read->buf_size); + q6lsm_add_hdr(client, &read->hdr, sizeof(struct lsm_cmd_read), true); + read->hdr.opcode = LSM_SESSION_CMD_READ; + rc = q6lsm_apr_send_pkt(client, client->apr, read, false, NULL); + if (rc) + pr_err("%s: read buffer call failed rc %d\n", __func__, rc); + return rc; +} + +int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc) +{ + int ret = 0, i = 0; + size_t allocate_size = 0, len = 0; + + if (!client) { + pr_err("%s: invalid client\n", __func__); + return -EINVAL; + } + if (alloc) { + if (client->lab_buffer) { + pr_err("%s: buffers are allocated period count %d period size %d\n", + __func__, + client->hw_params.period_count, + client->hw_params.buf_sz); + return -EINVAL; + } + allocate_size = client->hw_params.period_count * + client->hw_params.buf_sz; + allocate_size = PAGE_ALIGN(allocate_size); + client->lab_buffer = + kzalloc(sizeof(struct lsm_lab_buffer) * + client->hw_params.period_count, GFP_KERNEL); + if (!client->lab_buffer) { + pr_err("%s: memory allocation for lab buffer failed count %d\n" + , __func__, + client->hw_params.period_count); + return -ENOMEM; + } + ret = msm_audio_ion_alloc("lsm_lab", + &client->lab_buffer[0].client, + &client->lab_buffer[0].handle, + allocate_size, &client->lab_buffer[0].phys, + &len, + &client->lab_buffer[0].data); + if (ret) + pr_err("%s: ion alloc failed ret %d size %zd\n", + __func__, ret, allocate_size); + else { + ret = q6lsm_memory_map_regions(client, + client->lab_buffer[0].phys, len, + &client->lab_buffer[0].mem_map_handle); + if (ret) { + pr_err("%s: memory map filed ret %d size %zd\n", + __func__, ret, len); + msm_audio_ion_free( + client->lab_buffer[0].client, + client->lab_buffer[0].handle); + } + } + if (ret) { + pr_err("%s: alloc lab buffer failed ret %d\n", + __func__, ret); + kfree(client->lab_buffer); + client->lab_buffer = NULL; + } else { + pr_debug("%s: Memory map handle %x phys %pK size %d\n", + __func__, + client->lab_buffer[0].mem_map_handle, + &client->lab_buffer[0].phys, + client->hw_params.buf_sz); + for (i = 0; i < client->hw_params.period_count; i++) { + client->lab_buffer[i].phys = + client->lab_buffer[0].phys + + (i * client->hw_params.buf_sz); + client->lab_buffer[i].size = + client->hw_params.buf_sz; + client->lab_buffer[i].data = + (u8 *)(client->lab_buffer[0].data) + + (i * client->hw_params.buf_sz); + client->lab_buffer[i].mem_map_handle = + client->lab_buffer[0].mem_map_handle; + } + } + } else { + ret = q6lsm_memory_unmap_regions(client, + client->lab_buffer[0].mem_map_handle); + if (!ret) + msm_audio_ion_free( + client->lab_buffer[0].client, + client->lab_buffer[0].handle); + else + pr_err("%s: unmap failed not freeing memory\n", + __func__); + kfree(client->lab_buffer); + client->lab_buffer = NULL; + } + return ret; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case LSM_CUST_TOPOLOGY_CAL_TYPE: + ret = LSM_CUSTOM_TOP_IDX; + break; + case LSM_TOPOLOGY_CAL_TYPE: + ret = LSM_TOP_IDX; + break; + case LSM_CAL_TYPE: + ret = LSM_CAL_IDX; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int q6lsm_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + lsm_common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6lsm_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + lsm_common.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6lsm_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + lsm_common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == LSM_CUSTOM_TOP_IDX) { + mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + lsm_common.set_custom_topology = 1; + mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + } + +done: + return ret; +} + +static void lsm_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(LSM_MAX_CAL_IDX, lsm_common.cal_data); +} + +static int q6lsm_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{LSM_CUST_TOPOLOGY_CAL_TYPE, + {q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL, + q6lsm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{LSM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + q6lsm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{LSM_CAL_TYPE, + {q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL, + q6lsm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} } + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(LSM_MAX_CAL_IDX, + lsm_common.cal_data, cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type!\n", + __func__); + ret = -EINVAL; + goto err; + } + + return ret; +err: + lsm_delete_cal_data(); + return ret; +} + +static int __init q6lsm_init(void) +{ + int i = 0; + + pr_debug("%s:\n", __func__); + spin_lock_init(&lsm_session_lock); + spin_lock_init(&mmap_lock); + mutex_init(&lsm_common.apr_lock); + for (; i <= LSM_MAX_SESSION_ID; i++) { + lsm_common.common_client[i].session = LSM_CONTROL_SESSION; + init_waitqueue_head(&lsm_common.common_client[i].cmd_wait); + mutex_init(&lsm_common.common_client[i].cmd_lock); + atomic_set(&lsm_common.common_client[i].cmd_state, + CMD_STATE_CLEARED); + } + + if (q6lsm_init_cal_data()) + pr_err("%s: could not init cal data!\n", __func__); + + return 0; +} + +static void __exit q6lsm_exit(void) +{ + lsm_delete_cal_data(); +} + +device_initcall(q6lsm_init); +__exitcall(q6lsm_exit); diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c new file mode 100644 index 000000000000..54d72bede7ed --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6voice.c @@ -0,0 +1,8425 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sound/q6audio-v2.h" +#include "sound/apr_audio-v2.h" +#include "sound/q6afe-v2.h" +#include +#include "q6voice.h" +#include + +#define TIMEOUT_MS 300 + + +#define CMD_STATUS_SUCCESS 0 +#define CMD_STATUS_FAIL 1 + +enum { + VOC_TOKEN_NONE, + VOIP_MEM_MAP_TOKEN, + VOC_CAL_MEM_MAP_TOKEN, + VOC_VOICE_HOST_PCM_MAP_TOKEN, + VOC_RTAC_MEM_MAP_TOKEN, + VOC_SOURCE_TRACKING_MEM_MAP_TOKEN +}; + +struct cvd_version_table cvd_version_table_mapping[CVD_INT_VERSION_MAX] = { + {CVD_VERSION_DEFAULT, CVD_INT_VERSION_DEFAULT}, + {CVD_VERSION_0_0, CVD_INT_VERSION_0_0}, + {CVD_VERSION_2_1, CVD_INT_VERSION_2_1}, + {CVD_VERSION_2_2, CVD_INT_VERSION_2_2}, + {CVD_VERSION_2_3, CVD_INT_VERSION_2_3}, +}; + +static struct common_data common; +static bool module_initialized; + +static int voice_send_enable_vocproc_cmd(struct voice_data *v); +static int voice_send_netid_timing_cmd(struct voice_data *v); +static int voice_send_attach_vocproc_cmd(struct voice_data *v); +static int voice_send_set_device_cmd(struct voice_data *v); +static int voice_send_vol_step_cmd(struct voice_data *v); +static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v, + uint32_t mem_handle); +static int voice_send_mvm_cal_network_cmd(struct voice_data *v); +static int voice_send_mvm_media_type_cmd(struct voice_data *v); +static int voice_send_mvm_cvd_version_cmd(struct voice_data *v); +static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v); +static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v); +static int voice_set_packet_exchange_mode_and_config(uint32_t session_id, + uint32_t mode); + +static int voice_send_cvs_register_cal_cmd(struct voice_data *v); +static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v); +static int voice_send_cvp_create_cmd(struct voice_data *v); +static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v); +static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v); +static int voice_send_cvp_register_cal_cmd(struct voice_data *v); +static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v); +static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v); +static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v); +static int voice_send_cvp_device_channels_cmd(struct voice_data *v); +static int voice_send_cvp_topology_commit_cmd(struct voice_data *v); + +static int voice_cvs_stop_playback(struct voice_data *v); +static int voice_cvs_start_playback(struct voice_data *v); +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode); +static int voice_cvs_stop_record(struct voice_data *v); + +static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv); +static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv); +static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv); + +static int voice_send_set_pp_enable_cmd(struct voice_data *v, + uint32_t module_id, int enable); +static int is_cal_memory_allocated(void); +static bool is_cvd_version_queried(void); +static int is_voip_memory_allocated(void); +static int voice_get_cvd_int_version(char *cvd_ver_string); +static int voice_alloc_cal_mem_map_table(void); +static int voice_alloc_rtac_mem_map_table(void); +static int voice_alloc_oob_shared_mem(void); +static int voice_free_oob_shared_mem(void); +static int voice_alloc_oob_mem_table(void); +static int voice_alloc_and_map_oob_mem(struct voice_data *v); +static void voice_vote_powerstate_to_bms(struct voice_data *v, bool state); + +static struct voice_data *voice_get_session_by_idx(int idx); + +static int remap_cal_data(struct cal_block_data *cal_block, + uint32_t session_id); +static int voice_unmap_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block); + +static int is_source_tracking_shared_memomry_allocated(void); +static int voice_alloc_source_tracking_shared_memory(void); +static int voice_alloc_and_map_source_tracking_shared_memory( + struct voice_data *v); +static int voice_unmap_and_free_source_tracking_shared_memory( + struct voice_data *v); +static int voice_send_set_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param soundFocusData); +static int voice_send_get_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param *soundFocusData); +static int voice_send_get_source_tracking_cmd(struct voice_data *v, + struct source_tracking_param *sourceTrackingData); + +static void voice_itr_init(struct voice_session_itr *itr, + u32 session_id) +{ + if (itr == NULL) + return; + itr->session_idx = voice_get_idx_for_session(session_id); + if (session_id == ALL_SESSION_VSID) + itr->cur_idx = 0; + else + itr->cur_idx = itr->session_idx; + +} + +static bool voice_itr_get_next_session(struct voice_session_itr *itr, + struct voice_data **voice) +{ + bool ret = false; + + if (itr == NULL) + return false; + pr_debug("%s : cur idx = %d session idx = %d\n", + __func__, itr->cur_idx, itr->session_idx); + + if (itr->cur_idx <= itr->session_idx) { + ret = true; + *voice = voice_get_session_by_idx(itr->cur_idx); + itr->cur_idx++; + } else { + *voice = NULL; + } + + return ret; +} + +static bool voice_is_valid_session_id(uint32_t session_id) +{ + bool ret = false; + + switch (session_id) { + case VOICE_SESSION_VSID: + case VOICE2_SESSION_VSID: + case VOLTE_SESSION_VSID: + case VOIP_SESSION_VSID: + case QCHAT_SESSION_VSID: + case VOWLAN_SESSION_VSID: + case VOICEMMODE1_VSID: + case VOICEMMODE2_VSID: + case ALL_SESSION_VSID: + ret = true; + break; + default: + pr_err("%s: Invalid session_id : %x\n", __func__, session_id); + + break; + } + + return ret; +} + +static u16 voice_get_mvm_handle(struct voice_data *v) +{ + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return 0; + } + + pr_debug("%s: mvm_handle %d\n", __func__, v->mvm_handle); + + return v->mvm_handle; +} + +static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle) +{ + pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + v->mvm_handle = mvm_handle; +} + +static u16 voice_get_cvs_handle(struct voice_data *v) +{ + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return 0; + } + + pr_debug("%s: cvs_handle %d\n", __func__, v->cvs_handle); + + return v->cvs_handle; +} + +static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle) +{ + pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + v->cvs_handle = cvs_handle; +} + +static u16 voice_get_cvp_handle(struct voice_data *v) +{ + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return 0; + } + + pr_debug("%s: cvp_handle %d\n", __func__, v->cvp_handle); + + return v->cvp_handle; +} + +static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle) +{ + pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + v->cvp_handle = cvp_handle; +} + +char *voc_get_session_name(u32 session_id) +{ + char *session_name = NULL; + + if (session_id == common.voice[VOC_PATH_PASSIVE].session_id) { + session_name = VOICE_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOLTE_PASSIVE].session_id) { + session_name = VOLTE_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_QCHAT_PASSIVE].session_id) { + session_name = QCHAT_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id) { + session_name = VOWLAN_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id) { + session_name = VOICEMMODE1_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id) { + session_name = VOICEMMODE2_NAME; + } else if (session_id == common.voice[VOC_PATH_FULL].session_id) { + session_name = VOIP_SESSION_NAME; + } + return session_name; +} + +uint32_t voc_get_session_id(char *name) +{ + u32 session_id = 0; + + if (name != NULL) { + if (!strcmp(name, "Voice session")) + session_id = common.voice[VOC_PATH_PASSIVE].session_id; + else if (!strcmp(name, "Voice2 session")) + session_id = + common.voice[VOC_PATH_VOICE2_PASSIVE].session_id; + else if (!strcmp(name, "VoLTE session")) + session_id = + common.voice[VOC_PATH_VOLTE_PASSIVE].session_id; + else if (!strcmp(name, "QCHAT session")) + session_id = + common.voice[VOC_PATH_QCHAT_PASSIVE].session_id; + else if (!strcmp(name, "VoWLAN session")) + session_id = + common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id; + else if (!strcmp(name, "VoiceMMode1")) + session_id = + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id; + else if (!strcmp(name, "VoiceMMode2")) + session_id = + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id; + else + session_id = common.voice[VOC_PATH_FULL].session_id; + + pr_debug("%s: %s has session id 0x%x\n", __func__, name, + session_id); + } + + return session_id; +} + +static struct voice_data *voice_get_session(u32 session_id) +{ + struct voice_data *v = NULL; + + switch (session_id) { + case VOICE_SESSION_VSID: + v = &common.voice[VOC_PATH_PASSIVE]; + break; + + case VOICE2_SESSION_VSID: + v = &common.voice[VOC_PATH_VOICE2_PASSIVE]; + break; + + case VOLTE_SESSION_VSID: + v = &common.voice[VOC_PATH_VOLTE_PASSIVE]; + break; + + case VOIP_SESSION_VSID: + v = &common.voice[VOC_PATH_FULL]; + break; + + case QCHAT_SESSION_VSID: + v = &common.voice[VOC_PATH_QCHAT_PASSIVE]; + break; + + case VOWLAN_SESSION_VSID: + v = &common.voice[VOC_PATH_VOWLAN_PASSIVE]; + break; + + case VOICEMMODE1_VSID: + v = &common.voice[VOC_PATH_VOICEMMODE1_PASSIVE]; + break; + + case VOICEMMODE2_VSID: + v = &common.voice[VOC_PATH_VOICEMMODE2_PASSIVE]; + break; + + case ALL_SESSION_VSID: + break; + + default: + pr_err("%s: Invalid session_id : %x\n", __func__, session_id); + + break; + } + + pr_debug("%s:session_id 0x%x session handle %pK\n", + __func__, session_id, v); + + return v; +} + +int voice_get_idx_for_session(u32 session_id) +{ + int idx = 0; + + switch (session_id) { + case VOICE_SESSION_VSID: + idx = VOC_PATH_PASSIVE; + break; + + case VOICE2_SESSION_VSID: + idx = VOC_PATH_VOICE2_PASSIVE; + break; + + case VOLTE_SESSION_VSID: + idx = VOC_PATH_VOLTE_PASSIVE; + break; + + case VOIP_SESSION_VSID: + idx = VOC_PATH_FULL; + break; + + case QCHAT_SESSION_VSID: + idx = VOC_PATH_QCHAT_PASSIVE; + break; + + case VOWLAN_SESSION_VSID: + idx = VOC_PATH_VOWLAN_PASSIVE; + break; + + case VOICEMMODE1_VSID: + idx = VOC_PATH_VOICEMMODE1_PASSIVE; + break; + + case VOICEMMODE2_VSID: + idx = VOC_PATH_VOICEMMODE2_PASSIVE; + break; + + case ALL_SESSION_VSID: + idx = MAX_VOC_SESSIONS - 1; + break; + + default: + pr_err("%s: Invalid session_id : %x\n", __func__, session_id); + + break; + } + + return idx; +} + +static struct voice_data *voice_get_session_by_idx(int idx) +{ + return ((idx < 0 || idx >= MAX_VOC_SESSIONS) ? + NULL : &common.voice[idx]); +} + +static bool is_voip_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_FULL].session_id); +} + +static bool is_volte_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id); +} + +static bool is_voice2_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_VOICE2_PASSIVE].session_id); +} + +static bool is_qchat_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_QCHAT_PASSIVE].session_id); +} + +static bool is_vowlan_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id); +} + +static bool is_voicemmode1(u32 session_id) +{ + return session_id == + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id; +} + +static bool is_voicemmode2(u32 session_id) +{ + return session_id == + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id; +} + +static bool is_voc_state_active(int voc_state) +{ + if ((voc_state == VOC_RUN) || + (voc_state == VOC_CHANGE) || + (voc_state == VOC_STANDBY)) + return true; + + return false; +} + +static void voc_set_error_state(uint16_t reset_proc) +{ + struct voice_data *v = NULL; + int i; + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + if (v != NULL) + v->voc_state = VOC_ERROR; + } +} + +static bool is_other_session_active(u32 session_id) +{ + int i; + bool ret = false; + + /* Check if there is other active session except the input one */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + if (common.voice[i].session_id == session_id) + continue; + + if (is_voc_state_active(common.voice[i].voc_state)) { + ret = true; + break; + } + } + pr_debug("%s: ret %d\n", __func__, ret); + + return ret; +} + +static bool is_sub1_vsid(u32 session_id) +{ + bool ret; + + switch (session_id) { + case VOICE_SESSION_VSID: + case VOLTE_SESSION_VSID: + case VOWLAN_SESSION_VSID: + case VOICEMMODE1_VSID: + ret = true; + break; + default: + ret = false; + } + + return ret; +} + +static bool is_sub2_vsid(u32 session_id) +{ + bool ret; + + switch (session_id) { + case VOICE2_SESSION_VSID: + case VOICEMMODE2_VSID: + ret = true; + break; + default: + ret = false; + } + + return ret; +} + +static bool is_voice_app_id(u32 session_id) +{ + return is_sub1_vsid(session_id) || is_sub2_vsid(session_id); +} + +static void init_session_id(void) +{ + common.voice[VOC_PATH_PASSIVE].session_id = VOICE_SESSION_VSID; + common.voice[VOC_PATH_VOLTE_PASSIVE].session_id = VOLTE_SESSION_VSID; + common.voice[VOC_PATH_VOICE2_PASSIVE].session_id = VOICE2_SESSION_VSID; + common.voice[VOC_PATH_FULL].session_id = VOIP_SESSION_VSID; + common.voice[VOC_PATH_QCHAT_PASSIVE].session_id = QCHAT_SESSION_VSID; + common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id = VOWLAN_SESSION_VSID; + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id = + VOICEMMODE1_VSID; + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id = + VOICEMMODE2_VSID; +} + +static bool is_cvd_version_queried(void) +{ + bool ret = 0; + + if (!strcmp(common.cvd_version, CVD_VERSION_DEFAULT)) + ret = false; + else + ret = true; + + return ret; +} + +static int voice_get_cvd_int_version(char *cvd_ver_string) +{ + unsigned int idx; + int cvd_int_ver = CVD_INT_VERSION_DEFAULT; + + for (idx = 0; idx < CVD_INT_VERSION_MAX; idx++) { + if (strcmp((char *)cvd_ver_string, + cvd_version_table_mapping[idx].cvd_ver) == 0) { + cvd_int_ver = + cvd_version_table_mapping[idx].cvd_ver_int; + break; + } + } + return cvd_int_ver; +} + +static int voice_apr_register(uint32_t session_id) +{ + + pr_debug("%s\n", __func__); + + mutex_lock(&common.common_lock); + + /* register callback to APR */ + if (common.apr_q6_mvm == NULL) { + pr_debug("%s: Start to register MVM callback\n", __func__); + + common.apr_q6_mvm = apr_register("ADSP", "MVM", + qdsp_mvm_callback, + 0xFFFFFFFF, &common); + + if (common.apr_q6_mvm == NULL) { + pr_err("%s: Unable to register MVM\n", __func__); + goto err; + } + } + + if (common.apr_q6_cvs == NULL) { + pr_debug("%s: Start to register CVS callback\n", __func__); + + common.apr_q6_cvs = apr_register("ADSP", "CVS", + qdsp_cvs_callback, + 0xFFFFFFFF, &common); + + if (common.apr_q6_cvs == NULL) { + pr_err("%s: Unable to register CVS\n", __func__); + goto err; + } + rtac_set_voice_handle(RTAC_CVS, common.apr_q6_cvs); + } + + if (common.apr_q6_cvp == NULL) { + pr_debug("%s: Start to register CVP callback\n", __func__); + + common.apr_q6_cvp = apr_register("ADSP", "CVP", + qdsp_cvp_callback, + 0xFFFFFFFF, &common); + + if (common.apr_q6_cvp == NULL) { + pr_err("%s: Unable to register CVP\n", __func__); + goto err; + } + rtac_set_voice_handle(RTAC_CVP, common.apr_q6_cvp); + } + + mutex_unlock(&common.common_lock); + + return 0; + +err: + if (common.apr_q6_cvs != NULL) { + apr_deregister(common.apr_q6_cvs); + common.apr_q6_cvs = NULL; + rtac_set_voice_handle(RTAC_CVS, NULL); + } + if (common.apr_q6_mvm != NULL) { + apr_deregister(common.apr_q6_mvm); + common.apr_q6_mvm = NULL; + } + + mutex_unlock(&common.common_lock); + + return -ENODEV; +} + +static int voice_send_mvm_cvd_version_cmd(struct voice_data *v) +{ + int ret; + struct apr_hdr cvd_version_get_cmd; + void *apr_mvm; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + /* Send command to CVD to retrieve Version */ + cvd_version_get_cmd.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvd_version_get_cmd.pkt_size = APR_PKT_SIZE( + APR_HDR_SIZE, + sizeof(cvd_version_get_cmd) - + APR_HDR_SIZE); + cvd_version_get_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvd_version_get_cmd.dest_port = 0; + cvd_version_get_cmd.token = 0; + cvd_version_get_cmd.opcode = VSS_IVERSION_CMD_GET; + + pr_debug("%s: send CVD version get cmd, pkt size = %d\n", + __func__, cvd_version_get_cmd.pkt_size); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &cvd_version_get_cmd); + if (ret < 0) { + pr_err("%s: Error sending command\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout, fall back to default\n", + __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + ret = 0; + +done: + if (ret) { + strlcpy(common.cvd_version, CVD_VERSION_0_0, + sizeof(common.cvd_version)); + } + pr_debug("%s: CVD Version retrieved=%s\n", + __func__, common.cvd_version); + + return ret; +} + +static int voice_send_dual_control_cmd(struct voice_data *v) +{ + int ret = 0; + struct mvm_modem_dual_control_session_cmd mvm_voice_ctl_cmd; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + pr_debug("%s: Send Dual Control command to MVM\n", __func__); + if (!is_voip_session(v->session_id)) { + mvm_handle = voice_get_mvm_handle(v); + mvm_voice_ctl_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_voice_ctl_cmd.hdr.pkt_size = APR_PKT_SIZE( + APR_HDR_SIZE, + sizeof(mvm_voice_ctl_cmd) - + APR_HDR_SIZE); + pr_debug("%s: send mvm Voice Ctl pkt size = %d\n", + __func__, mvm_voice_ctl_cmd.hdr.pkt_size); + mvm_voice_ctl_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_voice_ctl_cmd.hdr.dest_port = mvm_handle; + mvm_voice_ctl_cmd.hdr.token = 0; + mvm_voice_ctl_cmd.hdr.opcode = + VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL; + mvm_voice_ctl_cmd.voice_ctl.enable_flag = true; + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_voice_ctl_cmd); + if (ret < 0) { + pr_err("%s: Error sending MVM Voice CTL CMD\n", + __func__); + ret = -EINVAL; + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } + ret = 0; +fail: + return ret; +} + + +static int voice_create_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_create_ctl_session_cmd mvm_session_cmd; + struct cvs_create_passive_ctl_session_cmd cvs_session_cmd; + struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd; + struct mvm_attach_stream_cmd attach_stream_cmd; + void *apr_mvm, *apr_cvs, *apr_cvp; + u16 mvm_handle, cvs_handle, cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + apr_cvs = common.apr_q6_cvs; + apr_cvp = common.apr_q6_cvp; + + if (!apr_mvm || !apr_cvs || !apr_cvp) { + pr_err("%s: apr_mvm or apr_cvs or apr_cvp is NULL\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvs_handle = voice_get_cvs_handle(v); + cvp_handle = voice_get_cvp_handle(v); + + pr_debug("%s: mvm_hdl=%d, cvs_hdl=%d\n", __func__, + mvm_handle, cvs_handle); + /* send cmd to create mvm session and wait for response */ + + if (!mvm_handle) { + memset(mvm_session_cmd.mvm_session.name, 0, + sizeof(mvm_session_cmd.mvm_session.name)); + if (!is_voip_session(v->session_id)) { + mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = APR_PKT_SIZE( + APR_HDR_SIZE, + sizeof(mvm_session_cmd) - + APR_HDR_SIZE); + pr_debug("%s: send mvm create session pkt size = %d\n", + __func__, mvm_session_cmd.hdr.pkt_size); + mvm_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + if (is_volte_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + "default volte voice", + strlen("default volte voice")+1); + } else if (is_voice2_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOICE2_SESSION_VSID_STR, + strlen(VOICE2_SESSION_VSID_STR)+1); + } else if (is_qchat_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + QCHAT_SESSION_VSID_STR, + strlen(QCHAT_SESSION_VSID_STR)+1); + } else if (is_vowlan_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOWLAN_SESSION_VSID_STR, + strlen(VOWLAN_SESSION_VSID_STR)+1); + } else if (is_voicemmode1(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOICEMMODE1_VSID_STR, + strlen(VOICEMMODE1_VSID_STR) + 1); + } else if (is_voicemmode2(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOICEMMODE2_VSID_STR, + strlen(VOICEMMODE2_VSID_STR) + 1); + } else { + strlcpy(mvm_session_cmd.mvm_session.name, + "default modem voice", + strlen("default modem voice")+1); + } + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_err("%s: Error sending MVM_CONTROL_SESSION\n", + __func__); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } else { + pr_debug("%s: creating MVM full ctrl\n", __func__); + mvm_session_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_session_cmd) - + APR_HDR_SIZE); + mvm_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION; + strlcpy(mvm_session_cmd.mvm_session.name, + "default voip", + strlen("default voip")+1); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_err("Fail in sending MVM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } + /* Get the created MVM handle. */ + mvm_handle = voice_get_mvm_handle(v); + } + /* send cmd to create cvs session */ + if (!cvs_handle) { + memset(cvs_session_cmd.cvs_session.name, 0, + sizeof(cvs_session_cmd.cvs_session.name)); + if (!is_voip_session(v->session_id)) { + pr_debug("%s: creating CVS passive session\n", + __func__); + + cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_session_cmd) - + APR_HDR_SIZE); + cvs_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_session_cmd.hdr.dest_port = 0; + cvs_session_cmd.hdr.token = 0; + cvs_session_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + if (is_volte_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + "default volte voice", + strlen("default volte voice")+1); + } else if (is_voice2_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOICE2_SESSION_VSID_STR, + strlen(VOICE2_SESSION_VSID_STR)+1); + } else if (is_qchat_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + QCHAT_SESSION_VSID_STR, + strlen(QCHAT_SESSION_VSID_STR)+1); + } else if (is_vowlan_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOWLAN_SESSION_VSID_STR, + strlen(VOWLAN_SESSION_VSID_STR)+1); + } else if (is_voicemmode1(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOICEMMODE1_VSID_STR, + strlen(VOICEMMODE1_VSID_STR) + 1); + } else if (is_voicemmode2(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOICEMMODE2_VSID_STR, + strlen(VOICEMMODE2_VSID_STR) + 1); + } else { + strlcpy(cvs_session_cmd.cvs_session.name, + "default modem voice", + strlen("default modem voice")+1); + } + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &cvs_session_cmd); + if (ret < 0) { + pr_err("Fail in sending STREAM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + + } else { + pr_debug("%s: creating CVS full session\n", __func__); + + cvs_full_ctl_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + cvs_full_ctl_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_full_ctl_cmd) - + APR_HDR_SIZE); + + cvs_full_ctl_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_full_ctl_cmd.hdr.dest_port = 0; + cvs_full_ctl_cmd.hdr.token = 0; + cvs_full_ctl_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION; + cvs_full_ctl_cmd.cvs_session.direction = 2; + cvs_full_ctl_cmd.cvs_session.enc_media_type = + common.mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.dec_media_type = + common.mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.network_id = + common.mvs_info.network_type; + strlcpy(cvs_full_ctl_cmd.cvs_session.name, + "default q6 voice", + strlen("default q6 voice")+1); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &cvs_full_ctl_cmd); + + if (ret < 0) { + pr_err("%s: Err %d sending CREATE_FULL_CTRL\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + + /* Attach MVM to CVS. */ + pr_debug("%s: Attach MVM to stream\n", __func__); + + attach_stream_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + attach_stream_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(attach_stream_cmd) - + APR_HDR_SIZE); + attach_stream_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + attach_stream_cmd.hdr.dest_port = mvm_handle; + attach_stream_cmd.hdr.token = 0; + attach_stream_cmd.hdr.opcode = + VSS_IMVM_CMD_ATTACH_STREAM; + attach_stream_cmd.attach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &attach_stream_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending ATTACH_STREAM\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } + } + return 0; + +fail: + return ret; +} + +static int voice_unmap_cal_block(struct voice_data *v, int cal_index) +{ + int result = 0; + struct cal_block_data *cal_block; + + if (common.cal_data[cal_index] == NULL) { + pr_err("%s: Cal type is NULL, index %d!\n", + __func__, cal_index); + + goto done; + } + + mutex_lock(&common.cal_data[cal_index]->lock); + cal_block = cal_utils_get_only_cal_block( + common.cal_data[cal_index]); + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL, index %d!\n", + __func__, cal_index); + + result = -EINVAL; + goto unlock; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_debug("%s: Q6 handle is not set!\n", __func__); + + result = -EINVAL; + goto unlock; + } + + mutex_lock(&common.common_lock); + result = voice_send_mvm_unmap_memory_physical_cmd( + v, cal_block->map_data.q6map_handle); + if (result) + pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n", + __func__, v->session_id, result); + + cal_block->map_data.q6map_handle = 0; + mutex_unlock(&common.common_lock); +unlock: + mutex_unlock(&common.cal_data[cal_index]->lock); +done: + return result; +} + +static int voice_destroy_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_detach_stream_cmd detach_stream; + struct apr_hdr mvm_destroy; + struct apr_hdr cvs_destroy; + void *apr_mvm, *apr_cvs; + u16 mvm_handle, cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + apr_cvs = common.apr_q6_cvs; + + if (!apr_mvm || !apr_cvs) { + pr_err("%s: apr_mvm or apr_cvs is NULL\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvs_handle = voice_get_cvs_handle(v); + + /* MVM, CVS sessions are destroyed only for Full control sessions. */ + if (is_voip_session(v->session_id)) { + pr_debug("%s: MVM detach stream, VOC_STATE: %d\n", __func__, + v->voc_state); + + /* Detach voice stream. */ + detach_stream.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(detach_stream) - APR_HDR_SIZE); + detach_stream.hdr.src_port = + voice_get_idx_for_session(v->session_id); + detach_stream.hdr.dest_port = mvm_handle; + detach_stream.hdr.token = 0; + detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM; + detach_stream.detach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream); + if (ret < 0) { + pr_err("%s: Error %d sending DETACH_STREAM\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + /* Unmap memory */ + if (v->shmem_info.mem_handle != 0) { + ret = voice_send_mvm_unmap_memory_physical_cmd(v, + v->shmem_info.mem_handle); + if (ret < 0) { + pr_err("%s Memory_unmap for voip failed %d\n", + __func__, ret); + + goto fail; + } + v->shmem_info.mem_handle = 0; + } + } + + /* Unmap Source Tracking shared memory if mapped earlier */ + voice_unmap_and_free_source_tracking_shared_memory(v); + + if (is_voip_session(v->session_id) || + is_qchat_session(v->session_id) || + is_volte_session(v->session_id) || + is_vowlan_session(v->session_id) || + v->voc_state == VOC_ERROR || common.is_destroy_cvd) { + /* Destroy CVS. */ + pr_debug("%s: CVS destroy session\n", __func__); + + cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_destroy) - APR_HDR_SIZE); + cvs_destroy.src_port = + voice_get_idx_for_session(v->session_id); + cvs_destroy.dest_port = cvs_handle; + cvs_destroy.token = 0; + cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy); + if (ret < 0) { + pr_err("%s: Error %d sending CVS DESTROY\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + cvs_handle = 0; + voice_set_cvs_handle(v, cvs_handle); + + /* Unmap physical memory for all calibration buffers */ + if (!is_other_session_active(v->session_id)) { + if (voice_unmap_cal_block(v, CVP_VOCPROC_CAL)) + pr_err("%s: Unmap VOCPROC cal failed\n", + __func__); + if (voice_unmap_cal_block(v, CVP_VOCVOL_CAL)) + pr_err("%s: Unmap VOCVOL cal failed\n", + __func__); + if (voice_unmap_cal_block(v, CVP_VOCDEV_CFG_CAL)) + pr_err("%s: Unmap VOCDEV_CFG cal failed\n", + __func__); + if (voice_unmap_cal_block(v, CVS_VOCSTRM_CAL)) + pr_err("%s: Unmap VOCSTRM cal failed\n", + __func__); + } + + /* Destroy MVM. */ + pr_debug("%s: MVM destroy session\n", __func__); + + mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_destroy) - APR_HDR_SIZE); + mvm_destroy.src_port = + voice_get_idx_for_session(v->session_id); + mvm_destroy.dest_port = mvm_handle; + mvm_destroy.token = 0; + mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy); + if (ret < 0) { + pr_err("%s: Error %d sending MVM DESTROY\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + mvm_handle = 0; + voice_set_mvm_handle(v, mvm_handle); + } + return 0; +fail: + return ret; +} + +static int voice_send_tty_mode_cmd(struct voice_data *v) +{ + int ret = 0; + struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + /* send tty mode cmd to mvm */ + mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_tty_mode_cmd) - + APR_HDR_SIZE); + pr_debug("%s: pkt size = %d\n", + __func__, mvm_tty_mode_cmd.hdr.pkt_size); + mvm_tty_mode_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_tty_mode_cmd.hdr.dest_port = mvm_handle; + mvm_tty_mode_cmd.hdr.token = 0; + mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE; + mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode; + pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending SET_TTY_MODE\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_set_pp_enable_cmd(struct voice_data *v, + uint32_t module_id, int enable) +{ + struct cvs_set_pp_enable_cmd cvs_set_pp_cmd; + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + cvs_set_pp_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_pp_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_pp_cmd) - + APR_HDR_SIZE); + cvs_set_pp_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + cvs_set_pp_cmd.hdr.dest_port = cvs_handle; + cvs_set_pp_cmd.hdr.token = 0; + cvs_set_pp_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_UI_PROPERTY; + + cvs_set_pp_cmd.vss_set_pp.module_id = module_id; + cvs_set_pp_cmd.vss_set_pp.param_id = VOICE_PARAM_MOD_ENABLE; + cvs_set_pp_cmd.vss_set_pp.param_size = MOD_ENABLE_PARAM_LEN; + cvs_set_pp_cmd.vss_set_pp.reserved = 0; + cvs_set_pp_cmd.vss_set_pp.enable = enable; + cvs_set_pp_cmd.vss_set_pp.reserved_field = 0; + pr_debug("voice_send_set_pp_enable_cmd, module_id=%d, enable=%d\n", + module_id, enable); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_pp_cmd); + if (ret < 0) { + pr_err("Fail: sending cvs set pp enable,\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_hd_cmd(struct voice_data *v, int enable) +{ + struct mvm_set_hd_enable_cmd mvm_set_hd_cmd; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + mvm_handle = voice_get_mvm_handle(v); + if (!mvm_handle) { + pr_err("%s: mvm_handle is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mvm_set_hd_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_hd_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_hd_cmd) - + APR_HDR_SIZE); + mvm_set_hd_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + mvm_set_hd_cmd.hdr.dest_port = mvm_handle; + mvm_set_hd_cmd.hdr.token = 0; + + if (enable) + mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_ENABLE; + else + mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_DISABLE; + + pr_debug("%s: enable=%d\n", __func__, enable); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_hd_cmd); + if (ret < 0) { + pr_err("%s: Failed to sending mvm set HD Voice enable %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_set_dtx(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + /* Set DTX */ + cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_dtx) - APR_HDR_SIZE); + cvs_set_dtx.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_dtx.hdr.dest_port = cvs_handle; + cvs_set_dtx.hdr.token = 0; + cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE; + cvs_set_dtx.dtx_mode.enable = common.mvs_info.dtx_mode; + + pr_debug("%s: Setting DTX %d\n", __func__, common.mvs_info.dtx_mode); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx); + if (ret < 0) { + pr_err("%s: Error %d sending SET_DTX\n", __func__, ret); + return -EINVAL; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + return ret; + } + + return 0; +} + +static int voice_send_mvm_media_type_cmd(struct voice_data *v) +{ + struct vss_imvm_cmd_set_cal_media_type_t mvm_set_cal_media_type; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_set_cal_media_type.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_cal_media_type.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_cal_media_type) - + APR_HDR_SIZE); + mvm_set_cal_media_type.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_cal_media_type.hdr.dest_port = mvm_handle; + mvm_set_cal_media_type.hdr.token = 0; + mvm_set_cal_media_type.hdr.opcode = VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE; + mvm_set_cal_media_type.media_id = common.mvs_info.media_type; + pr_debug("%s: setting media_id as %x\n", + __func__, mvm_set_cal_media_type.media_id); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_media_type); + if (ret < 0) { + pr_err("%s: Error %d sending media type\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_dtmf_rx_detection_cmd(struct voice_data *v, + uint32_t enable) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct cvs_set_rx_dtmf_detection_cmd cvs_dtmf_rx_detection; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + /* Set SET_DTMF_RX_DETECTION */ + cvs_dtmf_rx_detection.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_dtmf_rx_detection.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE); + cvs_dtmf_rx_detection.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle; + cvs_dtmf_rx_detection.hdr.token = 0; + cvs_dtmf_rx_detection.hdr.opcode = + VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION; + cvs_dtmf_rx_detection.cvs_dtmf_det.enable = enable; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_dtmf_rx_detection); + if (ret < 0) { + pr_err("%s: Error %d sending SET_DTMF_RX_DETECTION\n", + __func__, + ret); + return -EINVAL; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + return ret; + } + + return ret; +} + +static void voice_vote_powerstate_to_bms(struct voice_data *v, bool state) +{ + union power_supply_propval pval = {0, }; + + if (!v->psy) + v->psy = power_supply_get_by_name("bms"); + if (v->psy && !(is_voip_session(v->session_id) || + is_vowlan_session(v->session_id))) { + pval.intval = VMBMS_VOICE_CALL_BIT; + if (state) { + power_supply_set_property(v->psy, + POWER_SUPPLY_PROP_HI_POWER, + &pval); + pr_debug("%s : Vote High power to BMS\n", + __func__); + } else { + power_supply_set_property(v->psy, + POWER_SUPPLY_PROP_LOW_POWER, + &pval); + pr_debug("%s: Vote low power to BMS\n", + __func__); + } + } else { + pr_debug("%s: No OP", __func__); + } + +} + +void voc_disable_dtmf_det_on_active_sessions(void) +{ + struct voice_data *v = NULL; + int i; + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + if ((v->dtmf_rx_detect_en) && + is_voc_state_active(v->voc_state)) { + + pr_debug("disable dtmf det on ses_id=%d\n", + v->session_id); + voice_send_dtmf_rx_detection_cmd(v, 0); + } + } +} + +int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + return -EINVAL; + } + + mutex_lock(&v->lock); + v->dtmf_rx_detect_en = enable; + + if (is_voc_state_active(v->voc_state)) + ret = voice_send_dtmf_rx_detection_cmd(v, + v->dtmf_rx_detect_en); + + mutex_unlock(&v->lock); + + return ret; +} + +void voc_set_destroy_cvd_flag(bool is_destroy_cvd) +{ + pr_debug("%s: %d\n", __func__, is_destroy_cvd); + common.is_destroy_cvd = is_destroy_cvd; +} + +void voc_set_vote_bms_flag(bool is_vote_bms) +{ + pr_debug("%s: flag value: %d\n", __func__, is_vote_bms); + common.is_vote_bms = is_vote_bms; +} + +int voc_alloc_cal_shared_memory(void) +{ + int rc = 0; + + mutex_lock(&common.common_lock); + if (is_cal_memory_allocated()) { + pr_debug("%s: Calibration shared buffer already allocated", + __func__); + } else { + /* Allocate memory for calibration memory map table. */ + rc = voice_alloc_cal_mem_map_table(); + if ((rc < 0) && (rc != -EPROBE_DEFER)) { + pr_err("%s: Failed to allocate cal memory, err=%d", + __func__, rc); + } + } + mutex_unlock(&common.common_lock); + + return rc; +} + +int voc_alloc_voip_shared_memory(void) +{ + int rc = 0; + + /* Allocate shared memory for OOB Voip */ + rc = voice_alloc_oob_shared_mem(); + if (rc < 0) { + pr_err("%s: Failed to alloc shared memory for OOB rc:%d\n", + __func__, rc); + } else { + /* Allocate mem map table for OOB */ + rc = voice_alloc_oob_mem_table(); + if (rc < 0) { + pr_err("%s: Failed to alloc mem map talbe rc:%d\n", + __func__, rc); + + voice_free_oob_shared_mem(); + } + } + + return rc; +} + +static int is_cal_memory_allocated(void) +{ + bool ret; + + if (common.cal_mem_map_table.client != NULL && + common.cal_mem_map_table.handle != NULL) + ret = true; + else + ret = false; + + return ret; +} + + +static int free_cal_map_table(void) +{ + int ret = 0; + + if ((common.cal_mem_map_table.client == NULL) || + (common.cal_mem_map_table.handle == NULL)) + goto done; + + ret = msm_audio_ion_free(common.cal_mem_map_table.client, + common.cal_mem_map_table.handle); + if (ret < 0) + pr_err("%s: msm_audio_ion_free failed:\n", __func__); + +done: + common.cal_mem_map_table.client = NULL; + common.cal_mem_map_table.handle = NULL; + return ret; +} + +static int is_rtac_memory_allocated(void) +{ + bool ret; + + if (common.rtac_mem_map_table.client != NULL && + common.rtac_mem_map_table.handle != NULL) + ret = true; + else + ret = false; + + return ret; +} + +static int free_rtac_map_table(void) +{ + int ret = 0; + + if ((common.rtac_mem_map_table.client == NULL) || + (common.rtac_mem_map_table.handle == NULL)) + goto done; + + ret = msm_audio_ion_free(common.rtac_mem_map_table.client, + common.rtac_mem_map_table.handle); + if (ret < 0) + pr_err("%s: msm_audio_ion_free failed:\n", __func__); + +done: + common.rtac_mem_map_table.client = NULL; + common.rtac_mem_map_table.handle = NULL; + return ret; +} + + +static int is_voip_memory_allocated(void) +{ + bool ret; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + if (v == NULL) { + pr_err("%s: v is NULL, session_id:%d\n", __func__, + common.voice[VOC_PATH_FULL].session_id); + + ret = false; + goto done; + } + + mutex_lock(&common.common_lock); + if (v->shmem_info.sh_buf.client != NULL && + v->shmem_info.sh_buf.handle != NULL) + ret = true; + else + ret = false; + mutex_unlock(&common.common_lock); + +done: + return ret; +} + +static int voice_config_cvs_vocoder_amr_rate(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvs_handle = voice_get_cvs_handle(v); + + pr_debug("%s: Setting AMR rate. Media Type: %d\n", __func__, + common.mvs_info.media_type); + + cvs_set_amr_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_amr_rate) - APR_HDR_SIZE); + cvs_set_amr_rate.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_amr_rate.hdr.dest_port = cvs_handle; + cvs_set_amr_rate.hdr.token = 0; + + if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_NB_MODEM) + cvs_set_amr_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE; + else if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_WB_MODEM) + cvs_set_amr_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE; + + cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate); + if (ret < 0) { + pr_err("%s: Error %d sending SET_AMR_RATE\n", + __func__, ret); + + goto done; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + return 0; +done: + return ret; +} + +static int voice_config_cvs_vocoder(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + /* Set media type. */ + struct cvs_set_media_type_cmd cvs_set_media_cmd; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_media_cmd) - + APR_HDR_SIZE); + cvs_set_media_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_media_cmd.hdr.dest_port = cvs_handle; + cvs_set_media_cmd.hdr.token = 0; + cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE; + cvs_set_media_cmd.media_type.tx_media_id = common.mvs_info.media_type; + cvs_set_media_cmd.media_type.rx_media_id = common.mvs_info.media_type; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending SET_MEDIA_TYPE\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + /* Set encoder properties. */ + switch (common.mvs_info.media_type) { + case VSS_MEDIA_ID_EVRC_MODEM: + case VSS_MEDIA_ID_4GV_NB_MODEM: + case VSS_MEDIA_ID_4GV_WB_MODEM: + case VSS_MEDIA_ID_4GV_NW_MODEM: { + struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate; + + pr_debug("Setting EVRC min-max rate\n"); + + cvs_set_cdma_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE); + cvs_set_cdma_rate.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_cdma_rate.hdr.dest_port = cvs_handle; + cvs_set_cdma_rate.hdr.token = 0; + cvs_set_cdma_rate.hdr.opcode = + VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE; + cvs_set_cdma_rate.cdma_rate.min_rate = + common.mvs_info.evrc_min_rate; + cvs_set_cdma_rate.cdma_rate.max_rate = + common.mvs_info.evrc_max_rate; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate); + if (ret < 0) { + pr_err("%s: Error %d sending SET_EVRC_MINMAX_RATE\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + if (common.mvs_info.media_type != VSS_MEDIA_ID_EVRC_MODEM) { + ret = voice_set_dtx(v); + if (ret < 0) + goto fail; + } + + break; + } + case VSS_MEDIA_ID_AMR_NB_MODEM: + case VSS_MEDIA_ID_AMR_WB_MODEM: { + ret = voice_config_cvs_vocoder_amr_rate(v); + if (ret) { + pr_err("%s: Failed to update vocoder rate. %d\n", + __func__, ret); + + goto fail; + } + + ret = voice_set_dtx(v); + if (ret < 0) + goto fail; + + break; + } + case VSS_MEDIA_ID_G729: + case VSS_MEDIA_ID_G711_ALAW: + case VSS_MEDIA_ID_G711_MULAW: { + ret = voice_set_dtx(v); + + break; + } + default: + /* Do nothing. */ + break; + } + return 0; + +fail: + return ret; +} + +int voc_update_amr_vocoder_rate(uint32_t session_id) +{ + int ret = 0; + struct voice_data *v; + + pr_debug("%s: session_id:%d", __func__, session_id); + + v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: v is NULL, session_id:%d\n", __func__, + session_id); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&v->lock); + ret = voice_config_cvs_vocoder_amr_rate(v); + mutex_unlock(&v->lock); + +done: + return ret; +} + +static int voice_send_start_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_start_voice_cmd; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_start_voice_cmd pkt size = %d\n", + mvm_start_voice_cmd.pkt_size); + mvm_start_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_start_voice_cmd.dest_port = mvm_handle; + mvm_start_voice_cmd.token = 0; + mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } else { + if (common.is_vote_bms) { + /* vote high power to BMS during call start */ + voice_vote_powerstate_to_bms(v, true); + } + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static void voc_get_tx_rx_topology(struct voice_data *v, + uint32_t *tx_topology_id, + uint32_t *rx_topology_id) +{ + uint32_t tx_id = 0; + uint32_t rx_id = 0; + + if (v->lch_mode == VOICE_LCH_START || v->disable_topology) { + pr_debug("%s: Setting TX and RX topology to NONE for LCH\n", + __func__); + + tx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE; + rx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE; + } else { + tx_id = voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL); + rx_id = voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL); + } + + *tx_topology_id = tx_id; + *rx_topology_id = rx_id; +} + +static int voice_send_set_device_cmd(struct voice_data *v) +{ + struct cvp_set_device_cmd cvp_setdev_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + cvp_handle = voice_get_cvp_handle(v); + + /* set device and wait for response */ + cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_setdev_cmd) - APR_HDR_SIZE); + pr_debug(" send create cvp setdev, pkt size = %d\n", + cvp_setdev_cmd.hdr.pkt_size); + cvp_setdev_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_setdev_cmd.hdr.dest_port = cvp_handle; + cvp_setdev_cmd.hdr.token = 0; + + if (voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2) + cvp_setdev_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_SET_DEVICE_V3; + else + cvp_setdev_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_SET_DEVICE_V2; + + voc_get_tx_rx_topology(v, + &cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id, + &cvp_setdev_cmd.cvp_set_device_v2.rx_topology_id); + + cvp_setdev_cmd.cvp_set_device_v2.tx_port_id = v->dev_tx.port_id; + cvp_setdev_cmd.cvp_set_device_v2.rx_port_id = v->dev_rx.port_id; + + if (common.ec_ref_ext) { + cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING; + cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id = + common.ec_port_id; + } else { + cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING; + cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id = + VSS_IVOCPROC_PORT_ID_NONE; + } + pr_debug("topology=%d , tx_port_id=%d, rx_port_id=%d\n", + cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id, + cvp_setdev_cmd.cvp_set_device_v2.tx_port_id, + cvp_setdev_cmd.cvp_set_device_v2.rx_port_id); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IVOCPROC_CMD_SET_DEVICE\n"); + goto fail; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_stop_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_stop_voice_cmd; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_stop_voice_cmd pkt size = %d\n", + mvm_stop_voice_cmd.pkt_size); + mvm_stop_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_stop_voice_cmd.dest_port = mvm_handle; + mvm_stop_voice_cmd.token = 0; + mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} +static int voice_get_cal(struct cal_block_data **cal_block, + int cal_block_idx, + struct cal_block_data **col_data, + int col_data_idx, int session_id) +{ + int ret = 0; + + *cal_block = cal_utils_get_only_cal_block( + common.cal_data[cal_block_idx]); + if (*cal_block == NULL) { + pr_err("%s: No cal data for cal %d!\n", + __func__, cal_block_idx); + + ret = -ENODEV; + goto done; + } + ret = remap_cal_data(*cal_block, session_id); + if (ret < 0) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_block_idx); + + ret = -ENODEV; + goto done; + } + + if (col_data == NULL) + goto done; + + *col_data = cal_utils_get_only_cal_block( + common.cal_data[col_data_idx]); + if (*col_data == NULL) { + pr_err("%s: No cal data for cal %d!\n", + __func__, col_data_idx); + + ret = -ENODEV; + goto done; + } +done: + return ret; +} + +static int voice_send_cvs_register_cal_cmd(struct voice_data *v) +{ + struct cvs_register_cal_data_cmd cvs_reg_cal_cmd; + struct cal_block_data *cal_block = NULL; + struct cal_block_data *col_data = NULL; + int ret = 0; + + memset(&cvs_reg_cal_cmd, 0, sizeof(cvs_reg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[CVS_VOCSTRM_CAL]->lock); + mutex_lock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVS_VOCSTRM_CAL, &col_data, + CVS_VOCSTRM_COL_CAL, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVS_VOCSTRM_CAL); + + goto unlock; + } + + memcpy(&cvs_reg_cal_cmd.cvs_cal_data.column_info[0], + (void *) &((struct audio_cal_info_voc_col *) + col_data->cal_info)->data, + col_data->cal_data.size); + + cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE); + cvs_reg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v); + cvs_reg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvs_reg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA; + else + cvs_reg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2; + + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_handle = + cal_block->map_data.q6map_handle; + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_size = + cal_block->cal_data.size; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_reg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVS cal\n", __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock); + mutex_unlock(&common.cal_data[CVS_VOCSTRM_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v) +{ + struct cvs_deregister_cal_data_cmd cvs_dereg_cal_cmd; + int ret = 0; + + memset(&cvs_dereg_cal_cmd, 0, sizeof(cvs_dereg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE); + cvs_dereg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v); + cvs_dereg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvs_dereg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA; + else + cvs_dereg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_dereg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; + +} + +static int voice_send_cvp_create_cmd(struct voice_data *v) +{ + struct cvp_create_full_ctl_session_cmd cvp_session_cmd; + void *apr_cvp; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + /* create cvp session and wait for response */ + cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_session_cmd) - APR_HDR_SIZE); + pr_debug("%s: send create cvp session, pkt size = %d\n", + __func__, cvp_session_cmd.hdr.pkt_size); + cvp_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_session_cmd.hdr.dest_port = 0; + cvp_session_cmd.hdr.token = 0; + + if (voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2) + cvp_session_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3; + else + cvp_session_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2; + + voc_get_tx_rx_topology(v, + &cvp_session_cmd.cvp_session.tx_topology_id, + &cvp_session_cmd.cvp_session.rx_topology_id); + + cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/ + cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.port_id; + cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.port_id; + cvp_session_cmd.cvp_session.profile_id = + VSS_ICOMMON_CAL_NETWORK_ID_NONE; + if (common.ec_ref_ext) { + cvp_session_cmd.cvp_session.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING; + cvp_session_cmd.cvp_session.ec_ref_port_id = + common.ec_port_id; + } else { + cvp_session_cmd.cvp_session.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING; + cvp_session_cmd.cvp_session.ec_ref_port_id = + VSS_IVOCPROC_PORT_ID_NONE; + } + + pr_debug("tx_topology: %d tx_port_id=%d, rx_port_id=%d, mode: 0x%x\n", + cvp_session_cmd.cvp_session.tx_topology_id, + cvp_session_cmd.cvp_session.tx_port_id, + cvp_session_cmd.cvp_session.rx_port_id, + cvp_session_cmd.cvp_session.vocproc_mode); + pr_debug("rx_topology: %d, profile_id: 0x%x, pkt_size: %d\n", + cvp_session_cmd.cvp_session.rx_topology_id, + cvp_session_cmd.cvp_session.profile_id, + cvp_session_cmd.hdr.pkt_size); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd); + if (ret < 0) { + pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n"); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v) +{ + struct cvp_register_dev_cfg_cmd cvp_reg_dev_cfg_cmd; + struct cal_block_data *cal_block = NULL; + int ret = 0; + + memset(&cvp_reg_dev_cfg_cmd, 0, sizeof(cvp_reg_dev_cfg_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + mutex_lock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVP_VOCDEV_CFG_CAL, NULL, + 0, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVP_VOCDEV_CFG_CAL); + + goto unlock; + } + + cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE); + cvp_reg_dev_cfg_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_reg_dev_cfg_cmd.hdr.token = 0; + cvp_reg_dev_cfg_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG; + + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_handle = + cal_block->map_data.q6map_handle; + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_size = + cal_block->cal_data.size; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_reg_dev_cfg_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVP dev cfg cal\n", + __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v) +{ + struct cvp_deregister_dev_cfg_cmd cvp_dereg_dev_cfg_cmd; + int ret = 0; + + memset(&cvp_dereg_dev_cfg_cmd, 0, sizeof(cvp_dereg_dev_cfg_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + cvp_dereg_dev_cfg_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE); + cvp_dereg_dev_cfg_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_dereg_dev_cfg_cmd.hdr.token = 0; + cvp_dereg_dev_cfg_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_dereg_dev_cfg_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVP dev cfg cal\n", + __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_register_cal_cmd(struct voice_data *v) +{ + struct cvp_register_cal_data_cmd cvp_reg_cal_cmd; + struct cal_block_data *cal_block = NULL; + struct cal_block_data *col_data = NULL; + int ret = 0; + + memset(&cvp_reg_cal_cmd, 0, sizeof(cvp_reg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[CVP_VOCPROC_CAL]->lock); + mutex_lock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVP_VOCPROC_CAL, &col_data, + CVP_VOCPROC_COL_CAL, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVP_VOCPROC_CAL); + + goto unlock; + } + + v->dev_tx.dev_id = ((struct audio_cal_info_vocproc *) + cal_block->cal_info)->tx_acdb_id; + v->dev_rx.dev_id = ((struct audio_cal_info_vocproc *) + cal_block->cal_info)->rx_acdb_id; + pr_debug("%s: %s: Tx acdb id = %d and Rx acdb id = %d", __func__, + voc_get_session_name(v->session_id), v->dev_tx.dev_id, + v->dev_rx.dev_id); + + memcpy(&cvp_reg_cal_cmd.cvp_cal_data.column_info[0], + (void *) &((struct audio_cal_info_voc_col *) + col_data->cal_info)->data, + col_data->cal_data.size); + + cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE); + cvp_reg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_reg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_reg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA; + else + cvp_reg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2; + + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_handle = + cal_block->map_data.q6map_handle; + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_size = + cal_block->cal_data.size; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_reg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVP cal\n", __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock); + mutex_unlock(&common.cal_data[CVP_VOCPROC_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v) +{ + struct cvp_deregister_cal_data_cmd cvp_dereg_cal_cmd; + int ret = 0; + + memset(&cvp_dereg_cal_cmd, 0, sizeof(cvp_dereg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EPERM; + goto done; + } + + cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE); + cvp_dereg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_dereg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_dereg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA; + else + cvp_dereg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_dereg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v) +{ + struct cvp_register_vol_cal_data_cmd cvp_reg_vol_cal_cmd; + struct cal_block_data *cal_block = NULL; + struct cal_block_data *col_data = NULL; + int ret = 0; + + memset(&cvp_reg_vol_cal_cmd, 0, sizeof(cvp_reg_vol_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[CVP_VOCVOL_CAL]->lock); + mutex_lock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVP_VOCVOL_CAL, &col_data, + CVP_VOCVOL_COL_CAL, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVP_VOCVOL_CAL); + + goto unlock; + } + + memcpy(&cvp_reg_vol_cal_cmd.cvp_vol_cal_data.column_info[0], + (void *) &((struct audio_cal_info_voc_col *) + col_data->cal_info)->data, + col_data->cal_data.size); + + cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE); + cvp_reg_vol_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_reg_vol_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_reg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA; + else + cvp_reg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA; + + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_handle = + cal_block->map_data.q6map_handle; + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_size = + cal_block->cal_data.size; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_reg_vol_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock); + mutex_unlock(&common.cal_data[CVP_VOCVOL_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v) +{ + struct cvp_deregister_vol_cal_data_cmd cvp_dereg_vol_cal_cmd; + int ret = 0; + + memset(&cvp_dereg_vol_cal_cmd, 0, sizeof(cvp_dereg_vol_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + cvp_dereg_vol_cal_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE); + cvp_dereg_vol_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_dereg_vol_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_dereg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA; + else + cvp_dereg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_dereg_vol_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVP vol cal\n", + __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_map_memory_physical_cmd(struct voice_data *v, + struct mem_map_table *table_info, + dma_addr_t phys, + uint32_t size, + uint32_t token) +{ + struct vss_imemory_cmd_map_physical_t mvm_map_phys_cmd; + uint32_t *memtable; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!common.apr_q6_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!table_info->data) { + pr_err("%s: memory table is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + memtable = (uint32_t *) table_info->data; + + /* + * Store next table descriptor's address(64 bit) as NULL as there + * is only one memory block + */ + memtable[0] = 0; + memtable[1] = 0; + + /* Store next table descriptor's size */ + memtable[2] = 0; + + /* Store shared mem adddress (64 bit) */ + memtable[3] = lower_32_bits(phys); + memtable[4] = msm_audio_populate_upper_32_bits(phys); + + /* Store shared memory size */ + memtable[5] = size; + + mvm_map_phys_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE); + mvm_map_phys_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v); + mvm_map_phys_cmd.hdr.token = token; + mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL; + + mvm_map_phys_cmd.table_descriptor.mem_address_lsw = + lower_32_bits(table_info->phys); + mvm_map_phys_cmd.table_descriptor.mem_address_msw = + msm_audio_populate_upper_32_bits(table_info->phys); + mvm_map_phys_cmd.table_descriptor.mem_size = + sizeof(struct vss_imemory_block_t) + + sizeof(struct vss_imemory_table_descriptor_t); + mvm_map_phys_cmd.is_cached = true; + mvm_map_phys_cmd.cache_line_size = 128; + mvm_map_phys_cmd.access_mask = 3; + mvm_map_phys_cmd.page_align = 4096; + mvm_map_phys_cmd.min_data_width = 8; + mvm_map_phys_cmd.max_data_width = 64; + + pr_debug("%s: next table desc: add: %lld, size: %d\n", + __func__, *((uint64_t *) memtable), + *(((uint32_t *) memtable) + 2)); + pr_debug("%s: phy add of of mem being mapped LSW:0x%x, MSW:0x%x size: %d\n", + __func__, *(((uint32_t *) memtable) + 3), + *(((uint32_t *) memtable) + 4), *(((uint32_t *) memtable) + 5)); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_mvm, (uint32_t *) &mvm_map_phys_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending mvm map phy cmd\n", __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_pause_voice_call(struct voice_data *v) +{ + struct apr_hdr mvm_pause_voice_cmd; + void *apr_mvm; + int ret = 0; + + pr_debug("%s\n", __func__); + + if (v == NULL) { + pr_err("%s: Voice data is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + mvm_pause_voice_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_pause_voice_cmd.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_pause_voice_cmd) - APR_HDR_SIZE); + mvm_pause_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_pause_voice_cmd.dest_port = voice_get_mvm_handle(v); + mvm_pause_voice_cmd.token = 0; + mvm_pause_voice_cmd.opcode = VSS_IMVM_CMD_PAUSE_VOICE; + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + pr_debug("%s: send mvm_pause_voice_cmd pkt size = %d\n", + __func__, mvm_pause_voice_cmd.pkt_size); + + ret = apr_send_pkt(apr_mvm, + (uint32_t *)&mvm_pause_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_PAUSE_VOICE\n"); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_map_cal_memory(struct cal_block_data *cal_block, + uint32_t session_id) +{ + int result = 0; + int voc_index; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: Map size is 0!\n", __func__); + + result = -EINVAL; + goto done; + } + + voc_index = voice_get_idx_for_session(session_id); + if (voc_index < 0) { + pr_err("%s: Invalid session ID %d\n", __func__, session_id); + + goto done; + } + + mutex_lock(&common.common_lock); + v = &common.voice[voc_index]; + + result = voice_map_memory_physical_cmd(v, + &common.cal_mem_map_table, + (dma_addr_t)cal_block->cal_data.paddr, + cal_block->map_data.map_size, + VOC_CAL_MEM_MAP_TOKEN); + if (result < 0) { + pr_err("%s: Mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + + goto done_unlock; + } + + cal_block->map_data.q6map_handle = common.cal_mem_handle; +done_unlock: + mutex_unlock(&common.common_lock); +done: + return result; +} + +static int remap_cal_data(struct cal_block_data *cal_block, + uint32_t session_id) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + if (cal_block->map_data.ion_client == NULL) { + pr_err("%s: No ION allocation for session_id %d!\n", + __func__, session_id); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + + /* cal type not used */ + ret = voice_map_cal_memory(cal_block, session_id); + if (ret < 0) { + pr_err("%s: Mmap did not work! size = %zd\n", + __func__, cal_block->map_data.map_size); + + goto done; + } + } else { + pr_debug("%s: Cal block 0x%pK, size %zd already mapped. Q6 map handle = %d\n", + __func__, &cal_block->cal_data.paddr, + cal_block->map_data.map_size, + cal_block->map_data.q6map_handle); + } +done: + return ret; +} + +static int voice_unmap_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int result = 0; + int result2 = 0; + int i; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_debug("%s: Q6 handle is not set!\n", __func__); + + result = -EINVAL; + goto done; + } + + mutex_lock(&common.common_lock); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + result2 = voice_pause_voice_call(v); + if (result2 < 0) { + pr_err("%s: Voice_pause_voice_call failed for session 0x%x, err %d!\n", + __func__, v->session_id, result2); + + result = result2; + } + + if (cal_type == CVP_VOCPROC_DYNAMIC_CAL_TYPE) + voice_send_cvp_deregister_vol_cal_cmd(v); + else if (cal_type == CVP_VOCPROC_STATIC_CAL_TYPE) + voice_send_cvp_deregister_cal_cmd(v); + else if (cal_type == CVP_VOCDEV_CFG_CAL_TYPE) + voice_send_cvp_deregister_dev_cfg_cmd(v); + else if (cal_type == CVS_VOCSTRM_STATIC_CAL_TYPE) + voice_send_cvs_deregister_cal_cmd(v); + else + pr_err("%s: Invalid cal type %d!\n", + __func__, cal_type); + + result2 = voice_send_start_voice_cmd(v); + if (result2) { + pr_err("%s: Voice_send_start_voice_cmd failed for session 0x%x, err %d!\n", + __func__, v->session_id, result2); + + result = result2; + } + } + + if ((cal_block->map_data.q6map_handle != 0) && + (!is_other_session_active(v->session_id))) { + + result2 = voice_send_mvm_unmap_memory_physical_cmd( + v, cal_block->map_data.q6map_handle); + if (result2) { + pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n", + __func__, v->session_id, result2); + + result = result2; + } + cal_block->map_data.q6map_handle = 0; + } + mutex_unlock(&v->lock); + } + mutex_unlock(&common.common_lock); +done: + return result; +} + +int voc_register_vocproc_vol_table(void) +{ + int result = 0; + int result2 = 0; + int i; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + mutex_lock(&common.common_lock); + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + result2 = voice_send_cvp_register_vol_cal_cmd(v); + if (result2 < 0) { + pr_err("%s: Failed to register vocvol table for session 0x%x!\n", + __func__, v->session_id); + + result = result2; + /* Still try to register other sessions */ + } + } + mutex_unlock(&v->lock); + } + + mutex_unlock(&common.common_lock); + return result; +} + +int voc_deregister_vocproc_vol_table(void) +{ + int result = 0; + int success = 0; + int i; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + mutex_lock(&common.common_lock); + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + result = voice_send_cvp_deregister_vol_cal_cmd(v); + if (result < 0) { + pr_err("%s: Failed to deregister vocvol table for session 0x%x!\n", + __func__, v->session_id); + + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); + if (success) { + pr_err("%s: Try to re-register all deregistered sessions!\n", + __func__); + + voc_register_vocproc_vol_table(); + } + goto done; + } else { + success = 1; + } + } + mutex_unlock(&v->lock); + } + mutex_unlock(&common.common_lock); +done: + return result; +} + +int voc_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + + result = -EINVAL; + goto done; + } + + mutex_lock(&common.common_lock); + /* use first session */ + v = &common.voice[0]; + mutex_lock(&v->lock); + + if (!is_rtac_memory_allocated()) { + result = voice_alloc_rtac_mem_map_table(); + if (result < 0) { + pr_err("%s: RTAC alloc mem map table did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + + goto done_unlock; + } + } + + result = voice_map_memory_physical_cmd(v, + &common.rtac_mem_map_table, + (dma_addr_t)cal_block->cal_data.paddr, + cal_block->map_data.map_size, + VOC_RTAC_MEM_MAP_TOKEN); + if (result < 0) { + pr_err("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + + free_rtac_map_table(); + goto done_unlock; + } + + cal_block->map_data.map_handle = common.rtac_mem_handle; +done_unlock: + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); +done: + return result; +} + +int voc_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (mem_map_handle == NULL) { + pr_debug("%s: Map handle is NULL, nothing to unmap\n", + __func__); + + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + + goto done; + } + + mutex_lock(&common.common_lock); + /* Use first session */ + /* Only used for apr wait lock */ + v = &common.voice[0]; + mutex_lock(&v->lock); + + result = voice_send_mvm_unmap_memory_physical_cmd( + v, *mem_map_handle); + if (result) { + pr_err("%s: voice_send_mvm_unmap_memory_physical_cmd Failed for session 0x%x!\n", + __func__, v->session_id); + } else { + *mem_map_handle = 0; + common.rtac_mem_handle = 0; + free_rtac_map_table(); + } + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); +done: + return result; +} + +static int voice_setup_vocproc(struct voice_data *v) +{ + int ret = 0; + + ret = voice_send_cvp_create_cmd(v); + if (ret < 0) { + pr_err("%s: CVP create failed err:%d\n", __func__, ret); + goto fail; + } + + ret = voice_send_cvp_device_channels_cmd(v); + if (ret < 0) { + pr_err("%s: Set device channels failed err:%d\n", + __func__, ret); + goto fail; + } + + ret = voice_send_cvp_topology_commit_cmd(v); + if (ret < 0) { + pr_err("%s: Set topology commit failed err:%d\n", + __func__, ret); + goto fail; + } + + voice_send_cvs_register_cal_cmd(v); + voice_send_cvp_register_dev_cfg_cmd(v); + voice_send_cvp_register_cal_cmd(v); + voice_send_cvp_register_vol_cal_cmd(v); + + /* enable vocproc */ + ret = voice_send_enable_vocproc_cmd(v); + if (ret < 0) + goto fail; + + /* attach vocproc */ + ret = voice_send_attach_vocproc_cmd(v); + if (ret < 0) + goto fail; + + /* send tty mode if tty device is used */ + voice_send_tty_mode_cmd(v); + + if (is_voip_session(v->session_id)) { + ret = voice_send_mvm_cal_network_cmd(v); + if (ret < 0) + pr_err("%s: voice_send_mvm_cal_network_cmd: %d\n", + __func__, ret); + + ret = voice_send_mvm_media_type_cmd(v); + if (ret < 0) + pr_err("%s: voice_send_mvm_media_type_cmd: %d\n", + __func__, ret); + + voice_send_netid_timing_cmd(v); + } + + if (v->st_enable && !v->tty_mode) + voice_send_set_pp_enable_cmd(v, + MODULE_ID_VOICE_MODULE_ST, + v->st_enable); + /* Start in-call music delivery if this feature is enabled */ + if (v->music_info.play_enable) + voice_cvs_start_playback(v); + + /* Start in-call recording if this feature is enabled */ + if (v->rec_info.rec_enable) + voice_cvs_start_record(v, v->rec_info.rec_mode); + + if (v->dtmf_rx_detect_en) + voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en); + + if (v->hd_enable) + voice_send_hd_cmd(v, v->hd_enable); + + rtac_add_voice(voice_get_cvs_handle(v), + voice_get_cvp_handle(v), + v->dev_rx.port_id, v->dev_tx.port_id, + v->dev_rx.dev_id, v->dev_tx.dev_id, + v->session_id); + + return 0; + +fail: + return ret; +} + +static int voice_send_cvp_device_channels_cmd(struct voice_data *v) +{ + int ret = 0; + struct cvp_set_dev_channels_cmd cvp_set_dev_channels_cmd; + void *apr_cvp; + u16 cvp_handle; + + if (!(voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2)) { + pr_debug("%s CVD ver %s doesn't support send_device_channels cmd\n", + __func__, common.cvd_version); + + goto done; + } + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + cvp_set_dev_channels_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_dev_channels_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_dev_channels_cmd) - APR_HDR_SIZE); + cvp_set_dev_channels_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_dev_channels_cmd.hdr.dest_port = cvp_handle; + cvp_set_dev_channels_cmd.hdr.token = 0; + cvp_set_dev_channels_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS; + cvp_set_dev_channels_cmd.cvp_set_channels.rx_num_channels = + VSS_NUM_DEV_CHANNELS_1; + cvp_set_dev_channels_cmd.cvp_set_channels.tx_num_channels = + v->dev_tx.no_of_channels; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_dev_channels_cmd); + if (ret < 0) { + pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS\n", + __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_topology_commit_cmd(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvp_topology_commit_cmd; + void *apr_cvp; + u16 cvp_handle; + + if (!(voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2)) { + pr_debug("%s CVD version string %s doesn't support this command\n", + __func__, common.cvd_version); + + goto done; + } + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + cvp_topology_commit_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_topology_commit_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_topology_commit_cmd) - APR_HDR_SIZE); + cvp_topology_commit_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_topology_commit_cmd.dest_port = cvp_handle; + cvp_topology_commit_cmd.token = 0; + cvp_topology_commit_cmd.opcode = VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_topology_commit_cmd); + if (ret < 0) { + pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT\n", + __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_enable_vocproc_cmd(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvp_enable_cmd; + void *apr_cvp; + u16 cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + cvp_handle = voice_get_cvp_handle(v); + + /* enable vocproc and wait for respose */ + cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_enable_cmd) - APR_HDR_SIZE); + pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n", + cvp_enable_cmd.pkt_size, cvp_handle); + cvp_enable_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_enable_cmd.dest_port = cvp_handle; + cvp_enable_cmd.token = 0; + cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_mvm_cal_network_cmd(struct voice_data *v) +{ + struct vss_imvm_cmd_set_cal_network_t mvm_set_cal_network; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_set_cal_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_cal_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_cal_network) - APR_HDR_SIZE); + mvm_set_cal_network.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_cal_network.hdr.dest_port = mvm_handle; + mvm_set_cal_network.hdr.token = 0; + mvm_set_cal_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK; + mvm_set_cal_network.network_id = VSS_ICOMMON_CAL_NETWORK_ID_NONE; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_network); + if (ret < 0) { + pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_netid_timing_cmd(struct voice_data *v) +{ + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + struct mvm_set_network_cmd mvm_set_network; + struct mvm_set_voice_timing_cmd mvm_set_voice_timing; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + ret = voice_config_cvs_vocoder(v); + if (ret < 0) { + pr_err("%s: Error %d configuring CVS voc", + __func__, ret); + goto fail; + } + /* Set network ID. */ + pr_debug("Setting network ID %x\n", common.mvs_info.network_type); + + mvm_set_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_network) - APR_HDR_SIZE); + mvm_set_network.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_network.hdr.dest_port = mvm_handle; + mvm_set_network.hdr.token = 0; + mvm_set_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK; + mvm_set_network.network.network_id = common.mvs_info.network_type; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network); + if (ret < 0) { + pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + /* Set voice timing. */ + pr_debug("Setting voice timing\n"); + + mvm_set_voice_timing.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_voice_timing) - + APR_HDR_SIZE); + mvm_set_voice_timing.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_voice_timing.hdr.dest_port = mvm_handle; + mvm_set_voice_timing.hdr.token = 0; + mvm_set_voice_timing.hdr.opcode = VSS_ICOMMON_CMD_SET_VOICE_TIMING; + mvm_set_voice_timing.timing.mode = 0; + mvm_set_voice_timing.timing.enc_offset = 8000; + mvm_set_voice_timing.timing.dec_req_offset = 3300; + mvm_set_voice_timing.timing.dec_offset = 8300; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing); + if (ret < 0) { + pr_err("%s: Error %d sending SET_TIMING\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_attach_vocproc_cmd(struct voice_data *v) +{ + int ret = 0; + struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd; + void *apr_mvm; + u16 mvm_handle, cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvp_handle = voice_get_cvp_handle(v); + + /* attach vocproc and wait for response */ + mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_a_vocproc_cmd pkt size = %d\n", + mvm_a_vocproc_cmd.hdr.pkt_size); + mvm_a_vocproc_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_a_vocproc_cmd.hdr.token = 0; + mvm_a_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_ATTACH_VOCPROC; + mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_ATTACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static void voc_update_session_params(struct voice_data *v) +{ + /* reset LCH mode */ + v->lch_mode = 0; + + /* clear disable topology setting */ + v->disable_topology = false; + + /* clear mute setting */ + v->dev_rx.dev_mute = common.default_mute_val; + v->dev_tx.dev_mute = common.default_mute_val; + v->stream_rx.stream_mute = common.default_mute_val; + v->stream_tx.stream_mute = common.default_mute_val; +} + +static int voice_destroy_vocproc(struct voice_data *v) +{ + struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd; + struct apr_hdr cvp_destroy_session_cmd; + int ret = 0; + void *apr_mvm, *apr_cvp; + u16 mvm_handle, cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + apr_cvp = common.apr_q6_cvp; + + if (!apr_mvm || !apr_cvp) { + pr_err("%s: apr_mvm or apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvp_handle = voice_get_cvp_handle(v); + + /* disable slowtalk if st_enable is set */ + if (v->st_enable) + voice_send_set_pp_enable_cmd(v, MODULE_ID_VOICE_MODULE_ST, 0); + + /* Disable HD Voice if hd_enable is set */ + if (v->hd_enable) + voice_send_hd_cmd(v, 0); + + /* stop playback or recording */ + v->music_info.force = 1; + voice_cvs_stop_playback(v); + voice_cvs_stop_record(v); + /* If voice call is active during VoLTE, SRVCC happens. + * Start recording on voice session if recording started during VoLTE. + */ + if (is_volte_session(v->session_id) && + ((common.voice[VOC_PATH_PASSIVE].voc_state == VOC_RUN) || + (common.voice[VOC_PATH_PASSIVE].voc_state == VOC_CHANGE))) { + if (v->rec_info.rec_enable) { + voice_cvs_start_record( + &common.voice[VOC_PATH_PASSIVE], + v->rec_info.rec_mode); + common.srvcc_rec_flag = true; + + pr_debug("%s: switch recording, srvcc_rec_flag %d\n", + __func__, common.srvcc_rec_flag); + } + } + + /* send stop voice cmd */ + voice_send_stop_voice_cmd(v); + + /* send stop dtmf detecton cmd */ + if (v->dtmf_rx_detect_en) + voice_send_dtmf_rx_detection_cmd(v, 0); + + /* detach VOCPROC and wait for response from mvm */ + mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE); + pr_debug("mvm_d_vocproc_cmd pkt size = %d\n", + mvm_d_vocproc_cmd.hdr.pkt_size); + mvm_d_vocproc_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_d_vocproc_cmd.hdr.token = 0; + mvm_d_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_DETACH_VOCPROC; + mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_DETACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + voice_send_cvp_deregister_vol_cal_cmd(v); + voice_send_cvp_deregister_cal_cmd(v); + voice_send_cvp_deregister_dev_cfg_cmd(v); + voice_send_cvs_deregister_cal_cmd(v); + + /* destrop cvp session */ + cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE); + pr_debug("cvp_destroy_session_cmd pkt size = %d\n", + cvp_destroy_session_cmd.pkt_size); + cvp_destroy_session_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_destroy_session_cmd.dest_port = cvp_handle; + cvp_destroy_session_cmd.token = 0; + cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd); + if (ret < 0) { + pr_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + rtac_remove_voice(voice_get_cvs_handle(v)); + cvp_handle = 0; + voice_set_cvp_handle(v, cvp_handle); + return 0; +fail: + return ret; +} + +static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v, + uint32_t mem_handle) +{ + struct vss_imemory_cmd_unmap_t mem_unmap; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mem_unmap.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mem_unmap.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mem_unmap) - APR_HDR_SIZE); + mem_unmap.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mem_unmap.hdr.dest_port = mvm_handle; + mem_unmap.hdr.token = 0; + mem_unmap.hdr.opcode = VSS_IMEMORY_CMD_UNMAP; + mem_unmap.mem_handle = mem_handle; + + pr_debug("%s: mem_handle: 0x%x\n", __func__, mem_unmap.mem_handle); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mem_unmap); + if (ret < 0) { + pr_err("mem_unmap op[0x%x]ret[%d]\n", + mem_unmap.hdr.opcode, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; + +fail: + return ret; +} + +static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v) +{ + struct vss_istream_cmd_set_oob_packet_exchange_config_t + packet_exchange_config_pkt; + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + packet_exchange_config_pkt.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + packet_exchange_config_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(packet_exchange_config_pkt) - + APR_HDR_SIZE); + packet_exchange_config_pkt.hdr.src_port = + voice_get_idx_for_session(v->session_id); + packet_exchange_config_pkt.hdr.dest_port = cvs_handle; + packet_exchange_config_pkt.hdr.token = 0; + packet_exchange_config_pkt.hdr.opcode = + VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG; + packet_exchange_config_pkt.mem_handle = v->shmem_info.mem_handle; + /* dec buffer address */ + packet_exchange_config_pkt.dec_buf_addr_lsw = + lower_32_bits(v->shmem_info.sh_buf.buf[0].phys); + packet_exchange_config_pkt.dec_buf_addr_msw = + msm_audio_populate_upper_32_bits( + v->shmem_info.sh_buf.buf[0].phys); + packet_exchange_config_pkt.dec_buf_size = 4096; + /* enc buffer address */ + packet_exchange_config_pkt.enc_buf_addr_lsw = + lower_32_bits(v->shmem_info.sh_buf.buf[1].phys); + packet_exchange_config_pkt.enc_buf_addr_msw = + msm_audio_populate_upper_32_bits( + v->shmem_info.sh_buf.buf[1].phys); + packet_exchange_config_pkt.enc_buf_size = 4096; + + pr_debug("%s: dec buf add: lsw %0x msw %0x, size %d, enc buf add: lsw %0x msw %0x, size %d\n", + __func__, + packet_exchange_config_pkt.dec_buf_addr_lsw, + packet_exchange_config_pkt.dec_buf_addr_msw, + packet_exchange_config_pkt.dec_buf_size, + packet_exchange_config_pkt.enc_buf_addr_lsw, + packet_exchange_config_pkt.enc_buf_addr_msw, + packet_exchange_config_pkt.enc_buf_size); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &packet_exchange_config_pkt); + if (ret < 0) { + pr_err("Failed to send packet exchange config cmd %d\n", ret); + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: wait_event timeout %d\n", __func__, ret); + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v) +{ + struct vss_istream_cmd_set_packet_exchange_mode_t data_exchange_pkt; + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + data_exchange_pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + data_exchange_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(data_exchange_pkt) - APR_HDR_SIZE); + data_exchange_pkt.hdr.src_port = + voice_get_idx_for_session(v->session_id); + data_exchange_pkt.hdr.dest_port = cvs_handle; + data_exchange_pkt.hdr.token = 0; + data_exchange_pkt.hdr.opcode = VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE; + data_exchange_pkt.mode = VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &data_exchange_pkt); + if (ret < 0) { + pr_err("Failed to send data exchange mode %d\n", ret); + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: wait_event timeout %d\n", __func__, ret); + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_stream_mute_cmd(struct voice_data *v, uint16_t direction, + uint16_t mute_flag, uint32_t ramp_duration) +{ + struct cvs_set_mute_cmd cvs_mute_cmd; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!common.apr_q6_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + /* send mute/unmute to cvs */ + cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_mute_cmd) - APR_HDR_SIZE); + cvs_mute_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v); + cvs_mute_cmd.hdr.token = 0; + cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2; + cvs_mute_cmd.cvs_set_mute.direction = direction; + cvs_mute_cmd.cvs_set_mute.mute_flag = mute_flag; + cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = ramp_duration; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_mute_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending stream mute\n", __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_send_device_mute_cmd(struct voice_data *v, uint16_t direction, + uint16_t mute_flag, uint32_t ramp_duration) +{ + struct cvp_set_mute_cmd cvp_mute_cmd; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + cvp_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_mute_cmd) - APR_HDR_SIZE); + cvp_mute_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_mute_cmd.hdr.token = 0; + cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2; + cvp_mute_cmd.cvp_set_mute.direction = direction; + cvp_mute_cmd.cvp_set_mute.mute_flag = mute_flag; + cvp_mute_cmd.cvp_set_mute.ramp_duration_ms = ramp_duration; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending rx device cmd\n", __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_send_vol_step_cmd(struct voice_data *v) +{ + struct cvp_set_rx_volume_step_cmd cvp_vol_step_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + cvp_handle = voice_get_cvp_handle(v); + + /* send volume index to cvp */ + cvp_vol_step_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_vol_step_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_vol_step_cmd) - APR_HDR_SIZE); + cvp_vol_step_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_vol_step_cmd.hdr.dest_port = cvp_handle; + cvp_vol_step_cmd.hdr.token = 0; + cvp_vol_step_cmd.hdr.opcode = VSS_IVOLUME_CMD_SET_STEP; + cvp_vol_step_cmd.cvp_set_vol_step.direction = VSS_IVOLUME_DIRECTION_RX; + cvp_vol_step_cmd.cvp_set_vol_step.value = v->dev_rx.volume_step_value; + cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms = + v->dev_rx.volume_ramp_duration_ms; + pr_debug("%s step_value:%d, ramp_duration_ms:%d", + __func__, + cvp_vol_step_cmd.cvp_set_vol_step.value, + cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_step_cmd); + if (ret < 0) { + pr_err("Fail in sending RX VOL step\n"); + return -EINVAL; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + return ret; + } + return 0; +} + +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + struct cvs_start_record_cmd cvs_start_record; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (!v->rec_info.recording) { + cvs_start_record.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_record) - APR_HDR_SIZE); + cvs_start_record.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_start_record.hdr.dest_port = cvs_handle; + cvs_start_record.hdr.token = 0; + cvs_start_record.hdr.opcode = VSS_IRECORD_CMD_START; + + cvs_start_record.rec_mode.port_id = + VSS_IRECORD_PORT_ID_DEFAULT; + if (rec_mode == VOC_REC_UPLINK) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_IRECORD_TAP_POINT_NONE; + cvs_start_record.rec_mode.tx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + } else if (rec_mode == VOC_REC_DOWNLINK) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = + VSS_IRECORD_TAP_POINT_NONE; + } else if (rec_mode == VOC_REC_BOTH) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + } else { + pr_err("%s: Invalid in-call rec_mode %d\n", __func__, + rec_mode); + + ret = -EINVAL; + goto fail; + } + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record); + if (ret < 0) { + pr_err("%s: Error %d sending START_RECORD\n", __func__, + ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + v->rec_info.recording = 1; + } else { + pr_debug("%s: Start record already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_record(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct apr_hdr cvs_stop_record; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (v->rec_info.recording) { + cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_record) - APR_HDR_SIZE); + cvs_stop_record.src_port = + voice_get_idx_for_session(v->session_id); + cvs_stop_record.dest_port = cvs_handle; + cvs_stop_record.token = 0; + cvs_stop_record.opcode = VSS_IRECORD_CMD_STOP; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record); + if (ret < 0) { + pr_err("%s: Error %d sending STOP_RECORD\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + v->rec_info.recording = 0; + } else { + pr_debug("%s: Stop record already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id) +{ + int ret = 0; + int rec_mode = 0; + u16 cvs_handle; + int rec_set = 0; + struct voice_session_itr itr; + struct voice_data *v = NULL; + + /* check if session_id is valid */ + if (!voice_is_valid_session_id(session_id)) { + pr_err("%s: Invalid session id:%u\n", __func__, + session_id); + + return -EINVAL; + } + + voice_itr_init(&itr, session_id); + pr_debug("%s: session_id:%u\n", __func__, session_id); + + while (voice_itr_get_next_session(&itr, &v)) { + if (v == NULL) { + pr_err("%s: v is NULL, sessionid:%u\n", __func__, + session_id); + + break; + } + pr_debug("%s: port_id: %d, set: %d, v: %pK\n", + __func__, port_id, set, v); + + mutex_lock(&v->lock); + rec_mode = v->rec_info.rec_mode; + rec_set = set; + if (set) { + if ((v->rec_route_state.ul_flag != 0) && + (v->rec_route_state.dl_flag != 0)) { + pr_debug("%s: rec mode already set.\n", + __func__); + + mutex_unlock(&v->lock); + continue; + } + + if (port_id == VOICE_RECORD_TX) { + if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag == 0)) { + rec_mode = VOC_REC_UPLINK; + v->rec_route_state.ul_flag = 1; + } else if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag != 0)) { + voice_cvs_stop_record(v); + rec_mode = VOC_REC_BOTH; + v->rec_route_state.ul_flag = 1; + } + } else if (port_id == VOICE_RECORD_RX) { + if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag == 0)) { + rec_mode = VOC_REC_DOWNLINK; + v->rec_route_state.dl_flag = 1; + } else if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag == 0)) { + voice_cvs_stop_record(v); + rec_mode = VOC_REC_BOTH; + v->rec_route_state.dl_flag = 1; + } + } + rec_set = 1; + } else { + if ((v->rec_route_state.ul_flag == 0) && + (v->rec_route_state.dl_flag == 0)) { + pr_debug("%s: rec already stops.\n", + __func__); + mutex_unlock(&v->lock); + continue; + } + + if (port_id == VOICE_RECORD_TX) { + if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag == 0)) { + v->rec_route_state.ul_flag = 0; + rec_set = 0; + } else if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag != 0)) { + voice_cvs_stop_record(v); + v->rec_route_state.ul_flag = 0; + rec_mode = VOC_REC_DOWNLINK; + rec_set = 1; + } + } else if (port_id == VOICE_RECORD_RX) { + if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag != 0)) { + v->rec_route_state.dl_flag = 0; + rec_set = 0; + } else if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag != 0)) { + voice_cvs_stop_record(v); + v->rec_route_state.dl_flag = 0; + rec_mode = VOC_REC_UPLINK; + rec_set = 1; + } + } + } + pr_debug("%s: mode =%d, set =%d\n", __func__, + rec_mode, rec_set); + cvs_handle = voice_get_cvs_handle(v); + + if (cvs_handle != 0) { + if (rec_set) + ret = voice_cvs_start_record(v, rec_mode); + else + ret = voice_cvs_stop_record(v); + } + + /* During SRVCC, recording will switch from VoLTE session to + * voice session. + * Then stop recording, need to stop recording on voice session. + */ + if ((!rec_set) && common.srvcc_rec_flag) { + pr_debug("%s, srvcc_rec_flag:%d\n", __func__, + common.srvcc_rec_flag); + + voice_cvs_stop_record(&common.voice[VOC_PATH_PASSIVE]); + common.srvcc_rec_flag = false; + } + + /* Cache the value */ + v->rec_info.rec_enable = rec_set; + v->rec_info.rec_mode = rec_mode; + + mutex_unlock(&v->lock); + } + + return ret; +} + +static int voice_cvs_start_playback(struct voice_data *v) +{ + int ret = 0; + struct cvs_start_playback_cmd cvs_start_playback; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (!v->music_info.playing && v->music_info.count) { + cvs_start_playback.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_start_playback.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_playback) - APR_HDR_SIZE); + cvs_start_playback.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_start_playback.hdr.dest_port = cvs_handle; + cvs_start_playback.hdr.token = 0; + cvs_start_playback.hdr.opcode = VSS_IPLAYBACK_CMD_START; + cvs_start_playback.playback_mode.port_id = + v->music_info.port_id; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback); + + if (ret < 0) { + pr_err("%s: Error %d sending START_PLAYBACK\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + v->music_info.playing = 1; + } else { + pr_debug("%s: Start playback already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_playback(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvs_stop_playback; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (v->music_info.playing && ((!v->music_info.count) || + (v->music_info.force))) { + cvs_stop_playback.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_playback) - APR_HDR_SIZE); + cvs_stop_playback.src_port = + voice_get_idx_for_session(v->session_id); + cvs_stop_playback.dest_port = cvs_handle; + cvs_stop_playback.token = 0; + + cvs_stop_playback.opcode = VSS_IPLAYBACK_CMD_STOP; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback); + if (ret < 0) { + pr_err("%s: Error %d sending STOP_PLAYBACK\n", + __func__, ret); + + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + v->music_info.playing = 0; + v->music_info.force = 0; + } else { + pr_debug("%s: Stop playback already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +static int voc_lch_ops(struct voice_data *v, enum voice_lch_mode lch_mode) +{ + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + switch (lch_mode) { + case VOICE_LCH_START: + + ret = voc_end_voice_call(v->session_id); + if (ret < 0) + pr_err("%s: voice call end failed %d\n", + __func__, ret); + break; + case VOICE_LCH_STOP: + + ret = voc_start_voice_call(v->session_id); + if (ret < 0) { + pr_err("%s: voice call start failed %d\n", + __func__, ret); + goto done; + } + break; + default: + pr_err("%s: Invalid LCH mode: %d\n", + __func__, v->lch_mode); + break; + } +done: + return ret; +} + +int voc_start_playback(uint32_t set, uint16_t port_id) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + u16 cvs_handle; + + pr_debug("%s port_id = %#x set = %d", __func__, port_id, set); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if ((v != NULL) && + (((port_id == VOICE_PLAYBACK_TX) && + is_sub1_vsid(v->session_id)) || + ((port_id == VOICE2_PLAYBACK_TX) && + is_sub2_vsid(v->session_id)))) { + + mutex_lock(&v->lock); + v->music_info.port_id = port_id; + v->music_info.play_enable = set; + if (set) + v->music_info.count++; + else + v->music_info.count--; + pr_debug("%s: music_info count=%d\n", __func__, + v->music_info.count); + + cvs_handle = voice_get_cvs_handle(v); + if (cvs_handle != 0) { + if (set) + ret = voice_cvs_start_playback(v); + else + ret = voice_cvs_stop_playback(v); + } + mutex_unlock(&v->lock); + } else { + pr_err("%s: Invalid session\n", __func__); + } + } + + return ret; +} + +int voc_disable_topology(uint32_t session_id, uint32_t disable) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + v->disable_topology = disable; + + mutex_unlock(&v->lock); + + return ret; +} + +static int voice_set_packet_exchange_mode_and_config(uint32_t session_id, + uint32_t mode) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + return -EINVAL; + } + + if (v->voc_state != VOC_RUN) + ret = voice_send_cvs_data_exchange_mode_cmd(v); + + if (ret) { + pr_err("%s: Error voice_send_data_exchange_mode_cmd %d\n", + __func__, ret); + goto fail; + } + + ret = voice_send_cvs_packet_exchange_config_cmd(v); + if (ret) { + pr_err("%s: Error: voice_send_packet_exchange_config_cmd %d\n", + __func__, ret); + goto fail; + } + + return ret; +fail: + return -EINVAL; +} + +int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + v->stream_tx.stream_mute = mute; + v->stream_tx.stream_mute_ramp_duration_ms = + ramp_duration; + if (is_voc_state_active(v->voc_state) && + (v->lch_mode == 0)) + ret = voice_send_stream_mute_cmd(v, + VSS_IVOLUME_DIRECTION_TX, + v->stream_tx.stream_mute, + v->stream_tx.stream_mute_ramp_duration_ms); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + + ret = -EINVAL; + break; + } + } + + return ret; +} + +int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (dir == VSS_IVOLUME_DIRECTION_TX) { + v->dev_tx.dev_mute = mute; + v->dev_tx.dev_mute_ramp_duration_ms = + ramp_duration; + } else { + v->dev_rx.dev_mute = mute; + v->dev_rx.dev_mute_ramp_duration_ms = + ramp_duration; + } + + if (((v->voc_state == VOC_RUN) || + (v->voc_state == VOC_STANDBY)) && + (v->lch_mode == 0)) + ret = voice_send_device_mute_cmd(v, + dir, + mute, + ramp_duration); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + + ret = -EINVAL; + break; + } + } + + return ret; +} + +int voc_get_rx_device_mute(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + ret = v->dev_rx.dev_mute; + + mutex_unlock(&v->lock); + + return ret; +} + +int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + v->tty_mode = tty_mode; + + mutex_unlock(&v->lock); + + return ret; +} + +uint8_t voc_get_tty_mode(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + ret = v->tty_mode; + + mutex_unlock(&v->lock); + + return ret; +} + +int voc_set_pp_enable(uint32_t session_id, uint32_t module_id, uint32_t enable) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + if (!(is_voice_app_id(v->session_id))) + continue; + + mutex_lock(&v->lock); + if (module_id == MODULE_ID_VOICE_MODULE_ST) + v->st_enable = enable; + + if (v->voc_state == VOC_RUN) { + if ((module_id == MODULE_ID_VOICE_MODULE_ST) && + (!v->tty_mode)) + ret = voice_send_set_pp_enable_cmd(v, + MODULE_ID_VOICE_MODULE_ST, + enable); + } + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + ret = -EINVAL; + break; + } + } + + return ret; +} + +int voc_set_hd_enable(uint32_t session_id, uint32_t enable) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + v->hd_enable = enable; + + if (v->voc_state == VOC_RUN) + ret = voice_send_hd_cmd(v, enable); + + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + ret = -EINVAL; + break; + } + } + + return ret; +} + +int voc_get_pp_enable(uint32_t session_id, uint32_t module_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + if (module_id == MODULE_ID_VOICE_MODULE_ST) + ret = v->st_enable; + mutex_unlock(&v->lock); + + return ret; +} + +int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step, + uint32_t ramp_duration) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + pr_debug("%s session id = %#x vol = %u", __func__, session_id, + vol_step); + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + v->dev_rx.volume_step_value = vol_step; + v->dev_rx.volume_ramp_duration_ms = ramp_duration; + if (is_voc_state_active(v->voc_state)) + ret = voice_send_vol_step_cmd(v); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + + ret = -EINVAL; + break; + } + } + + return ret; +} + +int voc_set_device_config(uint32_t session_id, uint8_t path_dir, + uint8_t no_of_channels, uint32_t port_id) +{ + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + pr_debug("%s: path_dir=%d port_id=%x, channels=%d\n", + __func__, path_dir, port_id, no_of_channels); + + mutex_lock(&v->lock); + if (path_dir == RX_PATH) { + v->dev_rx.port_id = q6audio_get_port_id(port_id); + v->dev_rx.no_of_channels = no_of_channels; + } else { + v->dev_tx.port_id = q6audio_get_port_id(port_id); + v->dev_tx.no_of_channels = no_of_channels; + } + mutex_unlock(&v->lock); + + return 0; +} + +int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set) +{ + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + pr_debug("%s: path_dir=%d, set=%d\n", __func__, path_dir, set); + + mutex_lock(&v->lock); + + if (path_dir == RX_PATH) + v->voc_route_state.rx_route_flag = set; + else + v->voc_route_state.tx_route_flag = set; + + mutex_unlock(&v->lock); + + return 0; +} + +uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return 0; + } + + mutex_lock(&v->lock); + + if (path_dir == RX_PATH) + ret = v->voc_route_state.rx_route_flag; + else + ret = v->voc_route_state.tx_route_flag; + + mutex_unlock(&v->lock); + + return ret; +} + +int voc_end_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN || v->voc_state == VOC_ERROR || + v->voc_state == VOC_CHANGE || v->voc_state == VOC_STANDBY) { + + pr_debug("%s: VOC_STATE: %d\n", __func__, v->voc_state); + + ret = voice_destroy_vocproc(v); + if (ret < 0) + pr_err("%s: destroy voice failed\n", __func__); + + voc_update_session_params(v); + + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_RELEASE; + if (common.is_vote_bms) { + /* vote low power to BMS during call stop */ + voice_vote_powerstate_to_bms(v, false); + } + } else { + pr_err("%s: Error: End voice called in state %d\n", + __func__, v->voc_state); + + ret = -EINVAL; + } + + mutex_unlock(&v->lock); + return ret; +} + +int voc_standby_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + struct apr_hdr mvm_standby_voice_cmd; + void *apr_mvm; + u16 mvm_handle; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: voc state=%d", __func__, v->voc_state); + + if (v->voc_state == VOC_RUN) { + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + mvm_handle = voice_get_mvm_handle(v); + mvm_standby_voice_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_standby_voice_cmd.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_standby_voice_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_standby_voice_cmd pkt size = %d\n", + mvm_standby_voice_cmd.pkt_size); + mvm_standby_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_standby_voice_cmd.dest_port = mvm_handle; + mvm_standby_voice_cmd.token = 0; + mvm_standby_voice_cmd.opcode = VSS_IMVM_CMD_STANDBY_VOICE; + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, + (uint32_t *)&mvm_standby_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_STANDBY_VOICE\n"); + ret = -EINVAL; + goto fail; + } + v->voc_state = VOC_STANDBY; + } +fail: + return ret; +} + +int voc_disable_device(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: voc state=%d\n", __func__, v->voc_state); + + mutex_lock(&v->lock); + if (v->voc_state == VOC_RUN) { + ret = voice_pause_voice_call(v); + if (ret < 0) { + pr_err("%s: Pause Voice Call failed for session 0x%x, err %d!\n", + __func__, v->session_id, ret); + goto done; + } + rtac_remove_voice(voice_get_cvs_handle(v)); + voice_send_cvp_deregister_vol_cal_cmd(v); + voice_send_cvp_deregister_cal_cmd(v); + voice_send_cvp_deregister_dev_cfg_cmd(v); + + v->voc_state = VOC_CHANGE; + } else { + pr_debug("%s: called in voc state=%d, No_OP\n", + __func__, v->voc_state); + } + +done: + mutex_unlock(&v->lock); + + return ret; +} + +int voc_enable_device(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: voc state=%d\n", __func__, v->voc_state); + mutex_lock(&v->lock); + if (v->voc_state == VOC_CHANGE) { + ret = voice_send_tty_mode_cmd(v); + if (ret < 0) { + pr_err("%s: Sending TTY mode failed, ret=%d\n", + __func__, ret); + /* Not a critical error, allow voice call to continue */ + } + + if (v->tty_mode) { + /* disable slowtalk */ + voice_send_set_pp_enable_cmd(v, + MODULE_ID_VOICE_MODULE_ST, + 0); + } else { + /* restore slowtalk */ + voice_send_set_pp_enable_cmd(v, + MODULE_ID_VOICE_MODULE_ST, + v->st_enable); + } + + ret = voice_send_set_device_cmd(v); + if (ret < 0) { + pr_err("%s: Set device failed, ret=%d\n", + __func__, ret); + goto done; + } + + ret = voice_send_cvp_device_channels_cmd(v); + if (ret < 0) { + pr_err("%s: Set device channels failed\n", __func__); + goto done; + } + + ret = voice_send_cvp_topology_commit_cmd(v); + if (ret < 0) { + pr_err("%s: Set topology commit failed\n", __func__); + goto done; + } + + voice_send_cvp_register_dev_cfg_cmd(v); + voice_send_cvp_register_cal_cmd(v); + voice_send_cvp_register_vol_cal_cmd(v); + + rtac_add_voice(voice_get_cvs_handle(v), + voice_get_cvp_handle(v), + v->dev_rx.port_id, v->dev_tx.port_id, + v->dev_rx.dev_id, v->dev_tx.dev_id, + v->session_id); + + ret = voice_send_start_voice_cmd(v); + if (ret < 0) { + pr_err("%s: Fail in sending START_VOICE, ret=%d\n", + __func__, ret); + goto done; + } + v->voc_state = VOC_RUN; + } else { + pr_debug("%s: called in voc state=%d, No_OP\n", + __func__, v->voc_state); + } + +done: + mutex_unlock(&v->lock); + + return ret; +} + +int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&v->lock); + if (v->lch_mode == lch_mode) { + pr_debug("%s: Session %d already in LCH mode %d\n", + __func__, session_id, lch_mode); + + mutex_unlock(&v->lock); + goto done; + } + + v->lch_mode = lch_mode; + mutex_unlock(&v->lock); + + ret = voc_lch_ops(v, v->lch_mode); + if (ret < 0) { + pr_err("%s: lch ops failed %d\n", __func__, ret); + goto done; + } + +done: + return ret; +} + +int voc_resume_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + ret = voice_send_start_voice_cmd(v); + if (ret < 0) { + pr_err("Fail in sending START_VOICE\n"); + goto fail; + } + v->voc_state = VOC_RUN; + return 0; +fail: + return -EINVAL; +} + +int voc_start_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_ERROR) { + pr_debug("%s: VOC in ERR state\n", __func__); + + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_INIT; + } + + if ((v->voc_state == VOC_INIT) || + (v->voc_state == VOC_RELEASE)) { + ret = voice_apr_register(session_id); + if (ret < 0) { + pr_err("%s: apr register failed\n", __func__); + goto fail; + } + + if (is_cvd_version_queried()) { + pr_debug("%s: Returning the cached value %s\n", + __func__, common.cvd_version); + } else { + ret = voice_send_mvm_cvd_version_cmd(v); + if (ret < 0) + pr_debug("%s: Error retrieving CVD version %d\n", + __func__, ret); + } + + ret = voice_create_mvm_cvs_session(v); + if (ret < 0) { + pr_err("create mvm and cvs failed\n"); + goto fail; + } + + if (is_voip_session(session_id)) { + /* Allocate oob mem if not already allocated and + * memory map the oob memory block. + */ + ret = voice_alloc_and_map_oob_mem(v); + if (ret < 0) { + pr_err("%s: voice_alloc_and_map_oob_mem() failed, ret:%d\n", + __func__, ret); + + goto fail; + } + + ret = voice_set_packet_exchange_mode_and_config( + session_id, + VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND); + if (ret) { + pr_err("%s: Err: exchange_mode_and_config %d\n", + __func__, ret); + + goto fail; + } + } + ret = voice_send_dual_control_cmd(v); + if (ret < 0) { + pr_err("Err Dual command failed\n"); + goto fail; + } + ret = voice_setup_vocproc(v); + if (ret < 0) { + pr_err("setup voice failed\n"); + goto fail; + } + + ret = voice_send_vol_step_cmd(v); + if (ret < 0) + pr_err("voice volume failed\n"); + + ret = voice_send_stream_mute_cmd(v, + VSS_IVOLUME_DIRECTION_TX, + v->stream_tx.stream_mute, + v->stream_tx.stream_mute_ramp_duration_ms); + if (ret < 0) + pr_err("voice mute failed\n"); + + ret = voice_send_start_voice_cmd(v); + if (ret < 0) { + pr_err("start voice failed\n"); + goto fail; + } + + v->voc_state = VOC_RUN; + } else { + pr_err("%s: Error: Start voice called in state %d\n", + __func__, v->voc_state); + + ret = -EINVAL; + goto fail; + } +fail: + mutex_unlock(&v->lock); + return ret; +} + +int voc_set_ext_ec_ref(uint16_t port_id, bool state) +{ + int ret = 0; + + mutex_lock(&common.common_lock); + if (state == true) { + if (port_id == AFE_PORT_INVALID) { + pr_err("%s: Invalid port id", __func__); + ret = -EINVAL; + goto exit; + } + common.ec_port_id = port_id; + common.ec_ref_ext = true; + } else { + common.ec_ref_ext = false; + common.ec_port_id = port_id; + } +exit: + mutex_unlock(&common.common_lock); + return ret; +} + +void voc_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + voip_ssr_cb ssr_cb, + void *private_data) +{ + common.mvs_info.ul_cb = ul_cb; + common.mvs_info.dl_cb = dl_cb; + common.mvs_info.ssr_cb = ssr_cb; + common.mvs_info.private_data = private_data; +} + +void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb, + void *private_data) +{ + common.dtmf_info.dtmf_rx_ul_cb = dtmf_rx_ul_cb; + common.dtmf_info.private_data = private_data; +} + +void voc_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode, + uint32_t evrc_min_rate, + uint32_t evrc_max_rate) +{ + common.mvs_info.media_type = media_type; + common.mvs_info.rate = rate; + common.mvs_info.network_type = network_type; + common.mvs_info.dtx_mode = dtx_mode; + common.mvs_info.evrc_min_rate = evrc_min_rate; + common.mvs_info.evrc_max_rate = evrc_max_rate; +} + +static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr = NULL; + struct common_data *c = NULL; + struct voice_data *v = NULL; + int i = 0; + struct vss_iversion_rsp_get_t *version_rsp = NULL; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + return -EINVAL; + } + + c = priv; + + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event received in Voice service\n", + __func__); + + if (common.mvs_info.ssr_cb) { + pr_debug("%s: Informing reset event to VoIP\n", + __func__); + common.mvs_info.ssr_cb(data->opcode, + common.mvs_info.private_data); + } + + apr_reset(c->apr_q6_mvm); + c->apr_q6_mvm = NULL; + + /* clean up memory handle */ + c->cal_mem_handle = 0; + c->rtac_mem_handle = 0; + cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES, + common.cal_data); + rtac_clear_mapping(VOICE_RTAC_CAL); + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + c->voice[i].mvm_handle = 0; + c->voice[i].shmem_info.mem_handle = 0; + } + + /* Free the ION memory and clear handles for Source Tracking */ + if (is_source_tracking_shared_memomry_allocated()) { + msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.client, + common.source_tracking_sh_mem.sh_mem_block.handle); + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.client = + NULL; + common.source_tracking_sh_mem.sh_mem_block.handle = + NULL; + } + /* clean up srvcc rec flag */ + c->srvcc_rec_flag = false; + voc_set_error_state(data->reset_proc); + return 0; + } + + pr_debug("%s: session_idx 0x%x\n", __func__, data->dest_port); + + v = voice_get_session_by_idx(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + /* ping mvm service ACK */ + switch (ptr[0]) { + case VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION: + case VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION: + /* Passive session is used for CS call + * Full session is used for VoIP call. + */ + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + pr_debug("%s: MVM handle is %d\n", + __func__, data->src_port); + voice_set_mvm_handle(v, data->src_port); + } else + pr_err("got NACK for sending MVM create session\n"); + v->mvm_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->mvm_wait); + break; + case VSS_IMVM_CMD_START_VOICE: + case VSS_IMVM_CMD_ATTACH_VOCPROC: + case VSS_IMVM_CMD_STOP_VOICE: + case VSS_IMVM_CMD_DETACH_VOCPROC: + case VSS_ISTREAM_CMD_SET_TTY_MODE: + case APRV2_IBASIC_CMD_DESTROY_SESSION: + case VSS_IMVM_CMD_ATTACH_STREAM: + case VSS_IMVM_CMD_DETACH_STREAM: + case VSS_ICOMMON_CMD_SET_NETWORK: + case VSS_ICOMMON_CMD_SET_VOICE_TIMING: + case VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL: + case VSS_IMVM_CMD_SET_CAL_NETWORK: + case VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE: + case VSS_IMEMORY_CMD_MAP_PHYSICAL: + case VSS_IMEMORY_CMD_UNMAP: + case VSS_IMVM_CMD_PAUSE_VOICE: + case VSS_IMVM_CMD_STANDBY_VOICE: + case VSS_IHDVOICE_CMD_ENABLE: + case VSS_IHDVOICE_CMD_DISABLE: + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->mvm_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->mvm_wait); + break; + case VSS_IVERSION_CMD_GET: + pr_debug("%s: Error retrieving CVD Version, error:%d\n", + __func__, ptr[1]); + + strlcpy(common.cvd_version, CVD_VERSION_0_0, + sizeof(common.cvd_version)); + pr_debug("%s: Fall back to default value, CVD Version = %s\n", + __func__, common.cvd_version); + + v->mvm_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->mvm_wait); + break; + default: + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + break; + } + } + } else if (data->opcode == VSS_IMEMORY_RSP_MAP) { + pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__); + + if (data->payload_size && data->token == VOIP_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + v->shmem_info.mem_handle = ptr[0]; + pr_debug("%s: shared mem_handle: 0x[%x]\n", + __func__, v->shmem_info.mem_handle); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_CAL_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + c->cal_mem_handle = ptr[0]; + + pr_debug("%s: cal mem handle 0x%x\n", + __func__, c->cal_mem_handle); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_VOICE_HOST_PCM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + common.voice_host_pcm_mem_handle = ptr[0]; + + pr_debug("%s: vhpcm mem handle 0x%x\n", + __func__, + common.voice_host_pcm_mem_handle); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_RTAC_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + c->rtac_mem_handle = ptr[0]; + + pr_debug("%s: cal mem handle 0x%x\n", + __func__, c->rtac_mem_handle); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_SOURCE_TRACKING_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + common.source_tracking_sh_mem.mem_handle = + ptr[0]; + + pr_debug("%s: Source Tracking shared mem handle 0x%x\n", + __func__, + common.source_tracking_sh_mem.mem_handle); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else { + pr_err("%s: Unknown mem map token %d\n", + __func__, data->token); + } + } else if (data->opcode == VSS_IVERSION_RSP_GET) { + pr_debug("%s: Received VSS_IVERSION_RSP_GET\n", __func__); + + if (data->payload_size) { + version_rsp = + (struct vss_iversion_rsp_get_t *)data->payload; + memcpy(common.cvd_version, version_rsp->version, + CVD_VERSION_STRING_MAX_SIZE); + pr_debug("%s: CVD Version = %s\n", + __func__, common.cvd_version); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } + return 0; +} + +static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr = NULL; + struct common_data *c = NULL; + struct voice_data *v = NULL; + int i = 0; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + return -EINVAL; + } + + c = priv; + + pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event received in Voice service\n", + __func__); + + apr_reset(c->apr_q6_cvs); + c->apr_q6_cvs = NULL; + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].cvs_handle = 0; + + cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES, + common.cal_data); + + /* Free the ION memory and clear handles for Source Tracking */ + if (is_source_tracking_shared_memomry_allocated()) { + msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.client, + common.source_tracking_sh_mem.sh_mem_block.handle); + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.client = + NULL; + common.source_tracking_sh_mem.sh_mem_block.handle = + NULL; + } + voc_set_error_state(data->reset_proc); + return 0; + } + + v = voice_get_session_by_idx(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + if (ptr[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, ptr[0], ptr[1]); + } + /*response from CVS */ + switch (ptr[0]) { + case VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION: + case VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION: + if (!ptr[1]) { + pr_debug("%s: CVS handle is %d\n", + __func__, data->src_port); + voice_set_cvs_handle(v, data->src_port); + } else + pr_err("got NACK for sending CVS create session\n"); + v->cvs_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvs_wait); + break; + case VSS_IVOLUME_CMD_MUTE_V2: + case VSS_ISTREAM_CMD_SET_MEDIA_TYPE: + case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE: + case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE: + case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE: + case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE: + case APRV2_IBASIC_CMD_DESTROY_SESSION: + case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2: + case VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA: + case VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA: + case VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA: + case VSS_ICOMMON_CMD_MAP_MEMORY: + case VSS_ICOMMON_CMD_UNMAP_MEMORY: + case VSS_ICOMMON_CMD_SET_UI_PROPERTY: + case VSS_IPLAYBACK_CMD_START: + case VSS_IPLAYBACK_CMD_STOP: + case VSS_IRECORD_CMD_START: + case VSS_IRECORD_CMD_STOP: + case VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE: + case VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG: + case VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION: + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->cvs_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvs_wait); + break; + case VOICE_CMD_SET_PARAM: + pr_debug("%s: VOICE_CMD_SET_PARAM\n", __func__); + rtac_make_voice_callback(RTAC_CVS, ptr, + data->payload_size); + break; + case VOICE_CMD_GET_PARAM: + pr_debug("%s: VOICE_CMD_GET_PARAM\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* VOICE_EVT_GET_PARAM_ACK */ + if (ptr[1] != 0) { + pr_err("%s: CVP get param error = %d, resuming\n", + __func__, ptr[1]); + rtac_make_voice_callback(RTAC_CVP, + data->payload, + data->payload_size); + } + break; + default: + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + break; + } + } + } else if (data->opcode == + VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY) { + int ret = 0; + u16 cvs_handle; + uint32_t *cvs_voc_pkt; + struct cvs_enc_buffer_consumed_cmd send_enc_buf_consumed_cmd; + void *apr_cvs; + + pr_debug("Encoder buffer is ready\n"); + + apr_cvs = common.apr_q6_cvs; + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + send_enc_buf_consumed_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + send_enc_buf_consumed_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(send_enc_buf_consumed_cmd) - APR_HDR_SIZE); + + send_enc_buf_consumed_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + send_enc_buf_consumed_cmd.hdr.dest_port = cvs_handle; + send_enc_buf_consumed_cmd.hdr.token = 0; + send_enc_buf_consumed_cmd.hdr.opcode = + VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED; + + cvs_voc_pkt = v->shmem_info.sh_buf.buf[1].data; + if (cvs_voc_pkt != NULL && common.mvs_info.ul_cb != NULL) { + /* cvs_voc_pkt[0] contains tx timestamp */ + common.mvs_info.ul_cb((uint8_t *)&cvs_voc_pkt[3], + cvs_voc_pkt[2], + cvs_voc_pkt[0], + common.mvs_info.private_data); + } else + pr_err("%s: cvs_voc_pkt or ul_cb is NULL\n", __func__); + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &send_enc_buf_consumed_cmd); + if (ret < 0) { + pr_err("%s: Err send ENC_BUF_CONSUMED_NOTIFY %d\n", + __func__, ret); + goto fail; + } + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) { + pr_debug("Recd VSS_ISTREAM_EVT_SEND_ENC_BUFFER\n"); + } else if (data->opcode == + VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST) { + int ret = 0; + u16 cvs_handle; + uint32_t *cvs_voc_pkt; + struct cvs_dec_buffer_ready_cmd send_dec_buf; + void *apr_cvs; + + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + send_dec_buf.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(send_dec_buf) - APR_HDR_SIZE); + + send_dec_buf.hdr.src_port = + voice_get_idx_for_session(v->session_id); + send_dec_buf.hdr.dest_port = cvs_handle; + send_dec_buf.hdr.token = 0; + send_dec_buf.hdr.opcode = + VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY; + + cvs_voc_pkt = (uint32_t *)(v->shmem_info.sh_buf.buf[0].data); + if (cvs_voc_pkt != NULL && common.mvs_info.dl_cb != NULL) { + /* Set timestamp to 0 and advance the pointer */ + cvs_voc_pkt[0] = 0; + /* Set media_type and advance the pointer */ + cvs_voc_pkt[1] = common.mvs_info.media_type; + common.mvs_info.dl_cb( + (uint8_t *)&cvs_voc_pkt[2], + common.mvs_info.private_data); + ret = apr_send_pkt(apr_cvs, (uint32_t *) &send_dec_buf); + if (ret < 0) { + pr_err("%s: Err send DEC_BUF_READY_NOTIFI %d\n", + __func__, ret); + goto fail; + } + } else { + pr_debug("%s: voc_pkt or dl_cb is NULL\n", __func__); + goto fail; + } + } else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) { + pr_debug("Recd VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER\n"); + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER) { + pr_debug("Send dec buf resp\n"); + } else if (data->opcode == APR_RSP_ACCEPTED) { + ptr = data->payload; + if (ptr[0]) + pr_debug("%s: APR_RSP_ACCEPTED for 0x%x:\n", + __func__, ptr[0]); + } else if (data->opcode == VSS_ISTREAM_EVT_NOT_READY) { + pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n"); + } else if (data->opcode == VSS_ISTREAM_EVT_READY) { + pr_debug("Recd VSS_ISTREAM_EVT_READY\n"); + } else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) { + pr_debug("%s: VOICE_EVT_GET_PARAM_ACK\n", __func__); + ptr = data->payload; + if (ptr[0] != 0) { + pr_err("%s: VOICE_EVT_GET_PARAM_ACK returned error = 0x%x\n", + __func__, ptr[0]); + } + rtac_make_voice_callback(RTAC_CVS, data->payload, + data->payload_size); + } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) { + struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected; + uint32_t *voc_pkt = data->payload; + uint32_t pkt_len = data->payload_size; + + if ((voc_pkt != NULL) && + (pkt_len == + sizeof(struct vss_istream_evt_rx_dtmf_detected))) { + + dtmf_rx_detected = + (struct vss_istream_evt_rx_dtmf_detected *) voc_pkt; + pr_debug("RX_DTMF_DETECTED low_freq=%d high_freq=%d\n", + dtmf_rx_detected->low_freq, + dtmf_rx_detected->high_freq); + if (c->dtmf_info.dtmf_rx_ul_cb) + c->dtmf_info.dtmf_rx_ul_cb((uint8_t *)voc_pkt, + voc_get_session_name(v->session_id), + c->dtmf_info.private_data); + } else { + pr_err("Invalid packet\n"); + } + } else + pr_debug("Unknown opcode 0x%x\n", data->opcode); + +fail: + return 0; +} + +static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr = NULL; + struct common_data *c = NULL; + struct voice_data *v = NULL; + int i = 0; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + return -EINVAL; + } + + c = priv; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event received in Voice service\n", + __func__); + + apr_reset(c->apr_q6_cvp); + c->apr_q6_cvp = NULL; + cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES, + common.cal_data); + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].cvp_handle = 0; + + /* + * Free the ION memory and clear handles for + * Source Tracking + */ + if (is_source_tracking_shared_memomry_allocated()) { + msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.client, + common.source_tracking_sh_mem.sh_mem_block.handle); + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.client = + NULL; + common.source_tracking_sh_mem.sh_mem_block.handle = + NULL; + } + voc_set_error_state(data->reset_proc); + return 0; + } + + v = voice_get_session_by_idx(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + if (ptr[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, ptr[0], ptr[1]); + } + switch (ptr[0]) { + case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2: + case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3: + /*response from CVP */ + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + voice_set_cvp_handle(v, data->src_port); + pr_debug("status: %d, cvphdl=%d\n", + ptr[1], data->src_port); + } else + pr_err("got NACK from CVP create session response\n"); + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_IVOCPROC_CMD_SET_DEVICE_V2: + case VSS_IVOCPROC_CMD_SET_DEVICE_V3: + case VSS_IVOLUME_CMD_SET_STEP: + case VSS_IVOCPROC_CMD_ENABLE: + case VSS_IVOCPROC_CMD_DISABLE: + case APRV2_IBASIC_CMD_DESTROY_SESSION: + case VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2: + case VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG: + case VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG: + case VSS_ICOMMON_CMD_MAP_MEMORY: + case VSS_ICOMMON_CMD_UNMAP_MEMORY: + case VSS_IVOLUME_CMD_MUTE_V2: + case VSS_IVPCM_CMD_START_V2: + case VSS_IVPCM_CMD_STOP: + case VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS: + case VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT: + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_IVPCM_EVT_PUSH_BUFFER_V2: + break; + case VOICE_CMD_SET_PARAM: + pr_debug("%s: VOICE_CMD_SET_PARAM\n", __func__); + rtac_make_voice_callback(RTAC_CVP, ptr, + data->payload_size); + break; + case VOICE_CMD_GET_PARAM: + pr_debug("%s: VOICE_CMD_GET_PARAM\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* VOICE_EVT_GET_PARAM_ACK */ + if (ptr[1] != 0) { + pr_err("%s: CVP get param error = %d, resuming\n", + __func__, ptr[1]); + rtac_make_voice_callback(RTAC_CVP, + data->payload, + data->payload_size); + } + break; + case VSS_ISOUNDFOCUS_CMD_SET_SECTORS: + if (!ptr[1]) + common.is_sound_focus_resp_success = + true; + else + common.is_sound_focus_resp_success = + false; + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_ISOUNDFOCUS_CMD_GET_SECTORS: + /* + * Should only come here if there is an error + * response received from ADSP. Otherwise + * response will be returned as + * VSS_ISOUNDFOCUS_RSP_GET_SECTORS + */ + pr_err("%s: VSS_ISOUNDFOCUS_CMD_GET_SECTORS failed\n", + __func__); + + common.is_sound_focus_resp_success = false; + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_ISOURCETRACK_CMD_GET_ACTIVITY: + if (!ptr[1]) { + /* Read data from shared memory */ + memcpy(&common.sourceTrackingResponse, + common.source_tracking_sh_mem. + sh_mem_block.data, + sizeof(struct + vss_isourcetrack_activity_data_t)); + common.is_source_tracking_resp_success = + true; + } else { + common.is_source_tracking_resp_success = + false; + pr_err("%s: Error received for source tracking params\n", + __func__); + } + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + default: + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + break; + } + } + } else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) { + pr_debug("%s: VOICE_EVT_GET_PARAM_ACK\n", __func__); + ptr = data->payload; + if (ptr[0] != 0) { + pr_err("%s: VOICE_EVT_GET_PARAM_ACK returned error = 0x%x\n", + __func__, ptr[0]); + } + rtac_make_voice_callback(RTAC_CVP, data->payload, + data->payload_size); + } else if (data->opcode == VSS_IVPCM_EVT_NOTIFY_V2) { + if ((data->payload != NULL) && data->payload_size == + sizeof(struct vss_ivpcm_evt_notify_v2_t) && + common.hostpcm_info.hostpcm_evt_cb != NULL) { + common.hostpcm_info.hostpcm_evt_cb(data->payload, + voc_get_session_name(v->session_id), + common.hostpcm_info.private_data); + } + } else if (data->opcode == VSS_ISOUNDFOCUS_RSP_GET_SECTORS) { + if (data->payload && (data->payload_size == + sizeof(struct vss_isoundfocus_rsp_get_sectors_t))) { + common.is_sound_focus_resp_success = true; + memcpy(&common.soundFocusResponse, + (struct vss_isoundfocus_rsp_get_sectors_t *) + data->payload, + sizeof(struct + vss_isoundfocus_rsp_get_sectors_t)); + } else { + common.is_sound_focus_resp_success = false; + pr_debug("%s: Invalid payload received from CVD\n", + __func__); + } + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } + return 0; +} + +static int voice_free_oob_shared_mem(void) +{ + int rc = 0; + int cnt = 0; + int bufcnt = NUM_OF_BUFFERS; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + mutex_lock(&common.common_lock); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + rc = -EINVAL; + goto done; + } + + rc = msm_audio_ion_free(v->shmem_info.sh_buf.client, + v->shmem_info.sh_buf.handle); + v->shmem_info.sh_buf.client = NULL; + v->shmem_info.sh_buf.handle = NULL; + if (rc < 0) { + pr_err("%s: Error:%d freeing memory\n", __func__, rc); + + goto done; + } + + + while (cnt < bufcnt) { + v->shmem_info.sh_buf.buf[cnt].data = NULL; + v->shmem_info.sh_buf.buf[cnt].phys = 0; + cnt++; + } + + v->shmem_info.sh_buf.client = NULL; + v->shmem_info.sh_buf.handle = NULL; + +done: + mutex_unlock(&common.common_lock); + return rc; +} + +static int voice_alloc_oob_shared_mem(void) +{ + int cnt = 0; + int rc = 0; + size_t len; + void *mem_addr; + dma_addr_t phys; + int bufsz = BUFFER_BLOCK_SIZE; + int bufcnt = NUM_OF_BUFFERS; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + mutex_lock(&common.common_lock); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + rc = -EINVAL; + goto done; + } + + rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.sh_buf.client), + &(v->shmem_info.sh_buf.handle), + bufsz*bufcnt, + &phys, &len, + &mem_addr); + if (rc < 0) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, rc); + + goto done; + } + + while (cnt < bufcnt) { + v->shmem_info.sh_buf.buf[cnt].data = mem_addr + (cnt * bufsz); + v->shmem_info.sh_buf.buf[cnt].phys = phys + (cnt * bufsz); + v->shmem_info.sh_buf.buf[cnt].size = bufsz; + cnt++; + } + + pr_debug("%s buf[0].data:[%pK], buf[0].phys:[%pK], &buf[0].phys:[%pK],\n", + __func__, + (void *)v->shmem_info.sh_buf.buf[0].data, + &v->shmem_info.sh_buf.buf[0].phys, + (void *)&v->shmem_info.sh_buf.buf[0].phys); + pr_debug("%s: buf[1].data:[%pK], buf[1].phys[%pK], &buf[1].phys[%pK]\n", + __func__, + (void *)v->shmem_info.sh_buf.buf[1].data, + &v->shmem_info.sh_buf.buf[1].phys, + (void *)&v->shmem_info.sh_buf.buf[1].phys); + + memset((void *)v->shmem_info.sh_buf.buf[0].data, 0, (bufsz * bufcnt)); + +done: + mutex_unlock(&common.common_lock); + return rc; +} + +static int voice_alloc_oob_mem_table(void) +{ + int rc = 0; + size_t len; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + mutex_lock(&common.common_lock); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + rc = -EINVAL; + goto done; + } + + rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.memtbl.client), + &(v->shmem_info.memtbl.handle), + sizeof(struct vss_imemory_table_t), + &v->shmem_info.memtbl.phys, + &len, + &(v->shmem_info.memtbl.data)); + if (rc < 0) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, rc); + + goto done; + } + + v->shmem_info.memtbl.size = sizeof(struct vss_imemory_table_t); + pr_debug("%s data[%pK]phys[%pK][%pK]\n", __func__, + (void *)v->shmem_info.memtbl.data, + &v->shmem_info.memtbl.phys, + (void *)&v->shmem_info.memtbl.phys); + +done: + mutex_unlock(&common.common_lock); + return rc; +} + +int voc_send_cvp_start_vocpcm(uint32_t session_id, + struct vss_ivpcm_tap_point *vpcm_tp, + uint32_t no_of_tp) +{ + struct cvp_start_cmd cvp_start_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + struct voice_data *v = voice_get_session(session_id); + int i = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* Fill the header */ + cvp_start_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_start_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(struct vss_ivpcm_tap_point) * no_of_tp) + + sizeof(cvp_start_cmd.vpcm_start_cmd.num_tap_points) + + sizeof(cvp_start_cmd.vpcm_start_cmd.mem_handle); + cvp_start_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + cvp_start_cmd.hdr.dest_port = cvp_handle; + cvp_start_cmd.hdr.token = 0; + cvp_start_cmd.hdr.opcode = VSS_IVPCM_CMD_START_V2; + + for (i = 0; i < no_of_tp; i++) { + cvp_start_cmd.vpcm_start_cmd.tap_points[i].tap_point = + vpcm_tp[i].tap_point; + cvp_start_cmd.vpcm_start_cmd.tap_points[i].direction = + vpcm_tp[i].direction; + cvp_start_cmd.vpcm_start_cmd.tap_points[i].sampling_rate = + vpcm_tp[i].sampling_rate; + cvp_start_cmd.vpcm_start_cmd.tap_points[i].duration = 0; + } + + cvp_start_cmd.vpcm_start_cmd.mem_handle = + common.voice_host_pcm_mem_handle; + cvp_start_cmd.vpcm_start_cmd.num_tap_points = no_of_tp; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_start_cmd); + if (ret < 0) { + pr_err("%s: Fail: sending vocpcm map memory,\n", __func__); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +int voc_send_cvp_stop_vocpcm(uint32_t session_id) +{ + struct cvp_command vpcm_stop_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* fill in the header */ + vpcm_stop_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + vpcm_stop_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(vpcm_stop_cmd) - APR_HDR_SIZE); + vpcm_stop_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + vpcm_stop_cmd.hdr.dest_port = cvp_handle; + vpcm_stop_cmd.hdr.token = 0; + vpcm_stop_cmd.hdr.opcode = VSS_IVPCM_CMD_STOP; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_stop_cmd); + if (ret < 0) { + pr_err("Fail: sending vocpcm stop,\n"); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +int voc_send_cvp_map_vocpcm_memory(uint32_t session_id, + struct mem_map_table *tp_mem_table, + phys_addr_t paddr, uint32_t bufsize) +{ + return voice_map_memory_physical_cmd(voice_get_session(session_id), + tp_mem_table, + (dma_addr_t) paddr, bufsize, + VOC_VOICE_HOST_PCM_MAP_TOKEN); +} + +int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id) +{ + int ret = 0; + + ret = voice_send_mvm_unmap_memory_physical_cmd( + voice_get_session(session_id), + common.voice_host_pcm_mem_handle); + + if (ret == 0) + common.voice_host_pcm_mem_handle = 0; + + return ret; +} + +int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id, + struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt) +{ + struct cvp_push_buf_cmd vpcm_push_buf_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto done; + } + + memset(&vpcm_push_buf_cmd, 0, sizeof(vpcm_push_buf_cmd)); + cvp_handle = voice_get_cvp_handle(v); + + /* fill in the header */ + vpcm_push_buf_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + vpcm_push_buf_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(vpcm_push_buf_cmd) - APR_HDR_SIZE); + vpcm_push_buf_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + vpcm_push_buf_cmd.hdr.dest_port = cvp_handle; + vpcm_push_buf_cmd.hdr.token = 0; + vpcm_push_buf_cmd.hdr.opcode = VSS_IVPCM_EVT_PUSH_BUFFER_V2; + + vpcm_push_buf_cmd.vpcm_evt_push_buffer.tap_point = + push_buff_evt->tap_point; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.push_buf_mask = + push_buff_evt->push_buf_mask; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_address = + push_buff_evt->out_buf_mem_address; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_address = + push_buff_evt->in_buf_mem_address; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_size = + push_buff_evt->out_buf_mem_size; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_size = + push_buff_evt->in_buf_mem_size; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.sampling_rate = + push_buff_evt->sampling_rate; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.num_in_channels = + push_buff_evt->num_in_channels; + + ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_push_buf_cmd); + if (ret < 0) { + pr_err("Fail: sending vocpcm map memory,\n"); + goto done; + } + +done: + return ret; +} + +void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb, + void *private_data) +{ + common.hostpcm_info.hostpcm_evt_cb = hostpcm_cb; + common.hostpcm_info.private_data = private_data; +} + +void voc_deregister_hpcm_evt_cb(void) +{ + common.hostpcm_info.hostpcm_evt_cb = NULL; + common.hostpcm_info.private_data = NULL; +} + +int voc_get_cvd_version(char *cvd_version) +{ + int ret = 0; + struct voice_data *v = voice_get_session(VOICE_SESSION_VSID); + + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", + __func__, VOICE_SESSION_VSID); + + ret = -EINVAL; + goto done; + } + + if (is_cvd_version_queried()) { + pr_debug("%s: Returning the cached value %s\n", + __func__, common.cvd_version); + + goto done; + } + + /* Register callback to APR */ + ret = voice_apr_register(VOICE_SESSION_VSID); + if (ret < 0) { + pr_err("%s: apr register failed\n", __func__); + goto done; + } + + mutex_lock(&common.common_lock); + mutex_lock(&v->lock); + ret = voice_send_mvm_cvd_version_cmd(v); + if (ret < 0) { + pr_err("%s: voice_send_mvm_cvd_version_cmd failed\n", __func__); + goto unlock; + } + ret = 0; + +unlock: + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); + +done: + if (cvd_version) + memcpy(cvd_version, common.cvd_version, + CVD_VERSION_STRING_MAX_SIZE); + + return ret; +} + +static int voice_alloc_cal_mem_map_table(void) +{ + int ret = 0; + size_t len; + + ret = msm_audio_ion_alloc("voc_cal", + &(common.cal_mem_map_table.client), + &(common.cal_mem_map_table.handle), + sizeof(struct vss_imemory_table_t), + &common.cal_mem_map_table.phys, + &len, + &(common.cal_mem_map_table.data)); + if ((ret < 0) && (ret != -EPROBE_DEFER)) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, ret); + goto done; + } + + common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t); + pr_debug("%s: data %pK phys %pK\n", __func__, + common.cal_mem_map_table.data, + &common.cal_mem_map_table.phys); + +done: + return ret; +} + +static int voice_alloc_rtac_mem_map_table(void) +{ + int ret = 0; + size_t len; + + ret = msm_audio_ion_alloc("voc_rtac_cal", + &(common.rtac_mem_map_table.client), + &(common.rtac_mem_map_table.handle), + sizeof(struct vss_imemory_table_t), + &common.rtac_mem_map_table.phys, + &len, + &(common.rtac_mem_map_table.data)); + if (ret < 0) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, ret); + goto done; + } + + common.rtac_mem_map_table.size = sizeof(struct vss_imemory_table_t); + pr_debug("%s: data %pK phys %pK\n", __func__, + common.rtac_mem_map_table.data, + &common.rtac_mem_map_table.phys); + +done: + return ret; +} + +static int voice_alloc_and_map_oob_mem(struct voice_data *v) +{ + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (!is_voip_memory_allocated()) { + ret = voc_alloc_voip_shared_memory(); + if (ret < 0) { + pr_err("%s: Failed to create voip oob memory %d\n", + __func__, ret); + + goto done; + } + } + + ret = voice_map_memory_physical_cmd(v, + &v->shmem_info.memtbl, + v->shmem_info.sh_buf.buf[0].phys, + v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS, + VOIP_MEM_MAP_TOKEN); + if (ret) { + pr_err("%s: mvm_map_memory_phy failed %d\n", + __func__, ret); + + goto done; + } + +done: + return ret; +} + +uint32_t voice_get_topology(uint32_t topology_idx) +{ + uint32_t topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + struct cal_block_data *cal_block = NULL; + + /* initialize as default topology */ + if (topology_idx == CVP_VOC_RX_TOPOLOGY_CAL) { + topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + } else if (topology_idx == CVP_VOC_TX_TOPOLOGY_CAL) { + topology = VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS; + } else { + pr_err("%s: cal index %x is invalid!\n", + __func__, topology_idx); + + goto done; + } + + if (common.cal_data[topology_idx] == NULL) { + pr_err("%s: cal type is NULL for cal index %x\n", + __func__, topology_idx); + + goto done; + } + + mutex_lock(&common.cal_data[topology_idx]->lock); + cal_block = cal_utils_get_only_cal_block( + common.cal_data[topology_idx]); + if (cal_block == NULL) { + pr_debug("%s: cal_block not found for cal index %x\n", + __func__, topology_idx); + + goto unlock; + } + + topology = ((struct audio_cal_info_voc_top *) + cal_block->cal_info)->topology; +unlock: + mutex_unlock(&common.cal_data[topology_idx]->lock); +done: + pr_debug("%s: Using topology %d\n", __func__, topology); + + return topology; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: + ret = CVP_VOC_RX_TOPOLOGY_CAL; + break; + case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: + ret = CVP_VOC_TX_TOPOLOGY_CAL; + break; + case CVP_VOCPROC_STATIC_CAL_TYPE: + ret = CVP_VOCPROC_CAL; + break; + case CVP_VOCPROC_DYNAMIC_CAL_TYPE: + ret = CVP_VOCVOL_CAL; + break; + case CVS_VOCSTRM_STATIC_CAL_TYPE: + ret = CVS_VOCSTRM_CAL; + break; + case CVP_VOCDEV_CFG_CAL_TYPE: + ret = CVP_VOCDEV_CFG_CAL; + break; + case CVP_VOCPROC_STATIC_COL_CAL_TYPE: + ret = CVP_VOCPROC_COL_CAL; + break; + case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: + ret = CVP_VOCVOL_COL_CAL; + break; + case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: + ret = CVS_VOCSTRM_COL_CAL; + break; + case VOICE_RTAC_INFO_CAL_TYPE: + ret = VOICE_RTAC_INFO_CAL; + break; + case VOICE_RTAC_APR_CAL_TYPE: + ret = VOICE_RTAC_APR_CAL; + break; + default: + pr_err("%s: Invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int voice_prepare_volume_boost(int32_t cal_type, + size_t data_size, void *data) +{ + return voc_deregister_vocproc_vol_table(); +} + +static int voice_enable_volume_boost(int32_t cal_type, + size_t data_size, void *data) +{ + return voc_register_vocproc_vol_table(); +} + +static int voice_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + int cal_version; + + pr_debug("%s\n", __func__); + + cal_version = cal_utils_get_cal_type_version(data); + common.is_per_vocoder_cal_enabled = + !!(cal_version & PER_VOCODER_CAL_BIT_MASK); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: Cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int voice_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d!\n", + __func__, cal_index); + + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + common.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: Cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int voice_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d!\n", + __func__, cal_index); + + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: Cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static void voice_delete_cal_data(void) +{ + pr_debug("%s\n", __func__); + + cal_utils_destroy_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data); +} + +static int voice_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{CVP_VOC_RX_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVP_VOC_TX_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_STATIC_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, NULL, + voice_set_cal, NULL, NULL} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_DYNAMIC_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, + voice_prepare_volume_boost, + voice_set_cal, NULL, + voice_enable_volume_boost} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVP_VOCDEV_CFG_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, NULL, + voice_set_cal, NULL, NULL} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_STATIC_COL_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVS_VOCSTRM_STATIC_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, NULL, + voice_set_cal, NULL, NULL} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVS_VOCSTRM_STATIC_COL_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{VOICE_RTAC_INFO_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{VOICE_RTAC_APR_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + + ret = cal_utils_create_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: Could not create cal type!\n", + __func__); + + ret = -EINVAL; + goto err; + } + + return ret; +err: + voice_delete_cal_data(); + memset(&common, 0, sizeof(struct common_data)); + return ret; +} + +static int voice_send_set_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param soundFocusData) +{ + struct cvp_set_sound_focus_param_cmd_t cvp_set_sound_focus_param_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + pr_debug("%s: Enter\n", __func__); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + cvp_handle = voice_get_cvp_handle(v); + + /* send Sound Focus Params to cvp */ + cvp_set_sound_focus_param_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_sound_focus_param_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_sound_focus_param_cmd) - APR_HDR_SIZE); + cvp_set_sound_focus_param_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_sound_focus_param_cmd.hdr.dest_port = cvp_handle; + cvp_set_sound_focus_param_cmd.hdr.token = 0; + cvp_set_sound_focus_param_cmd.hdr.opcode = + VSS_ISOUNDFOCUS_CMD_SET_SECTORS; + + memset(&(cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param), 0xFF, + sizeof(struct vss_isoundfocus_cmd_set_sectors_t)); + for (i = 0; i < MAX_SECTORS; i++) { + cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param. + start_angles[i] = soundFocusData.start_angle[i]; + cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param. + enables[i] = soundFocusData.enable[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData.start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData.enable[i]); + } + cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.gain_step = + soundFocusData.gain_step; + pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_sound_focus_param_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (common.is_sound_focus_resp_success) { + ret = 0; + } else { + pr_err("%s: Error in setting sound focus params\n", __func__); + + ret = -EINVAL; + } + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +int voc_set_sound_focus(struct sound_focus_param soundFocusData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + pr_debug("%s: Enter\n", __func__); + + mutex_lock(&common.common_lock); + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_set_sound_focus_cmd(v, + soundFocusData); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session\n", __func__); + + ret = -EINVAL; + break; + } + } + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_send_get_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param *soundFocusData) +{ + struct apr_hdr cvp_get_sound_focus_param_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + pr_debug("%s: Enter\n", __func__); + + if (!v) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* send APR command to retrieve Sound Focus Params */ + cvp_get_sound_focus_param_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_get_sound_focus_param_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_get_sound_focus_param_cmd) - APR_HDR_SIZE); + cvp_get_sound_focus_param_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_get_sound_focus_param_cmd.dest_port = cvp_handle; + cvp_get_sound_focus_param_cmd.token = 0; + cvp_get_sound_focus_param_cmd.opcode = VSS_ISOUNDFOCUS_CMD_GET_SECTORS; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_get_sound_focus_param_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (common.is_sound_focus_resp_success) { + for (i = 0; i < MAX_SECTORS; i++) { + soundFocusData->start_angle[i] = + common.soundFocusResponse.start_angles[i]; + soundFocusData->enable[i] = + common.soundFocusResponse.enables[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData->start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData->enable[i]); + } + soundFocusData->gain_step = common.soundFocusResponse.gain_step; + pr_debug("%s: gain_step = %d\n", __func__, + soundFocusData->gain_step); + + common.is_sound_focus_resp_success = false; + ret = 0; + } else { + pr_err("%s: Invalid payload received from CVD\n", __func__); + + ret = -EINVAL; + } +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +int voc_get_sound_focus(struct sound_focus_param *soundFocusData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + pr_debug("%s: Enter\n", __func__); + + mutex_lock(&common.common_lock); + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_get_sound_focus_cmd(v, + soundFocusData); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session\n", __func__); + + ret = -EINVAL; + break; + } + } + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int is_source_tracking_shared_memomry_allocated(void) +{ + bool ret; + + pr_debug("%s: Enter\n", __func__); + + if (common.source_tracking_sh_mem.sh_mem_block.client != NULL && + common.source_tracking_sh_mem.sh_mem_block.handle != NULL) + ret = true; + else + ret = false; + + pr_debug("%s: Exit\n", __func__); + + return ret; +} + +static int voice_alloc_source_tracking_shared_memory(void) +{ + int ret = 0; + + pr_debug("%s: Enter\n", __func__); + + ret = msm_audio_ion_alloc("source_tracking_sh_mem_block", + &(common.source_tracking_sh_mem.sh_mem_block.client), + &(common.source_tracking_sh_mem.sh_mem_block.handle), + BUFFER_BLOCK_SIZE, + &(common.source_tracking_sh_mem.sh_mem_block.phys), + (size_t *)&(common.source_tracking_sh_mem.sh_mem_block.size), + &(common.source_tracking_sh_mem.sh_mem_block.data)); + if (ret < 0) { + pr_err("%s: audio ION alloc failed for sh_mem block, ret = %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + memset((void *)(common.source_tracking_sh_mem.sh_mem_block.data), 0, + common.source_tracking_sh_mem.sh_mem_block.size); + + pr_debug("%s: sh_mem_block: phys:[%pK], data:[0x%pK], size:[%zd]\n", + __func__, + &(common.source_tracking_sh_mem.sh_mem_block.phys), + (void *)(common.source_tracking_sh_mem.sh_mem_block.data), + (size_t)(common.source_tracking_sh_mem.sh_mem_block.size)); + + ret = msm_audio_ion_alloc("source_tracking_sh_mem_table", + &(common.source_tracking_sh_mem.sh_mem_table.client), + &(common.source_tracking_sh_mem.sh_mem_table.handle), + sizeof(struct vss_imemory_table_t), + &(common.source_tracking_sh_mem.sh_mem_table.phys), + (size_t *)&(common.source_tracking_sh_mem.sh_mem_table.size), + &(common.source_tracking_sh_mem.sh_mem_table.data)); + if (ret < 0) { + pr_err("%s: audio ION alloc failed for sh_mem table, ret = %d\n", + __func__, ret); + + ret = msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.client, + common.source_tracking_sh_mem.sh_mem_block.handle); + common.source_tracking_sh_mem.sh_mem_block.client = NULL; + common.source_tracking_sh_mem.sh_mem_block.handle = NULL; + if (ret < 0) + pr_err("%s: Error:%d freeing memory\n", __func__, ret); + + ret = -EINVAL; + goto done; + } + memset((void *)(common.source_tracking_sh_mem.sh_mem_table.data), 0, + common.source_tracking_sh_mem.sh_mem_table.size); + + pr_debug("%s sh_mem_table: phys:[%pK], data:[0x%pK], size:[%zd],\n", + __func__, + &(common.source_tracking_sh_mem.sh_mem_table.phys), + (void *)(common.source_tracking_sh_mem.sh_mem_table.data), + (size_t)(common.source_tracking_sh_mem.sh_mem_table.size)); + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_alloc_and_map_source_tracking_shared_memory( + struct voice_data *v) +{ + int ret = 0; + + pr_debug("%s: Enter\n", __func__); + + ret = voice_alloc_source_tracking_shared_memory(); + if (ret) { + pr_err("%s: Failed to allocate shared memory %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = voice_map_memory_physical_cmd(v, + &(common.source_tracking_sh_mem.sh_mem_table), + common.source_tracking_sh_mem.sh_mem_block.phys, + common.source_tracking_sh_mem.sh_mem_block.size, + VOC_SOURCE_TRACKING_MEM_MAP_TOKEN); + if (ret) { + pr_err("%s: memory mapping failed %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_unmap_and_free_source_tracking_shared_memory( + struct voice_data *v) +{ + int ret = 0; + + pr_debug("%s: Enter\n", __func__); + + if (common.source_tracking_sh_mem.mem_handle != 0) { + ret = voice_send_mvm_unmap_memory_physical_cmd(v, + common.source_tracking_sh_mem.mem_handle); + if (ret < 0) { + pr_err("%s: Memory_unmap failed err %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + } + + if ((common.source_tracking_sh_mem.sh_mem_block.client == NULL) || + (common.source_tracking_sh_mem.sh_mem_block.handle == NULL)) + goto done; + + ret = msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.client, + common.source_tracking_sh_mem.sh_mem_block.handle); + if (ret < 0) { + pr_err("%s: Error:%d freeing memory\n", __func__, ret); + + ret = -EINVAL; + goto done; + } + +done: + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.client = NULL; + common.source_tracking_sh_mem.sh_mem_block.handle = NULL; + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_send_get_source_tracking_cmd(struct voice_data *v, + struct source_tracking_param *sourceTrackingData) +{ + struct cvp_get_source_tracking_param_cmd_t st_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + pr_debug("%s: Enter\n", __func__); + + if (!v) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + + cvp_handle = voice_get_cvp_handle(v); + + if (!is_source_tracking_shared_memomry_allocated()) { + ret = voice_alloc_and_map_source_tracking_shared_memory(v); + if (ret) { + pr_err("%s: Fail in allocating/mapping shared memory\n", + __func__); + + ret = -EINVAL; + goto done; + } + } + st_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + st_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(st_cmd) - APR_HDR_SIZE); + st_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + st_cmd.hdr.dest_port = cvp_handle; + st_cmd.hdr.token = 0; + st_cmd.hdr.opcode = VSS_ISOURCETRACK_CMD_GET_ACTIVITY; + + st_cmd.cvp_get_source_tracking_param.mem_handle = + common.source_tracking_sh_mem.mem_handle; + st_cmd.cvp_get_source_tracking_param.mem_address_lsw = + lower_32_bits(common.source_tracking_sh_mem.sh_mem_block.phys); + st_cmd.cvp_get_source_tracking_param.mem_address_msw = + msm_audio_populate_upper_32_bits(common.source_tracking_sh_mem. + sh_mem_block.phys); + st_cmd.cvp_get_source_tracking_param.mem_size = + (uint32_t)common.source_tracking_sh_mem.sh_mem_block.size; + pr_debug("%s: mem_handle=0x%x, mem_address_lsw=0x%x, msw=0x%x, mem_size=%d\n", + __func__, + st_cmd.cvp_get_source_tracking_param.mem_handle, + st_cmd.cvp_get_source_tracking_param.mem_address_lsw, + st_cmd.cvp_get_source_tracking_param.mem_address_msw, + (uint32_t)st_cmd.cvp_get_source_tracking_param.mem_size); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, + (uint32_t *) &st_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (common.is_source_tracking_resp_success) { + for (i = 0; i < MAX_SECTORS; i++) { + sourceTrackingData->vad[i] = + common.sourceTrackingResponse.voice_active[i]; + pr_debug("%s: vad[%d] = %d\n", + __func__, i, sourceTrackingData->vad[i]); + } + sourceTrackingData->doa_speech = + common.sourceTrackingResponse.talker_doa; + pr_debug("%s: doa_speech = %d\n", + __func__, sourceTrackingData->doa_speech); + + for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) { + sourceTrackingData->doa_noise[i] = + common.sourceTrackingResponse.interferer_doa[i]; + pr_debug("%s: doa_noise[%d] = %d\n", + __func__, i, sourceTrackingData->doa_noise[i]); + } + for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) { + sourceTrackingData->polar_activity[i] = + common.sourceTrackingResponse.sound_strength[i]; + pr_debug("%s: polar_activity[%d] = %d\n", + __func__, i, sourceTrackingData->polar_activity[i]); + } + common.is_source_tracking_resp_success = false; + ret = 0; + } else { + pr_err("%s: Error response received from CVD\n", __func__); + + ret = -EINVAL; + } +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + pr_debug("%s: Enter\n", __func__); + + mutex_lock(&common.common_lock); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_get_source_tracking_cmd(v, + sourceTrackingData); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session\n", __func__); + + break; + } + } + + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +int is_voc_initialized(void) +{ + return module_initialized; +} + +static int __init voice_init(void) +{ + int rc = 0, i = 0; + + memset(&common, 0, sizeof(struct common_data)); + + /* set default value */ + common.default_mute_val = 0; /* default is un-mute */ + common.default_sample_val = 8000; + common.default_vol_step_val = 0; + common.default_vol_ramp_duration_ms = DEFAULT_VOLUME_RAMP_DURATION; + common.default_mute_ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION; + common.ec_ref_ext = false; + /* Initialize MVS info. */ + common.mvs_info.network_type = VSS_NETWORK_ID_DEFAULT; + + /* Initialize is low memory flag */ + common.is_destroy_cvd = false; + + /* Initialize CVD version */ + strlcpy(common.cvd_version, CVD_VERSION_DEFAULT, + sizeof(common.cvd_version)); + /* Initialize Per-Vocoder Calibration flag */ + common.is_per_vocoder_cal_enabled = false; + + mutex_init(&common.common_lock); + + /* Initialize session id with vsid */ + init_session_id(); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + + /* initialize dev_rx and dev_tx */ + common.voice[i].dev_rx.dev_mute = common.default_mute_val; + common.voice[i].dev_tx.dev_mute = common.default_mute_val; + common.voice[i].dev_rx.volume_step_value = + common.default_vol_step_val; + common.voice[i].dev_rx.volume_ramp_duration_ms = + common.default_vol_ramp_duration_ms; + common.voice[i].dev_rx.dev_mute_ramp_duration_ms = + common.default_mute_ramp_duration_ms; + common.voice[i].dev_tx.dev_mute_ramp_duration_ms = + common.default_mute_ramp_duration_ms; + common.voice[i].stream_rx.stream_mute = common.default_mute_val; + common.voice[i].stream_tx.stream_mute = common.default_mute_val; + + common.voice[i].dev_tx.port_id = 0x100B; + common.voice[i].dev_rx.port_id = 0x100A; + common.voice[i].dev_tx.dev_id = 0; + common.voice[i].dev_rx.dev_id = 0; + common.voice[i].dev_rx.no_of_channels = 0; + common.voice[i].dev_tx.no_of_channels = 0; + common.voice[i].sidetone_gain = 0x512; + common.voice[i].dtmf_rx_detect_en = 0; + common.voice[i].lch_mode = 0; + common.voice[i].disable_topology = false; + + common.voice[i].voc_state = VOC_INIT; + + init_waitqueue_head(&common.voice[i].mvm_wait); + init_waitqueue_head(&common.voice[i].cvs_wait); + init_waitqueue_head(&common.voice[i].cvp_wait); + + mutex_init(&common.voice[i].lock); + } + + if (voice_init_cal_data()) + pr_err("%s: Could not init cal data!\n", __func__); + + if (rc == 0) + module_initialized = true; + + pr_debug("%s: rc=%d\n", __func__, rc); + return rc; +} + +device_initcall(voice_init); + +static void __exit voice_exit(void) +{ + voice_delete_cal_data(); + free_cal_map_table(); +} + +__exitcall(voice_exit); diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h new file mode 100644 index 000000000000..f336a447d6eb --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6voice.h @@ -0,0 +1,1789 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __QDSP6VOICE_H__ +#define __QDSP6VOICE_H__ + +#include +#include +#include +#include +#include + +#define MAX_VOC_PKT_SIZE 642 +#define SESSION_NAME_LEN 20 +#define NUM_OF_MEMORY_BLOCKS 1 +#define NUM_OF_BUFFERS 2 +/* + * BUFFER BLOCK SIZE based on + * the supported page size + */ +#define BUFFER_BLOCK_SIZE 4096 + +#define MAX_COL_INFO_SIZE 324 + +#define VOC_REC_UPLINK 0x00 +#define VOC_REC_DOWNLINK 0x01 +#define VOC_REC_BOTH 0x02 + +#define VSS_IVERSION_CMD_GET 0x00011378 +#define VSS_IVERSION_RSP_GET 0x00011379 +#define CVD_VERSION_STRING_MAX_SIZE 31 +#define CVD_VERSION_DEFAULT "" +#define CVD_VERSION_0_0 "0.0" +#define CVD_VERSION_2_1 "2.1" +#define CVD_VERSION_2_2 "2.2" +#define CVD_VERSION_2_3 "2.3" + +#define CVD_INT_VERSION_DEFAULT 0 +#define CVD_INT_VERSION_0_0 1 +#define CVD_INT_VERSION_2_1 2 +#define CVD_INT_VERSION_2_2 3 +#define CVD_INT_VERSION_2_3 4 +#define CVD_INT_VERSION_LAST CVD_INT_VERSION_2_3 +#define CVD_INT_VERSION_MAX (CVD_INT_VERSION_LAST + 1) + +struct cvd_version_table { + char cvd_ver[CVD_VERSION_STRING_MAX_SIZE]; + int cvd_ver_int; +}; + +int voc_get_cvd_version(char *cvd_version); + +/* Payload structure for the VSS_IVERSION_RSP_GET command response */ +struct vss_iversion_rsp_get_t { + char version[CVD_VERSION_STRING_MAX_SIZE]; + /* NULL-terminated version string */ +}; + +enum { + CVP_VOC_RX_TOPOLOGY_CAL = 0, + CVP_VOC_TX_TOPOLOGY_CAL, + CVP_VOCPROC_CAL, + CVP_VOCVOL_CAL, + CVP_VOCDEV_CFG_CAL, + CVP_VOCPROC_COL_CAL, + CVP_VOCVOL_COL_CAL, + CVS_VOCSTRM_CAL, + CVS_VOCSTRM_COL_CAL, + VOICE_RTAC_INFO_CAL, + VOICE_RTAC_APR_CAL, + MAX_VOICE_CAL_TYPES +}; + +struct voice_header { + uint32_t id; + uint32_t data_len; +}; + +struct voice_init { + struct voice_header hdr; + void *cb_handle; +}; + +/* Stream information payload structure */ +struct stream_data { + uint32_t stream_mute; + uint32_t stream_mute_ramp_duration_ms; +}; + +/* Device information payload structure */ +struct device_data { + uint32_t dev_mute; + uint32_t sample; + uint32_t enabled; + uint32_t dev_id; + uint32_t port_id; + uint32_t volume_step_value; + uint32_t volume_ramp_duration_ms; + uint32_t dev_mute_ramp_duration_ms; + uint32_t no_of_channels; +}; + +struct voice_dev_route_state { + u16 rx_route_flag; + u16 tx_route_flag; +}; + +struct voice_rec_route_state { + u16 ul_flag; + u16 dl_flag; +}; + +enum { + VOC_INIT = 0, + VOC_RUN, + VOC_CHANGE, + VOC_RELEASE, + VOC_ERROR, + VOC_STANDBY, +}; + +struct mem_buffer { + dma_addr_t phys; + void *data; + uint32_t size; /* size of buffer */ +}; + +struct share_mem_buf { + struct ion_handle *handle; + struct ion_client *client; + struct mem_buffer buf[NUM_OF_BUFFERS]; +}; + +struct mem_map_table { + dma_addr_t phys; + void *data; + size_t size; /* size of buffer */ + struct ion_handle *handle; + struct ion_client *client; +}; + +/* Common */ +#define VSS_ICOMMON_CMD_SET_UI_PROPERTY 0x00011103 +/* Set a UI property */ +#define VSS_ICOMMON_CMD_MAP_MEMORY 0x00011025 +#define VSS_ICOMMON_CMD_UNMAP_MEMORY 0x00011026 +/* General shared memory; byte-accessible, 4 kB-aligned. */ +#define VSS_ICOMMON_MAP_MEMORY_SHMEM8_4K_POOL 3 + +struct vss_icommon_cmd_map_memory_t { + uint32_t phys_addr; + /* Physical address of a memory region; must be at least + * 4 kB aligned. + */ + + uint32_t mem_size; + /* Number of bytes in the region; should be a multiple of 32. */ + + uint16_t mem_pool_id; + /* Type of memory being provided. The memory ID implicitly defines + * the characteristics of the memory. The characteristics might include + * alignment type, permissions, etc. + * Memory pool ID. Possible values: + * 3 -- VSS_ICOMMON_MEM_TYPE_SHMEM8_4K_POOL. + */ +} __packed; + +struct vss_icommon_cmd_unmap_memory_t { + uint32_t phys_addr; + /* Physical address of a memory region; must be at least + * 4 kB aligned. + */ +} __packed; + +struct vss_map_memory_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_map_memory_t vss_map_mem; +} __packed; + +struct vss_unmap_memory_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_unmap_memory_t vss_unmap_mem; +} __packed; + +/* TO MVM commands */ +#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL 0x00011327 +/* + * VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL + * Description: This command is required to let MVM know + * who is in control of session. + * Payload: Defined by vss_imvm_cmd_set_policy_dual_control_t. + * Result: Wait for APRV2_IBASIC_RSP_RESULT response. + */ + +#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE +/* Create a new full control MVM session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C +/* Attach a stream to the MVM. */ + +#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D +/* Detach a stream from the MVM. */ + +#define VSS_IMVM_CMD_ATTACH_VOCPROC 0x0001123E +/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc + * to all the streams currently attached to it. + */ + +#define VSS_IMVM_CMD_DETACH_VOCPROC 0x0001123F +/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this + * vocproc from all the streams to which it is currently attached. + */ + +#define VSS_IMVM_CMD_START_VOICE 0x00011190 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_STANDBY_VOICE 0x00011191 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_STOP_VOICE 0x00011192 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_PAUSE_VOICE 0x0001137D +/* No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + + +#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C +/* Set the network type. */ + +#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0 +/* Set the voice timing parameters. */ + +#define VSS_IMEMORY_CMD_MAP_PHYSICAL 0x00011334 +#define VSS_IMEMORY_RSP_MAP 0x00011336 +#define VSS_IMEMORY_CMD_UNMAP 0x00011337 +#define VSS_IMVM_CMD_SET_CAL_NETWORK 0x0001137A +#define VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE 0x0001137B +#define VSS_IHDVOICE_CMD_ENABLE 0x000130A2 +#define VSS_IHDVOICE_CMD_DISABLE 0x000130A3 + +enum msm_audio_voc_rate { + VOC_0_RATE, /* Blank frame */ + VOC_8_RATE, /* 1/8 rate */ + VOC_4_RATE, /* 1/4 rate */ + VOC_2_RATE, /* 1/2 rate */ + VOC_1_RATE, /* Full rate */ + VOC_8_RATE_NC /* Noncritical 1/8 rate */ +}; + +struct vss_istream_cmd_set_tty_mode_t { + uint32_t mode; + /**< + * TTY mode. + * + * 0 : TTY disabled + * 1 : HCO + * 2 : VCO + * 3 : FULL + */ +} __packed; + +struct vss_istream_cmd_attach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being attached. */ +} __packed; + +struct vss_istream_cmd_detach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being detached. */ +} __packed; + +struct vss_imvm_cmd_attach_stream_t { + uint16_t handle; + /* The stream handle to attach. */ +} __packed; + +struct vss_imvm_cmd_detach_stream_t { + uint16_t handle; + /* The stream handle to detach. */ +} __packed; + +struct vss_icommon_cmd_set_network_t { + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ +} __packed; + +struct vss_icommon_cmd_set_voice_timing_t { + uint16_t mode; + /* + * The vocoder frame synchronization mode. + * + * 0 : No frame sync. + * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt). + */ + uint16_t enc_offset; + /* + * The offset in microseconds from the VFR to deliver a Tx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_req_offset; + /* + * The offset in microseconds from the VFR to request for an Rx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_offset; + /* + * The offset in microseconds from the VFR to indicate the deadline to + * receive an Rx vocoder packet. The offset should be less than 20000us. + * Rx vocoder packets received after this deadline are not guaranteed to + * be processed. + */ +} __packed; + +struct vss_imvm_cmd_create_control_session_t { + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + + +struct vss_imvm_cmd_set_policy_dual_control_t { + bool enable_flag; + /* Set to TRUE to enable modem state machine control */ +} __packed; + +struct mvm_attach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle; +} __packed; + +struct mvm_detach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle; +} __packed; + +struct mvm_create_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_create_control_session_t mvm_session; +} __packed; + +struct mvm_modem_dual_control_session_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_set_policy_dual_control_t voice_ctl; +} __packed; + +struct mvm_set_tty_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_tty_mode_t tty_mode; +} __packed; + +struct mvm_attach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_attach_stream_t attach_stream; +} __packed; + +struct mvm_detach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_detach_stream_t detach_stream; +} __packed; + +struct mvm_set_network_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_network_t network; +} __packed; + +struct mvm_set_voice_timing_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_voice_timing_t timing; +} __packed; + +struct mvm_set_hd_enable_cmd { + struct apr_hdr hdr; +} __packed; + +struct vss_imemory_table_descriptor_t { + uint32_t mem_address_lsw; + uint32_t mem_address_msw; + /* + * Base physical address of the table. The address must be aligned + * to LCM( cache_line_size, page_align, max_data_width ), where the + * attributes are specified in #VSS_IMEMORY_CMD_MAP_PHYSICAL, and + * LCM = Least Common Multiple. The table at the address must have + * the format specified by #vss_imemory_table_t. + */ + uint32_t mem_size; + /* Size in bytes of the table. */ +} __packed; + +struct vss_imemory_block_t { + uint64_t mem_address; + /* + * Base address of the memory block. The address is virtual for virtual + * memory and physical for physical memory. The address must be aligned + * to LCM( cache_line_size, page_align, max_data_width ), where the + * attributes are specified in VSS_IMEMORY_CMD_MAP_VIRTUAL or + * VSS_IMEMORY_CMD_MAP_PHYSICAL, and LCM = Least Common Multiple. + */ + uint32_t mem_size; + /* + * Size in bytes of the memory block. The size must be multiple of + * page_align, where page_align is specified in + * VSS_IMEMORY_CMD_MAP_VIRTUAL or #VSS_IMEMORY_CMD_MAP_PHYSICAL. + */ +} __packed; + +struct vss_imemory_table_t { + struct vss_imemory_table_descriptor_t next_table_descriptor; + /* + * Specifies the next table. If there is no next table, + * set the size of the table to 0 and the table address is ignored. + */ + struct vss_imemory_block_t blocks[NUM_OF_MEMORY_BLOCKS]; + /* Specifies one ore more memory blocks. */ +} __packed; + +struct vss_imemory_cmd_map_physical_t { + struct apr_hdr hdr; + struct vss_imemory_table_descriptor_t table_descriptor; + bool is_cached; + /* + * Indicates cached or uncached memory. Supported values: + * TRUE - Cached. + */ + uint16_t cache_line_size; + /* Cache line size in bytes. Supported values: 128 */ + uint32_t access_mask; + /* + * CVD's access permission to the memory while it is mapped. + * Supported values: + * bit 0 - If set, the memory is readable. + * bit 1 - If set, the memory is writable. + */ + uint32_t page_align; + /* Page frame alignment in bytes. Supported values: 4096 */ + uint8_t min_data_width; + /* + * Minimum native data type width in bits that can be accessed. + * Supported values: 8 + */ + uint8_t max_data_width; + /* + * Maximum native data type width in bits that can be accessed. + * Supported values: 64 + */ +} __packed; + +struct vss_imvm_cmd_set_cal_network_t { + struct apr_hdr hdr; + uint32_t network_id; +} __packed; + +struct vss_imvm_cmd_set_cal_media_type_t { + struct apr_hdr hdr; + uint32_t media_id; +} __packed; + +struct vss_imemory_cmd_unmap_t { + struct apr_hdr hdr; + uint32_t mem_handle; +} __packed; + +/* TO CVS commands */ +#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7 +/* Create a new full control stream session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +/* + * This command changes the mute setting. The new mute setting will + * be applied over the specified ramp duration. + */ +#define VSS_IVOLUME_CMD_MUTE_V2 0x0001138B + +#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011369 + +#define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA 0x0001127A + +#define VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA 0x0001307D +#define VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA 0x0001307E + +#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186 +/* Set media type on the stream. */ + +#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015 +/* Event sent by the stream to its client to provide an encoded packet. */ + +#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017 +/* Event sent by the stream to its client requesting for a decoder packet. + * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event. + */ + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST 0x0001136E + +#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016 +/* Event sent by the client to the stream in response to a + * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet. + */ + +#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E +/* Set AMR encoder rate. */ + +#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F +/* Set AMR-WB encoder rate. */ + +#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019 +/* Set encoder minimum and maximum rate. */ + +#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D +/* Set encoder DTX mode. */ + +#define MODULE_ID_VOICE_MODULE_ST 0x00010EE3 +#define VOICE_PARAM_MOD_ENABLE 0x00010E00 +#define MOD_ENABLE_PARAM_LEN 4 + +#define VSS_IPLAYBACK_CMD_START 0x000112BD +/* Start in-call music delivery on the Tx voice path. */ + +#define VSS_IPLAYBACK_CMD_STOP 0x00011239 +/* Stop the in-call music delivery on the Tx voice path. */ + +#define VSS_IPLAYBACK_PORT_ID_DEFAULT 0x0000FFFF +/* Default AFE port ID. */ + +#define VSS_IPLAYBACK_PORT_ID_VOICE 0x00008005 +/* AFE port ID for VOICE 1. */ + +#define VSS_IPLAYBACK_PORT_ID_VOICE2 0x00008002 +/* AFE port ID for VOICE 2. */ + +#define VSS_IRECORD_CMD_START 0x000112BE +/* Start in-call conversation recording. */ +#define VSS_IRECORD_CMD_STOP 0x00011237 +/* Stop in-call conversation recording. */ + +#define VSS_IRECORD_PORT_ID_DEFAULT 0x0000FFFF +/* Default AFE port ID. */ + +#define VSS_IRECORD_TAP_POINT_NONE 0x00010F78 +/* Indicates no tapping for specified path. */ + +#define VSS_IRECORD_TAP_POINT_STREAM_END 0x00010F79 +/* Indicates that specified path should be tapped at the end of the stream. */ + +#define VSS_IRECORD_MODE_TX_RX_STEREO 0x00010F7A +/* Select Tx on left channel and Rx on right channel. */ + +#define VSS_IRECORD_MODE_TX_RX_MIXING 0x00010F7B +/* Select mixed Tx and Rx paths. */ + +#define VSS_ISTREAM_EVT_NOT_READY 0x000110FD + +#define VSS_ISTREAM_EVT_READY 0x000110FC + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY 0x0001136F +/*notify dsp that decoder buffer is ready*/ + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY 0x0001136C +/*dsp notifying client that encoder buffer is ready*/ + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED 0x0001136D +/*notify dsp that encoder buffer is consumed*/ + +#define VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG 0x0001136B + +#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_INBAND 0 +/* In-band packet exchange mode. */ + +#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND 1 +/* Out-of-band packet exchange mode. */ + +#define VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE 0x0001136A + +struct vss_iplayback_cmd_start_t { + uint16_t port_id; + /* + * AFE Port ID from which the audio samples are available. + * To use the default AFE pseudo port (0x8005), set this value to + * #VSS_IPLAYBACK_PORT_ID_DEFAULT. + */ +} __packed; + +struct vss_irecord_cmd_start_t { + uint32_t rx_tap_point; + /* Tap point to use on the Rx path. Supported values are: + * VSS_IRECORD_TAP_POINT_NONE : Do not record Rx path. + * VSS_IRECORD_TAP_POINT_STREAM_END : Rx tap point is at the end of + * the stream. + */ + uint32_t tx_tap_point; + /* Tap point to use on the Tx path. Supported values are: + * VSS_IRECORD_TAP_POINT_NONE : Do not record tx path. + * VSS_IRECORD_TAP_POINT_STREAM_END : Tx tap point is at the end of + * the stream. + */ + uint16_t port_id; + /* AFE Port ID to which the conversation recording stream needs to be + * sent. Set this to #VSS_IRECORD_PORT_ID_DEFAULT to use default AFE + * pseudo ports (0x8003 for Rx and 0x8004 for Tx). + */ + uint32_t mode; + /* Recording Mode. The mode parameter value is ignored if the port_id + * is set to #VSS_IRECORD_PORT_ID_DEFAULT. + * The supported values: + * #VSS_IRECORD_MODE_TX_RX_STEREO + * #VSS_IRECORD_MODE_TX_RX_MIXING + */ +} __packed; + +struct vss_istream_cmd_create_passive_control_session_t { + char name[SESSION_NAME_LEN]; + /**< + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + +#define VSS_IVOLUME_DIRECTION_TX 0 +#define VSS_IVOLUME_DIRECTION_RX 1 + +#define VSS_IVOLUME_MUTE_OFF 0 +#define VSS_IVOLUME_MUTE_ON 1 + +#define DEFAULT_MUTE_RAMP_DURATION 500 +#define DEFAULT_VOLUME_RAMP_DURATION 20 +#define MAX_RAMP_DURATION 5000 + +struct vss_ivolume_cmd_mute_v2_t { + uint16_t direction; + /* + * The direction field sets the direction to apply the mute command. + * The Supported values: + * VSS_IVOLUME_DIRECTION_TX + * VSS_IVOLUME_DIRECTION_RX + */ + uint16_t mute_flag; + /* + * Turn mute on or off. The Supported values: + * VSS_IVOLUME_MUTE_OFF + * VSS_IVOLUME_MUTE_ON + */ + uint16_t ramp_duration_ms; + /* + * Mute change ramp duration in milliseconds. + * The Supported values: 0 to 5000. + */ +} __packed; + +struct vss_istream_cmd_create_full_control_session_t { + uint16_t direction; + /* + * Stream direction. + * + * 0 : TX only + * 1 : RX only + * 2 : TX and RX + * 3 : TX and RX loopback + */ + uint32_t enc_media_type; + /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t dec_media_type; + /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + +struct vss_istream_cmd_set_media_type_t { + uint32_t rx_media_id; + /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t tx_media_id; + /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ +} __packed; + +struct vss_istream_evt_send_enc_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data buffer. */ +} __packed; + +struct vss_istream_evt_send_dec_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data. */ +} __packed; + +struct vss_istream_cmd_voc_amr_set_enc_rate_t { + uint32_t mode; + /* Set the AMR encoder rate. + * + * 0x00000000 : 4.75 kbps + * 0x00000001 : 5.15 kbps + * 0x00000002 : 5.90 kbps + * 0x00000003 : 6.70 kbps + * 0x00000004 : 7.40 kbps + * 0x00000005 : 7.95 kbps + * 0x00000006 : 10.2 kbps + * 0x00000007 : 12.2 kbps + */ +} __packed; + +struct vss_istream_cmd_voc_amrwb_set_enc_rate_t { + uint32_t mode; + /* Set the AMR-WB encoder rate. + * + * 0x00000000 : 6.60 kbps + * 0x00000001 : 8.85 kbps + * 0x00000002 : 12.65 kbps + * 0x00000003 : 14.25 kbps + * 0x00000004 : 15.85 kbps + * 0x00000005 : 18.25 kbps + * 0x00000006 : 19.85 kbps + * 0x00000007 : 23.05 kbps + * 0x00000008 : 23.85 kbps + */ +} __packed; + +struct vss_istream_cmd_cdma_set_enc_minmax_rate_t { + uint16_t min_rate; + /* Set the lower bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ + uint16_t max_rate; + /* Set the upper bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ +} __packed; + +struct vss_istream_cmd_set_enc_dtx_mode_t { + uint32_t enable; + /* Toggle DTX on or off. + * + * 0 : Disables DTX + * 1 : Enables DTX + */ +} __packed; + +struct vss_istream_cmd_register_calibration_data_v2_t { + uint32_t cal_mem_handle; + /* Handle to the shared memory that holds the calibration data. */ + uint32_t cal_mem_address_lsw; + uint32_t cal_mem_address_msw; + /* Location of calibration data. */ + uint32_t cal_mem_size; + /* Size of the calibration data in bytes. */ + uint8_t column_info[MAX_COL_INFO_SIZE]; + /* + * Column info contains the number of columns and the array of columns + * in the calibration table. The order in which the columns are provided + * here must match the order in which they exist in the calibration + * table provided. + */ +} __packed; + +struct vss_icommon_cmd_set_ui_property_enable_t { + uint32_t module_id; + /* Unique ID of the module. */ + uint32_t param_id; + /* Unique ID of the parameter. */ + uint16_t param_size; + /* Size of the parameter in bytes: MOD_ENABLE_PARAM_LEN */ + uint16_t reserved; + /* Reserved; set to 0. */ + uint16_t enable; + uint16_t reserved_field; + /* Reserved, set to 0. */ +}; + +/* + * Event sent by the stream to the client that enables Rx DTMF + * detection whenever DTMF is detected in the Rx path. + * + * The DTMF detection feature can only be used to detect DTMF + * frequencies as listed in the vss_istream_evt_rx_dtmf_detected_t + * structure. + */ + +#define VSS_ISTREAM_EVT_RX_DTMF_DETECTED 0x0001101A + +struct vss_istream_cmd_set_rx_dtmf_detection { + /* + * Enables/disables Rx DTMF detection + * + * Possible values are + * 0 - disable + * 1 - enable + * + */ + uint32_t enable; +}; + +#define VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION 0x00011027 + +struct vss_istream_evt_rx_dtmf_detected { + uint16_t low_freq; + /* + * Detected low frequency. Possible values: + * 697 Hz + * 770 Hz + * 852 Hz + * 941 Hz + */ + uint16_t high_freq; + /* + * Detected high frequency. Possible values: + * 1209 Hz + * 1336 Hz + * 1477 Hz + * 1633 Hz + */ +}; + +struct cvs_set_rx_dtmf_detection_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_rx_dtmf_detection cvs_dtmf_det; +} __packed; + + +struct cvs_create_passive_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_passive_control_session_t cvs_session; +} __packed; + +struct cvs_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_full_control_session_t cvs_session; +} __packed; + +struct cvs_destroy_session_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvs_set_mute_cmd { + struct apr_hdr hdr; + struct vss_ivolume_cmd_mute_v2_t cvs_set_mute; +} __packed; + +struct cvs_set_media_type_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_media_type_t media_type; +} __packed; + +struct cvs_send_dec_buf_cmd { + struct apr_hdr hdr; + struct vss_istream_evt_send_dec_buffer_t dec_buf; +} __packed; + +struct cvs_set_amr_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate; +} __packed; + +struct cvs_set_amrwb_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate; +} __packed; + +struct cvs_set_cdma_enc_minmax_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate; +} __packed; + +struct cvs_set_enc_dtx_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode; +} __packed; + +struct cvs_register_cal_data_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_register_calibration_data_v2_t cvs_cal_data; +} __packed; + +struct cvs_deregister_cal_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvs_set_pp_enable_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_ui_property_enable_t vss_set_pp; +} __packed; +struct cvs_start_record_cmd { + struct apr_hdr hdr; + struct vss_irecord_cmd_start_t rec_mode; +} __packed; + +struct cvs_start_playback_cmd { + struct apr_hdr hdr; + struct vss_iplayback_cmd_start_t playback_mode; +} __packed; + +struct cvs_dec_buffer_ready_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvs_enc_buffer_consumed_cmd { + struct apr_hdr hdr; +} __packed; + +struct vss_istream_cmd_set_oob_packet_exchange_config_t { + struct apr_hdr hdr; + uint32_t mem_handle; + uint32_t enc_buf_addr_lsw; + uint32_t enc_buf_addr_msw; + uint32_t enc_buf_size; + uint32_t dec_buf_addr_lsw; + uint32_t dec_buf_addr_msw; + uint32_t dec_buf_size; +} __packed; + +struct vss_istream_cmd_set_packet_exchange_mode_t { + struct apr_hdr hdr; + uint32_t mode; +} __packed; + +/* TO CVP commands */ + +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +#define VSS_IVOCPROC_CMD_SET_DEVICE_V2 0x000112C6 + +#define VSS_IVOCPROC_CMD_SET_DEVICE_V3 0x0001316A + +#define VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS 0x00013199 + +#define VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT 0x00013198 + +#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB + +#define VSS_IVOLUME_CMD_SET_STEP 0x000112C2 + +#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +/* + * Registers the memory that contains device specific configuration data with + * the vocproc. The client must register device configuration data with the + * vocproc that corresponds with the device being set on the vocproc. + */ +#define VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG 0x00011371 + +/* + * Deregisters the memory that holds device configuration data from the + vocproc. +*/ +#define VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG 0x00011372 + +#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011373 +#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA 0x00011276 + +#define VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA 0x00011374 +#define VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA 0x00011375 + +#define VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA 0x00013079 +#define VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA 0x0001307A + +#define VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA 0x0001307B +#define VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA 0x0001307C + +#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72 + +#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77 + +/* Newtwork IDs */ +#define VSS_ICOMMON_CAL_NETWORK_ID_NONE 0x0001135E + +/* Select internal mixing mode. */ +#define VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING 0x00010F7C + +/* Select external mixing mode. */ +#define VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING 0x00010F7D + +/* Default AFE port ID. Applicable to Tx and Rx. */ +#define VSS_IVOCPROC_PORT_ID_NONE 0xFFFF + +#define VSS_NETWORK_ID_DEFAULT 0x00010037 + +/* Voice over Internet Protocol (VoIP) network ID. Common for all bands.*/ +#define VSS_NETWORK_ID_VOIP 0x00011362 + +/* Media types */ +#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2 +/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6 +/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7 +/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */ + +#define VSS_MEDIA_ID_PCM_8_KHZ 0x00010FCB +#define VSS_MEDIA_ID_PCM_16_KHZ 0x00010FCC +#define VSS_MEDIA_ID_PCM_32_KHZ 0x00010FD9 +#define VSS_MEDIA_ID_PCM_48_KHZ 0x00010FD6 + +/* Linear PCM (16-bit, little-endian). */ +#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD +/* G.711 a-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE +/* G.711 mu-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G729 0x00010FD0 +/* G.729AB (contains two 10ms vocoder frames. */ +#define VSS_MEDIA_ID_4GV_NB_MODEM 0x00010FC3 +/*CDMA EVRC-B vocoder modem format */ +#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4 +/*CDMA EVRC-WB vocoder modem format */ +#define VSS_MEDIA_ID_4GV_NW_MODEM 0x00010FC5 +/*CDMA EVRC-NW vocoder modem format */ + +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2 0x000112BF +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3 0x00013169 + +#define VSS_NUM_DEV_CHANNELS_1 1 +#define VSS_NUM_DEV_CHANNELS_2 2 +#define VSS_NUM_DEV_CHANNELS_3 3 +#define VSS_NUM_DEV_CHANNELS_4 4 + +struct vss_ivocproc_cmd_create_full_control_session_v2_t { + uint16_t direction; + /* + * Vocproc direction. The supported values: + * VSS_IVOCPROC_DIRECTION_RX + * VSS_IVOCPROC_DIRECTION_TX + * VSS_IVOCPROC_DIRECTION_RX_TX + */ + uint16_t tx_port_id; + /* + * Tx device port ID to which the vocproc connects. If a port ID is + * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t tx_topology_id; + /* + * Tx path topology ID. If a topology ID is not being supplied, set + * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + uint16_t rx_port_id; + /* + * Rx device port ID to which the vocproc connects. If a port ID is + * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t rx_topology_id; + /* + * Rx path topology ID. If a topology ID is not being supplied, set + * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + uint32_t profile_id; + /* Voice calibration profile ID. */ + uint32_t vocproc_mode; + /* + * Vocproc mode. The supported values: + * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING + * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING + */ + uint16_t ec_ref_port_id; + /* + * Port ID to which the vocproc connects for receiving echo + * cancellation reference signal. If a port ID is not being supplied, + * set this to #VSS_IVOCPROC_PORT_ID_NONE. This parameter value is + * ignored when the vocproc_mode parameter is set to + * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING. + */ + char name[SESSION_NAME_LEN]; + /* + * Session name string used to identify a session that can be shared + * with passive controllers (optional). The string size, including the + * NULL termination character, is limited to 31 characters. + */ +} __packed; + +struct vss_ivocproc_cmd_set_volume_index_t { + uint16_t vol_index; + /* + * Volume index utilized by the vocproc to index into the volume table + * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set + * volume on the VDSP. + */ +} __packed; + +struct vss_ivolume_cmd_set_step_t { + uint16_t direction; + /* + * The direction field sets the direction to apply the volume command. + * The supported values: + * #VSS_IVOLUME_DIRECTION_RX + */ + uint32_t value; + /* + * Volume step used to find the corresponding linear volume and + * the best match index in the registered volume calibration table. + */ + uint16_t ramp_duration_ms; + /* + * Volume change ramp duration in milliseconds. + * The supported values: 0 to 5000. + */ +} __packed; + +struct vss_ivocproc_cmd_set_device_v2_t { + uint16_t tx_port_id; + /* + * TX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t tx_topology_id; + /* + * TX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ + uint16_t rx_port_id; + /* + * RX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t rx_topology_id; + /* + * RX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ + uint32_t vocproc_mode; + /* Vocproc mode. The supported values: + * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING - 0x00010F7C + * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING - 0x00010F7D + */ + uint16_t ec_ref_port_id; + /* Port ID to which the vocproc connects for receiving + * echo + */ +} __packed; + +struct vss_ivocproc_cmd_register_device_config_t { + uint32_t mem_handle; + /* + * Handle to the shared memory that holds the per-network calibration + * data. + */ + uint32_t mem_address_lsw; + uint32_t mem_address_msw; + /* Location of calibration data. */ + uint32_t mem_size; + /* Size of the calibration data in bytes. */ +} __packed; + +struct vss_ivocproc_cmd_register_calibration_data_v2_t { + uint32_t cal_mem_handle; + /* + * Handle to the shared memory that holds the per-network calibration + * data. + */ + uint32_t cal_mem_address_lsw; + uint32_t cal_mem_address_msw; + /* Location of calibration data. */ + uint32_t cal_mem_size; + /* Size of the calibration data in bytes. */ + uint8_t column_info[MAX_COL_INFO_SIZE]; + /* + * Column info contains the number of columns and the array of columns + * in the calibration table. The order in which the columns are provided + * here must match the order in which they exist in the calibration + * table provided. + */ +} __packed; + +struct vss_ivocproc_cmd_register_volume_cal_data_t { + uint32_t cal_mem_handle; + /* + * Handle to the shared memory that holds the volume calibration + * data. + */ + uint32_t cal_mem_address_lsw; + uint32_t cal_mem_address_msw; + /* Location of volume calibration data. */ + uint32_t cal_mem_size; + /* Size of the volume calibration data in bytes. */ + uint8_t column_info[MAX_COL_INFO_SIZE]; + /* + * Column info contains the number of columns and the array of columns + * in the calibration table. The order in which the columns are provided + * here must match the order in which they exist in the calibration + * table provided. + */ +} __packed; + +struct vss_ivocproc_cmd_topology_set_dev_channels_t { + uint16_t tx_num_channels; + /* + * Number of Mics. + * Supported values + * 1 VSS_NUM_DEV_CHANNELS_1 + * 2 VSS_NUM_DEV_CHANNELS_2 + * 3 VSS_NUM_DEV_CHANNELS_3 + * 4 VSS_NUM_DEV_CHANNELS_4 + */ + uint16_t rx_num_channels; + /* + * Number of speaker channels. + * Supported values + * 1 VSS_NUM_DEV_CHANNELS_1 + */ +} __packed; + +/* Starts a vocoder PCM session */ +#define VSS_IVPCM_CMD_START_V2 0x00011339 + +/* Default tap point location on the TX path. */ +#define VSS_IVPCM_TAP_POINT_TX_DEFAULT 0x00011289 + +/* Default tap point location on the RX path. */ +#define VSS_IVPCM_TAP_POINT_RX_DEFAULT 0x0001128A + +/* Indicates tap point direction is output. */ +#define VSS_IVPCM_TAP_POINT_DIR_OUT 0 + +/* Indicates tap point direction is input. */ +#define VSS_IVPCM_TAP_POINT_DIR_IN 1 + +/* Indicates tap point direction is output and input. */ +#define VSS_IVPCM_TAP_POINT_DIR_OUT_IN 2 + + +#define VSS_IVPCM_SAMPLING_RATE_AUTO 0 + +/* Indicates 8 KHz vocoder PCM sampling rate. */ +#define VSS_IVPCM_SAMPLING_RATE_8K 8000 + +/* Indicates 16 KHz vocoder PCM sampling rate. */ +#define VSS_IVPCM_SAMPLING_RATE_16K 16000 + +/* RX and TX */ +#define MAX_TAP_POINTS_SUPPORTED 2 + +struct vss_ivpcm_tap_point { + uint32_t tap_point; + uint16_t direction; + uint16_t sampling_rate; + uint16_t duration; +} __packed; + + +struct vss_ivpcm_cmd_start_v2_t { + uint32_t mem_handle; + uint32_t num_tap_points; + struct vss_ivpcm_tap_point tap_points[MAX_TAP_POINTS_SUPPORTED]; +} __packed; + +#define VSS_IVPCM_EVT_PUSH_BUFFER_V2 0x0001133A + +/* Push buffer event mask indicating output buffer is filled. */ +#define VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER 1 + +/* Push buffer event mask indicating input buffer is consumed. */ +#define VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER 2 + + +struct vss_ivpcm_evt_push_buffer_v2_t { + uint32_t tap_point; + uint32_t push_buf_mask; + uint64_t out_buf_mem_address; + uint16_t out_buf_mem_size; + uint64_t in_buf_mem_address; + uint16_t in_buf_mem_size; + uint16_t sampling_rate; + uint16_t num_in_channels; +} __packed; + +#define VSS_IVPCM_EVT_NOTIFY_V2 0x0001133B + +/* Notify event mask indicates output buffer is filled. */ +#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER 1 + +/* Notify event mask indicates input buffer is consumed. */ +#define VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER 2 + +/* Notify event mask indicates a timetick */ +#define VSS_IVPCM_NOTIFY_MASK_TIMETICK 4 + +/* Notify event mask indicates an error occurred in output buffer operation */ +#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_ERROR 8 + +/* Notify event mask indicates an error occurred in input buffer operation */ +#define VSS_IVPCM_NOTIFY_MASK_INPUT_ERROR 16 + + +struct vss_ivpcm_evt_notify_v2_t { + uint32_t tap_point; + uint32_t notify_mask; + uint64_t out_buff_addr; + uint64_t in_buff_addr; + uint16_t filled_out_size; + uint16_t request_buf_size; + uint16_t sampling_rate; + uint16_t num_out_channels; +} __packed; + +struct cvp_start_cmd { + struct apr_hdr hdr; + struct vss_ivpcm_cmd_start_v2_t vpcm_start_cmd; +} __packed; + +struct cvp_push_buf_cmd { + struct apr_hdr hdr; + struct vss_ivpcm_evt_push_buffer_v2_t vpcm_evt_push_buffer; +} __packed; + +#define VSS_IVPCM_CMD_STOP 0x0001100B + +struct cvp_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_create_full_control_session_v2_t cvp_session; +} __packed; + +struct cvp_command { + struct apr_hdr hdr; +} __packed; + +struct cvp_set_device_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_device_v2_t cvp_set_device_v2; +} __packed; + +struct cvp_set_dev_channels_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_topology_set_dev_channels_t cvp_set_channels; +} __packed; + +struct cvp_set_vp3_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_set_rx_volume_index_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx; +} __packed; + +struct cvp_set_rx_volume_step_cmd { + struct apr_hdr hdr; + struct vss_ivolume_cmd_set_step_t cvp_set_vol_step; +} __packed; + +struct cvp_register_dev_cfg_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_register_device_config_t cvp_dev_cfg_data; +} __packed; + +struct cvp_deregister_dev_cfg_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_register_cal_data_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_register_calibration_data_v2_t cvp_cal_data; +} __packed; + +struct cvp_deregister_cal_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_register_vol_cal_data_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_register_volume_cal_data_t cvp_vol_cal_data; +} __packed; + +struct cvp_deregister_vol_cal_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_set_mute_cmd { + struct apr_hdr hdr; + struct vss_ivolume_cmd_mute_v2_t cvp_set_mute; +} __packed; + +/* CB for up-link packets. */ +typedef void (*ul_cb_fn)(uint8_t *voc_pkt, + uint32_t pkt_len, + uint32_t timestamp, + void *private_data); + +/* CB for down-link packets. */ +typedef void (*dl_cb_fn)(uint8_t *voc_pkt, + void *private_data); + +/* CB for DTMF RX Detection */ +typedef void (*dtmf_rx_det_cb_fn)(uint8_t *pkt, + char *session, + void *private_data); + +typedef void (*voip_ssr_cb) (uint32_t opcode, + void *private_data); + +typedef void (*hostpcm_cb_fn)(uint8_t *data, + char *session, + void *private_data); + +struct mvs_driver_info { + uint32_t media_type; + uint32_t rate; + uint32_t network_type; + uint32_t dtx_mode; + ul_cb_fn ul_cb; + dl_cb_fn dl_cb; + voip_ssr_cb ssr_cb; + void *private_data; + uint32_t evrc_min_rate; + uint32_t evrc_max_rate; +}; + +struct dtmf_driver_info { + dtmf_rx_det_cb_fn dtmf_rx_ul_cb; + void *private_data; +}; + +struct hostpcm_driver_info { + hostpcm_cb_fn hostpcm_evt_cb; + void *private_data; +}; + +struct incall_rec_info { + uint32_t rec_enable; + uint32_t rec_mode; + uint32_t recording; +}; + +struct incall_music_info { + uint32_t play_enable; + uint32_t playing; + int count; + int force; + uint16_t port_id; +}; + +struct share_memory_info { + u32 mem_handle; + struct share_mem_buf sh_buf; + struct mem_map_table memtbl; +}; + +#define VSS_ISOUNDFOCUS_CMD_SET_SECTORS 0x00013133 +#define VSS_ISOUNDFOCUS_CMD_GET_SECTORS 0x00013134 +#define VSS_ISOUNDFOCUS_RSP_GET_SECTORS 0x00013135 +#define VSS_ISOURCETRACK_CMD_GET_ACTIVITY 0x00013136 + +struct vss_isoundfocus_cmd_set_sectors_t { + uint16_t start_angles[8]; + uint8_t enables[8]; + uint16_t gain_step; +} __packed; + +/* Payload of the VSS_ISOUNDFOCUS_RSP_GET_SECTORS response */ +struct vss_isoundfocus_rsp_get_sectors_t { + uint16_t start_angles[8]; + uint8_t enables[8]; + uint16_t gain_step; +} __packed; + +struct cvp_set_sound_focus_param_cmd_t { + struct apr_hdr hdr; + struct vss_isoundfocus_cmd_set_sectors_t cvp_set_sound_focus_param; +} __packed; + +/* Payload structure for the VSS_ISOURCETRACK_CMD_GET_ACTIVITY command */ +struct vss_isourcetrack_cmd_get_activity_t { + uint32_t mem_handle; + uint32_t mem_address_lsw; + uint32_t mem_address_msw; + uint32_t mem_size; +} __packed; + +struct cvp_get_source_tracking_param_cmd_t { + struct apr_hdr hdr; + struct vss_isourcetrack_cmd_get_activity_t + cvp_get_source_tracking_param; +} __packed; + +/* Structure for the sound activity data */ +struct vss_isourcetrack_activity_data_t { + uint8_t voice_active[8]; + uint16_t talker_doa; + uint16_t interferer_doa[3]; + uint8_t sound_strength[360]; +} __packed; + +struct shared_mem_info { + uint32_t mem_handle; + struct mem_map_table sh_mem_block; + struct mem_map_table sh_mem_table; +}; + +struct voice_data { + int voc_state;/*INIT, CHANGE, RELEASE, RUN */ + + /* Shared mem to store decoder and encoder packets */ + struct share_memory_info shmem_info; + + wait_queue_head_t mvm_wait; + wait_queue_head_t cvs_wait; + wait_queue_head_t cvp_wait; + + /* Cache the values related to Rx and Tx devices */ + struct device_data dev_rx; + struct device_data dev_tx; + + /* Cache the values related to Rx and Tx streams */ + struct stream_data stream_rx; + struct stream_data stream_tx; + + u32 mvm_state; + u32 cvs_state; + u32 cvp_state; + + u32 async_err; + + /* Handle to MVM in the Q6 */ + u16 mvm_handle; + /* Handle to CVS in the Q6 */ + u16 cvs_handle; + /* Handle to CVP in the Q6 */ + u16 cvp_handle; + + struct mutex lock; + + bool disable_topology; + + uint16_t sidetone_gain; + uint8_t tty_mode; + /* slowtalk enable value */ + uint32_t st_enable; + uint32_t hd_enable; + uint32_t dtmf_rx_detect_en; + /* Local Call Hold mode */ + uint8_t lch_mode; + + struct voice_dev_route_state voc_route_state; + + u32 session_id; + + struct incall_rec_info rec_info; + + struct incall_music_info music_info; + + struct voice_rec_route_state rec_route_state; + + struct power_supply *psy; +}; + +struct cal_mem { + struct ion_handle *handle; + uint32_t phy; + void *buf; +}; + +#define MAX_VOC_SESSIONS 8 + +struct common_data { + /* these default values are for all devices */ + uint32_t default_mute_val; + uint32_t default_sample_val; + uint32_t default_vol_step_val; + uint32_t default_vol_ramp_duration_ms; + uint32_t default_mute_ramp_duration_ms; + bool ec_ref_ext; + uint16_t ec_port_id; + + /* APR to MVM in the Q6 */ + void *apr_q6_mvm; + /* APR to CVS in the Q6 */ + void *apr_q6_cvs; + /* APR to CVP in the Q6 */ + void *apr_q6_cvp; + + struct cal_type_data *cal_data[MAX_VOICE_CAL_TYPES]; + + struct mem_map_table cal_mem_map_table; + uint32_t cal_mem_handle; + + struct mem_map_table rtac_mem_map_table; + uint32_t rtac_mem_handle; + + uint32_t voice_host_pcm_mem_handle; + + struct cal_mem cvp_cal; + struct cal_mem cvs_cal; + + struct mutex common_lock; + + struct mvs_driver_info mvs_info; + + struct dtmf_driver_info dtmf_info; + + struct hostpcm_driver_info hostpcm_info; + + struct voice_data voice[MAX_VOC_SESSIONS]; + + bool srvcc_rec_flag; + bool is_destroy_cvd; + bool is_vote_bms; + char cvd_version[CVD_VERSION_STRING_MAX_SIZE]; + bool is_per_vocoder_cal_enabled; + bool is_sound_focus_resp_success; + bool is_source_tracking_resp_success; + struct vss_isoundfocus_rsp_get_sectors_t soundFocusResponse; + struct shared_mem_info source_tracking_sh_mem; + struct vss_isourcetrack_activity_data_t sourceTrackingResponse; +}; + +struct voice_session_itr { + int cur_idx; + int session_idx; +}; + +void voc_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + voip_ssr_cb ssr_cb, + void *private_data); + +void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb, + void *private_data); + +void voc_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode, + uint32_t evrc_min_rate, + uint32_t evrc_max_rate); + +enum { + DEV_RX = 0, + DEV_TX, +}; + +enum { + RX_PATH = 0, + TX_PATH, +}; + + +#define VOC_PATH_PASSIVE 0 +#define VOC_PATH_FULL 1 +#define VOC_PATH_VOLTE_PASSIVE 2 +#define VOC_PATH_VOICE2_PASSIVE 3 +#define VOC_PATH_QCHAT_PASSIVE 4 +#define VOC_PATH_VOWLAN_PASSIVE 5 +#define VOC_PATH_VOICEMMODE1_PASSIVE 6 +#define VOC_PATH_VOICEMMODE2_PASSIVE 7 + +#define MAX_SESSION_NAME_LEN 32 +#define VOICE_SESSION_NAME "Voice session" +#define VOIP_SESSION_NAME "VoIP session" +#define VOLTE_SESSION_NAME "VoLTE session" +#define VOICE2_SESSION_NAME "Voice2 session" +#define QCHAT_SESSION_NAME "QCHAT session" +#define VOWLAN_SESSION_NAME "VoWLAN session" +#define VOICEMMODE1_NAME "VoiceMMode1" +#define VOICEMMODE2_NAME "VoiceMMode2" + +#define VOICE2_SESSION_VSID_STR "10DC1000" +#define QCHAT_SESSION_VSID_STR "10803000" +#define VOWLAN_SESSION_VSID_STR "10002000" +#define VOICEMMODE1_VSID_STR "11C05000" +#define VOICEMMODE2_VSID_STR "11DC5000" +#define VOICE_SESSION_VSID 0x10C01000 +#define VOICE2_SESSION_VSID 0x10DC1000 +#define VOLTE_SESSION_VSID 0x10C02000 +#define VOIP_SESSION_VSID 0x10004000 +#define QCHAT_SESSION_VSID 0x10803000 +#define VOWLAN_SESSION_VSID 0x10002000 +#define VOICEMMODE1_VSID 0x11C05000 +#define VOICEMMODE2_VSID 0x11DC5000 +#define ALL_SESSION_VSID 0xFFFFFFFF +#define VSID_MAX ALL_SESSION_VSID + +/* called by alsa driver */ +int voc_set_pp_enable(uint32_t session_id, uint32_t module_id, + uint32_t enable); +int voc_get_pp_enable(uint32_t session_id, uint32_t module_id); +int voc_set_hd_enable(uint32_t session_id, uint32_t enable); +uint8_t voc_get_tty_mode(uint32_t session_id); +int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode); +int voc_start_voice_call(uint32_t session_id); +int voc_end_voice_call(uint32_t session_id); +int voc_standby_voice_call(uint32_t session_id); +int voc_resume_voice_call(uint32_t session_id); +int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode); +int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step, + uint32_t ramp_duration); +int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration); +int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration); +int voc_get_rx_device_mute(uint32_t session_id); +int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set); +uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir); +int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable); +void voc_disable_dtmf_det_on_active_sessions(void); +int voc_alloc_cal_shared_memory(void); +int voc_alloc_voip_shared_memory(void); +int is_voc_initialized(void); +int voc_register_vocproc_vol_table(void); +int voc_deregister_vocproc_vol_table(void); +int voc_send_cvp_map_vocpcm_memory(uint32_t session_id, + struct mem_map_table *tp_mem_table, + phys_addr_t paddr, uint32_t bufsize); +int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id); +int voc_send_cvp_start_vocpcm(uint32_t session_id, + struct vss_ivpcm_tap_point *vpcm_tp, + uint32_t no_of_tp); +int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id, + struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt); +int voc_send_cvp_stop_vocpcm(uint32_t session_id); +void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb, + void *private_data); +void voc_deregister_hpcm_evt_cb(void); + +int voc_map_rtac_block(struct rtac_cal_block_data *cal_block); +int voc_unmap_rtac_block(uint32_t *mem_map_handle); + +uint32_t voc_get_session_id(char *name); + +int voc_start_playback(uint32_t set, uint16_t port_id); +int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id); +int voice_get_idx_for_session(u32 session_id); +int voc_set_ext_ec_ref(uint16_t port_id, bool state); +int voc_update_amr_vocoder_rate(uint32_t session_id); +int voc_disable_device(uint32_t session_id); +int voc_enable_device(uint32_t session_id); +void voc_set_destroy_cvd_flag(bool is_destroy_cvd); +void voc_set_vote_bms_flag(bool is_vote_bms); +int voc_disable_topology(uint32_t session_id, uint32_t disable); +int voc_set_device_config(uint32_t session_id, uint8_t path_dir, + uint8_t no_of_channels, uint32_t dev_port_id); +uint32_t voice_get_topology(uint32_t topology_idx); +int voc_set_sound_focus(struct sound_focus_param sound_focus_param); +int voc_get_sound_focus(struct sound_focus_param *soundFocusData); +int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData); +#endif diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c new file mode 100644 index 000000000000..923908f59e6f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/rtac.c @@ -0,0 +1,1904 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6voice.h" +#include "msm-pcm-routing-v2.h" +#include + + +/* Max size of payload (buf size - apr header) */ +#define MAX_PAYLOAD_SIZE 4076 +#define RTAC_MAX_ACTIVE_VOICE_COMBOS 2 +#define RTAC_MAX_ACTIVE_POPP 8 +#define RTAC_BUF_SIZE 163840 + +#define TIMEOUT_MS 1000 + +struct rtac_cal_block_data rtac_cal[MAX_RTAC_BLOCKS] = { +/* ADM_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} }, +/* ASM_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} }, +/* VOICE_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} }, +/* AFE_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} } +}; + +struct rtac_common_data { + atomic_t usage_count; + atomic_t apr_err_code; +}; + +static struct rtac_common_data rtac_common; + +/* APR data */ +struct rtac_apr_data { + void *apr_handle; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; +}; + +static struct rtac_apr_data rtac_adm_apr_data; +static struct rtac_apr_data rtac_asm_apr_data[ASM_ACTIVE_STREAMS_ALLOWED + 1]; +static struct rtac_apr_data rtac_afe_apr_data; +static struct rtac_apr_data rtac_voice_apr_data[RTAC_VOICE_MODES]; + +/* ADM info & APR */ +static struct rtac_adm rtac_adm_data; +static u32 *rtac_adm_buffer; + + +/* ASM APR */ +static u32 *rtac_asm_buffer; + +static u32 *rtac_afe_buffer; + +/* Voice info & APR */ +struct rtac_voice_data_t { + uint32_t tx_topology_id; + uint32_t rx_topology_id; + uint32_t tx_afe_topology; + uint32_t rx_afe_topology; + uint32_t tx_afe_port; + uint32_t rx_afe_port; + uint16_t cvs_handle; + uint16_t cvp_handle; + uint32_t tx_acdb_id; + uint32_t rx_acdb_id; +}; + +struct rtac_voice { + uint32_t num_of_voice_combos; + struct rtac_voice_data_t voice[RTAC_MAX_ACTIVE_VOICE_COMBOS]; +}; + +struct rtac_afe_user_data { + uint32_t buf_size; + uint32_t cmd_size; + uint32_t port_id; + union { + struct rtac_afe_set { + struct afe_port_cmd_set_param_v2 cmd; + struct afe_port_param_data_v2 data; + } rtac_afe_set; + struct rtac_afe_get { + struct afe_port_cmd_get_param_v2 cmd; + struct afe_port_param_data_v2 data; + } rtac_afe_get; + }; +} __packed; + +static struct rtac_voice rtac_voice_data; +static u32 *rtac_voice_buffer; +static u32 voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS]; + + +struct mutex rtac_adm_mutex; +struct mutex rtac_adm_apr_mutex; +struct mutex rtac_asm_apr_mutex; +struct mutex rtac_voice_mutex; +struct mutex rtac_voice_apr_mutex; +struct mutex rtac_afe_apr_mutex; + +int rtac_clear_mapping(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_debug("%s: invalid cal type %d\n", __func__, cal_type); + result = -EINVAL; + goto done; + } + + rtac_cal[cal_type].map_data.map_handle = 0; +done: + return result; +} + +int rtac_allocate_cal_buffer(uint32_t cal_type) +{ + int result = 0; + size_t len; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].cal_data.paddr != 0) { + pr_err("%s: memory already allocated! cal_type %d, paddr 0x%pK\n", + __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr); + result = -EPERM; + goto done; + } + + result = msm_audio_ion_alloc("rtac_client", + &rtac_cal[cal_type].map_data.ion_client, + &rtac_cal[cal_type].map_data.ion_handle, + rtac_cal[cal_type].map_data.map_size, + &rtac_cal[cal_type].cal_data.paddr, + &len, + &rtac_cal[cal_type].cal_data.kvaddr); + if (result < 0) { + pr_err("%s: ION create client for RTAC failed\n", + __func__); + goto done; + } + + pr_debug("%s: cal_type %d, paddr 0x%pK, kvaddr 0x%pK, map_size 0x%x\n", + __func__, cal_type, + &rtac_cal[cal_type].cal_data.paddr, + rtac_cal[cal_type].cal_data.kvaddr, + rtac_cal[cal_type].map_data.map_size); +done: + return result; +} + +int rtac_free_cal_buffer(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].map_data.ion_client == NULL) { + pr_debug("%s: cal_type %d not allocated!\n", + __func__, cal_type); + goto done; + } + + result = msm_audio_ion_free(rtac_cal[cal_type].map_data.ion_client, + rtac_cal[cal_type].map_data.ion_handle); + if (result < 0) { + pr_err("%s: ION free for RTAC failed! cal_type %d, paddr 0x%pK\n", + __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr); + goto done; + } + + rtac_cal[cal_type].map_data.map_handle = 0; + rtac_cal[cal_type].map_data.ion_client = NULL; + rtac_cal[cal_type].map_data.ion_handle = NULL; + rtac_cal[cal_type].cal_data.size = 0; + rtac_cal[cal_type].cal_data.kvaddr = 0; + rtac_cal[cal_type].cal_data.paddr = 0; +done: + return result; +} + +int rtac_map_cal_buffer(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].map_data.map_handle != 0) { + pr_err("%s: already mapped cal_type %d\n", + __func__, cal_type); + result = -EPERM; + goto done; + } + + if (rtac_cal[cal_type].cal_data.paddr == 0) { + pr_err("%s: physical address is NULL cal_type %d\n", + __func__, cal_type); + result = -EPERM; + goto done; + } + + switch (cal_type) { + case ADM_RTAC_CAL: + result = adm_map_rtac_block(&rtac_cal[cal_type]); + break; + case ASM_RTAC_CAL: + result = q6asm_map_rtac_block(&rtac_cal[cal_type]); + break; + case VOICE_RTAC_CAL: + result = voc_map_rtac_block(&rtac_cal[cal_type]); + break; + case AFE_RTAC_CAL: + result = afe_map_rtac_block(&rtac_cal[cal_type]); + break; + } + if (result < 0) { + pr_err("%s: map RTAC failed! cal_type %d\n", + __func__, cal_type); + goto done; + } +done: + return result; +} + +int rtac_unmap_cal_buffer(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].map_data.map_handle == 0) { + pr_debug("%s: nothing to unmap cal_type %d\n", + __func__, cal_type); + goto done; + } + + switch (cal_type) { + case ADM_RTAC_CAL: + result = adm_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + case ASM_RTAC_CAL: + result = q6asm_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + case VOICE_RTAC_CAL: + result = voc_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + case AFE_RTAC_CAL: + result = afe_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + } + if (result < 0) { + pr_err("%s: unmap RTAC failed! cal_type %d\n", + __func__, cal_type); + goto done; + } +done: + return result; +} + +static int rtac_open(struct inode *inode, struct file *f) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + atomic_inc(&rtac_common.usage_count); + return result; +} + +static int rtac_release(struct inode *inode, struct file *f) +{ + int result = 0; + int result2 = 0; + int i; + + pr_debug("%s\n", __func__); + + atomic_dec(&rtac_common.usage_count); + pr_debug("%s: ref count %d!\n", __func__, + atomic_read(&rtac_common.usage_count)); + + if (atomic_read(&rtac_common.usage_count) > 0) + goto done; + + for (i = 0; i < MAX_RTAC_BLOCKS; i++) { + result2 = rtac_unmap_cal_buffer(i); + if (result2 < 0) { + pr_err("%s: unmap buffer failed! error %d!\n", + __func__, result2); + result = result2; + } + + result2 = rtac_free_cal_buffer(i); + if (result2 < 0) { + pr_err("%s: free buffer failed! error %d!\n", + __func__, result2); + result = result2; + } + } +done: + return result; +} + + +/* ADM Info */ +void add_popp(u32 dev_idx, u32 port_id, u32 popp_id) +{ + u32 i = 0; + + for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++) + if (rtac_adm_data.device[dev_idx].popp[i].popp == popp_id) + goto done; + + if (rtac_adm_data.device[dev_idx].num_of_popp == + RTAC_MAX_ACTIVE_POPP) { + pr_err("%s, Max POPP!\n", __func__); + goto done; + } + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp].popp = popp_id; + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp].popp_topology = + q6asm_get_asm_topology(popp_id); + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp++].app_type = + q6asm_get_asm_app_type(popp_id); + + pr_debug("%s: popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n", + __func__, + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp - 1].popp, + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp - 1].popp_topology, + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp - 1].app_type); +done: + return; +} + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id, + u32 app_type, u32 acdb_id) +{ + u32 i = 0; + + pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n", + __func__, rtac_adm_data.num_of_dev, port_id, copp_id); + + mutex_lock(&rtac_adm_mutex); + if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) { + pr_err("%s, Can't add anymore RTAC devices!\n", __func__); + goto done; + } + + /* Check if device already added */ + if (rtac_adm_data.num_of_dev != 0) { + for (; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id && + rtac_adm_data.device[i].copp == copp_id) { + add_popp(i, port_id, popp_id); + goto done; + } + if (rtac_adm_data.device[i].num_of_popp == + RTAC_MAX_ACTIVE_POPP) { + pr_err("%s, Max POPP!\n", __func__); + goto done; + } + } + } + + /* Add device */ + rtac_adm_data.num_of_dev++; + + rtac_adm_data.device[i].topology_id = + adm_get_topology_for_port_from_copp_id(port_id, copp_id); + rtac_adm_data.device[i].afe_topology = + afe_get_topology(port_id); + rtac_adm_data.device[i].afe_port = port_id; + rtac_adm_data.device[i].copp = copp_id; + rtac_adm_data.device[i].app_type = app_type; + rtac_adm_data.device[i].acdb_dev_id = acdb_id; + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp].popp = popp_id; + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp].popp_topology = + q6asm_get_asm_topology(popp_id); + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp++].app_type = + q6asm_get_asm_app_type(popp_id); + + pr_debug("%s: topology = 0x%x, afe_topology = 0x%x, port_id = %d, copp_id = %d, app id = 0x%x, acdb id = %d, popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n", + __func__, + rtac_adm_data.device[i].topology_id, + rtac_adm_data.device[i].afe_topology, + rtac_adm_data.device[i].afe_port, + rtac_adm_data.device[i].copp, + rtac_adm_data.device[i].app_type, + rtac_adm_data.device[i].acdb_dev_id, + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp - 1].popp, + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp - 1].popp_topology, + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp - 1].app_type); +done: + mutex_unlock(&rtac_adm_mutex); +} + +static void shift_adm_devices(u32 dev_idx) +{ + for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) { + memcpy(&rtac_adm_data.device[dev_idx], + &rtac_adm_data.device[dev_idx + 1], + sizeof(rtac_adm_data.device[dev_idx])); + memset(&rtac_adm_data.device[dev_idx + 1], 0, + sizeof(rtac_adm_data.device[dev_idx])); + } +} + +static void shift_popp(u32 copp_idx, u32 popp_idx) +{ + for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp; + popp_idx++) { + memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx].popp, + &rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp, sizeof(uint32_t)); + memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx]. + popp_topology, + &rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp_topology, + sizeof(uint32_t)); + memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp, 0, sizeof(uint32_t)); + memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp_topology, 0, sizeof(uint32_t)); + } +} + +void rtac_remove_adm_device(u32 port_id, u32 copp_id) +{ + s32 i; + + pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n", + __func__, rtac_adm_data.num_of_dev, port_id, copp_id); + + mutex_lock(&rtac_adm_mutex); + /* look for device */ + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id && + rtac_adm_data.device[i].copp == copp_id) { + memset(&rtac_adm_data.device[i], 0, + sizeof(rtac_adm_data.device[i])); + rtac_adm_data.num_of_dev--; + + if (rtac_adm_data.num_of_dev >= 1) { + shift_adm_devices(i); + break; + } + } + } + + mutex_unlock(&rtac_adm_mutex); +} + +void rtac_remove_popp_from_adm_devices(u32 popp_id) +{ + s32 i, j; + + pr_debug("%s: popp_id = %d\n", __func__, popp_id); + + mutex_lock(&rtac_adm_mutex); + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) { + if (rtac_adm_data.device[i].popp[j].popp == + popp_id) { + rtac_adm_data.device[i].popp[j].popp = 0; + rtac_adm_data.device[i].popp[j]. + popp_topology = 0; + rtac_adm_data.device[i].num_of_popp--; + shift_popp(i, j); + } + } + } + mutex_unlock(&rtac_adm_mutex); +} + + +/* Voice Info */ +static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle, + u32 rx_afe_port, u32 tx_afe_port, + u32 rx_acdb_id, u32 tx_acdb_id, + u32 session_id) +{ + rtac_voice_data.voice[idx].tx_topology_id = + voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL); + rtac_voice_data.voice[idx].rx_topology_id = + voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL); + rtac_voice_data.voice[idx].tx_afe_topology = + afe_get_topology(tx_afe_port); + rtac_voice_data.voice[idx].rx_afe_topology = + afe_get_topology(rx_afe_port); + rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port; + rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port; + rtac_voice_data.voice[idx].tx_acdb_id = tx_acdb_id; + rtac_voice_data.voice[idx].rx_acdb_id = rx_acdb_id; + rtac_voice_data.voice[idx].cvs_handle = cvs_handle; + rtac_voice_data.voice[idx].cvp_handle = cvp_handle; + pr_debug("%s\n%s: %x\n%s: %d %s: %d\n%s: %d %s: %d\n %s: %d\n %s: %d\n%s: %d %s: %d\n%s", + "<---- Voice Data Info ---->", "Session id", session_id, + "cvs_handle", cvs_handle, "cvp_handle", cvp_handle, + "rx_afe_topology", rtac_voice_data.voice[idx].rx_afe_topology, + "tx_afe_topology", rtac_voice_data.voice[idx].tx_afe_topology, + "rx_afe_port", rx_afe_port, "tx_afe_port", tx_afe_port, + "rx_acdb_id", rx_acdb_id, "tx_acdb_id", tx_acdb_id, + "<-----------End----------->"); + + /* Store session ID for voice RTAC */ + voice_session_id[idx] = session_id; +} + +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id, + u32 session_id) +{ + u32 i = 0; + + pr_debug("%s\n", __func__); + mutex_lock(&rtac_voice_mutex); + + if (rtac_voice_data.num_of_voice_combos == + RTAC_MAX_ACTIVE_VOICE_COMBOS) { + pr_err("%s, Can't add anymore RTAC devices!\n", __func__); + goto done; + } + + /* Check if device already added */ + if (rtac_voice_data.num_of_voice_combos != 0) { + for (; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == + cvs_handle) { + set_rtac_voice_data(i, cvs_handle, cvp_handle, + rx_afe_port, tx_afe_port, rx_acdb_id, + tx_acdb_id, session_id); + goto done; + } + } + } + + /* Add device */ + rtac_voice_data.num_of_voice_combos++; + set_rtac_voice_data(i, cvs_handle, cvp_handle, + rx_afe_port, tx_afe_port, + rx_acdb_id, tx_acdb_id, + session_id); +done: + mutex_unlock(&rtac_voice_mutex); +} + +static void shift_voice_devices(u32 idx) +{ + for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) { + memcpy(&rtac_voice_data.voice[idx], + &rtac_voice_data.voice[idx + 1], + sizeof(rtac_voice_data.voice[idx])); + voice_session_id[idx] = voice_session_id[idx + 1]; + } +} + +void rtac_remove_voice(u32 cvs_handle) +{ + u32 i = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_voice_mutex); + /* look for device */ + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) { + shift_voice_devices(i); + rtac_voice_data.num_of_voice_combos--; + memset(&rtac_voice_data.voice[ + rtac_voice_data.num_of_voice_combos], 0, + sizeof(rtac_voice_data.voice + [rtac_voice_data.num_of_voice_combos])); + voice_session_id[rtac_voice_data.num_of_voice_combos] + = 0; + break; + } + } + mutex_unlock(&rtac_voice_mutex); +} + +static u32 get_voice_session_id_cvs(u32 cvs_handle) +{ + u32 i; + + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) + return voice_session_id[i]; + } + + pr_err("%s: No voice index for CVS handle %d found returning 0\n", + __func__, cvs_handle); + return 0; +} + +static u32 get_voice_session_id_cvp(u32 cvp_handle) +{ + u32 i; + + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvp_handle == cvp_handle) + return voice_session_id[i]; + } + + pr_err("%s: No voice index for CVP handle %d found returning 0\n", + __func__, cvp_handle); + return 0; +} + +static int get_voice_index(u32 mode, u32 handle) +{ + if (mode == RTAC_CVP) + return voice_get_idx_for_session( + get_voice_session_id_cvp(handle)); + if (mode == RTAC_CVS) + return voice_get_idx_for_session( + get_voice_session_id_cvs(handle)); + + pr_err("%s: Invalid mode %d, returning 0\n", + __func__, mode); + return 0; +} + + +/* ADM APR */ +void rtac_set_adm_handle(void *handle) +{ + pr_debug("%s: handle = %pK\n", __func__, handle); + + mutex_lock(&rtac_adm_apr_mutex); + rtac_adm_apr_data.apr_handle = handle; + mutex_unlock(&rtac_adm_apr_mutex); +} + +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size) +{ + pr_debug("%s:cmd_state = %d\n", __func__, + atomic_read(&rtac_adm_apr_data.cmd_state)); + if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1) + return false; + + pr_debug("%s\n", __func__); + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_adm_apr_data.cmd_state, 0); + wake_up(&rtac_adm_apr_data.cmd_wait); + return true; +} + +int send_adm_apr(void *buf, u32 opcode) +{ + s32 result; + u32 user_buf_size = 0; + u32 bytes_returned = 0; + u32 copp_id; + u32 payload_size; + u32 data_size = 0; + int copp_idx; + int port_idx; + struct apr_hdr adm_params; + + pr_debug("%s\n", __func__); + + if (rtac_cal[ADM_RTAC_CAL].map_data.ion_handle == NULL) { + result = rtac_allocate_cal_buffer(ADM_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed!", + __func__); + goto done; + } + } + + if (rtac_cal[ADM_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(ADM_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed!", + __func__); + goto done; + } + } + + if (copy_from_user(&user_buf_size, (void *)buf, + sizeof(user_buf_size))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + if (user_buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_buf_size); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy port id from user buffer\n", + __func__); + goto done; + } + + if (adm_get_indexes_from_copp_id(copp_id, &copp_idx, &port_idx) != 0) { + pr_err("%s: Copp Id-%d is not active\n", __func__, copp_id); + goto done; + } + + mutex_lock(&rtac_adm_apr_mutex); + if (rtac_adm_apr_data.apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + if (opcode == ADM_CMD_SET_PP_PARAMS_V5) { + /* set payload size to in-band payload */ + /* set data size to actual out of band payload size */ + data_size = payload_size - 4 * sizeof(u32); + if (data_size > rtac_cal[ADM_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, data_size); + result = -EINVAL; + goto err; + } + payload_size = 4 * sizeof(u32); + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr, + buf + 7 * sizeof(u32), data_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + /* set payload size in packet */ + rtac_adm_buffer[8] = data_size; + } else { + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_adm_buffer + + sizeof(adm_params)/sizeof(u32), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + } + + /* Pack header */ + adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + adm_params.src_svc = APR_SVC_ADM; + adm_params.src_domain = APR_DOMAIN_APPS; + adm_params.src_port = copp_id; + adm_params.dest_svc = APR_SVC_ADM; + adm_params.dest_domain = APR_DOMAIN_ADSP; + adm_params.dest_port = copp_id; + adm_params.token = port_idx << 16 | copp_idx; + adm_params.opcode = opcode; + + /* fill for out-of-band */ + rtac_adm_buffer[5] = + lower_32_bits(rtac_cal[ADM_RTAC_CAL].cal_data.paddr); + rtac_adm_buffer[6] = + msm_audio_populate_upper_32_bits( + rtac_cal[ADM_RTAC_CAL].cal_data.paddr); + rtac_adm_buffer[7] = rtac_cal[ADM_RTAC_CAL].map_data.map_handle; + + memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params)); + atomic_set(&rtac_adm_apr_data.cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[ADM_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_adm_apr_data.apr_handle, + (uint32_t *)rtac_adm_buffer); + if (result < 0) { + pr_err("%s: Set params failed copp = %d\n", __func__, copp_id); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_adm_apr_data.cmd_wait, + (atomic_read(&rtac_adm_apr_data.cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set params timed out copp = %d\n", __func__, + copp_id); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == ADM_CMD_GET_PP_PARAMS_V5) { + bytes_returned = ((u32 *)rtac_cal[ADM_RTAC_CAL].cal_data. + kvaddr)[2] + 3 * sizeof(u32); + + if (bytes_returned > user_buf_size) { + pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", + __func__, user_buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user(buf, (void *) + rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user,size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + } else { + bytes_returned = data_size; + } + mutex_unlock(&rtac_adm_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_adm_apr_mutex); + return result; +} + + +/* ASM APR */ +void rtac_set_asm_handle(u32 session_id, void *handle) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_asm_apr_mutex); + rtac_asm_apr_data[session_id].apr_handle = handle; + mutex_unlock(&rtac_asm_apr_mutex); +} + +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size) +{ + if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1) + return false; + + pr_debug("%s\n", __func__); + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0); + wake_up(&rtac_asm_apr_data[session_id].cmd_wait); + return true; +} + +int send_rtac_asm_apr(void *buf, u32 opcode) +{ + s32 result; + u32 user_buf_size = 0; + u32 bytes_returned = 0; + u32 session_id = 0; + u32 payload_size; + u32 data_size = 0; + struct apr_hdr asm_params; + + pr_debug("%s\n", __func__); + + if (rtac_cal[ASM_RTAC_CAL].map_data.ion_handle == NULL) { + result = rtac_allocate_cal_buffer(ASM_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed!", + __func__); + goto done; + } + } + + if (rtac_cal[ASM_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(ASM_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed!", + __func__); + goto done; + } + } + + if (copy_from_user(&user_buf_size, (void *)buf, + sizeof(user_buf_size))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + if (user_buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_buf_size); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy session id from user buffer\n", + __func__); + goto done; + } + if (session_id >= (ASM_ACTIVE_STREAMS_ALLOWED + 1)) { + pr_err("%s: Invalid Session = %d\n", __func__, session_id); + goto done; + } + + mutex_lock(&rtac_asm_apr_mutex); + if (rtac_asm_apr_data[session_id].apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + if (opcode == ASM_STREAM_CMD_SET_PP_PARAMS_V2) { + /* set payload size to in-band payload */ + /* set data size to actual out of band payload size */ + data_size = payload_size - 4 * sizeof(u32); + if (data_size > rtac_cal[ASM_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, data_size); + result = -EINVAL; + goto err; + } + payload_size = 4 * sizeof(u32); + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr, + buf + 7 * sizeof(u32), data_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + /* set payload size in packet */ + rtac_asm_buffer[8] = data_size; + + } else { + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_asm_buffer + + sizeof(asm_params)/sizeof(u32), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + } + + /* Pack header */ + asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + asm_params.src_svc = q6asm_get_apr_service_id(session_id); + asm_params.src_domain = APR_DOMAIN_APPS; + asm_params.src_port = (session_id << 8) | 0x0001; + asm_params.dest_svc = APR_SVC_ASM; + asm_params.dest_domain = APR_DOMAIN_ADSP; + asm_params.dest_port = (session_id << 8) | 0x0001; + asm_params.token = session_id; + asm_params.opcode = opcode; + + /* fill for out-of-band */ + rtac_asm_buffer[5] = + lower_32_bits(rtac_cal[ASM_RTAC_CAL].cal_data.paddr); + rtac_asm_buffer[6] = + msm_audio_populate_upper_32_bits( + rtac_cal[ASM_RTAC_CAL].cal_data.paddr); + rtac_asm_buffer[7] = rtac_cal[ASM_RTAC_CAL].map_data.map_handle; + + memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params)); + atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[ASM_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle, + (uint32_t *)rtac_asm_buffer); + if (result < 0) { + pr_err("%s: Set params failed session = %d\n", + __func__, session_id); + goto err; + } + + /* Wait for the callback */ + result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait, + (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0), + 5 * HZ); + if (!result) { + pr_err("%s: Set params timed out session = %d\n", + __func__, session_id); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V2) { + bytes_returned = ((u32 *)rtac_cal[ASM_RTAC_CAL].cal_data. + kvaddr)[2] + 3 * sizeof(u32); + + if (bytes_returned > user_buf_size) { + pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", + __func__, user_buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user(buf, (void *) + rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user,size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + } else { + bytes_returned = data_size; + } + mutex_unlock(&rtac_asm_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_asm_apr_mutex); + return result; +} + +/* AFE APR */ +void rtac_set_afe_handle(void *handle) +{ + mutex_lock(&rtac_afe_apr_mutex); + rtac_afe_apr_data.apr_handle = handle; + mutex_unlock(&rtac_afe_apr_mutex); +} + +bool rtac_make_afe_callback(uint32_t *payload, uint32_t payload_size) +{ + pr_debug("%s:cmd_state = %d\n", __func__, + atomic_read(&rtac_afe_apr_data.cmd_state)); + if (atomic_read(&rtac_afe_apr_data.cmd_state) != 1) + return false; + + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_afe_apr_data.cmd_state, 0); + wake_up(&rtac_afe_apr_data.cmd_wait); + return true; +} + +static int fill_afe_apr_hdr(struct apr_hdr *apr_hdr, uint32_t port, + uint32_t opcode, uint32_t apr_msg_size) +{ + if (apr_hdr == NULL) { + pr_err("%s: invalid APR pointer", __func__); + return -EINVAL; + } + + apr_hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + apr_hdr->pkt_size = apr_msg_size; + apr_hdr->src_svc = APR_SVC_AFE; + apr_hdr->src_domain = APR_DOMAIN_APPS; + apr_hdr->src_port = 0; + apr_hdr->dest_svc = APR_SVC_AFE; + apr_hdr->dest_domain = APR_DOMAIN_ADSP; + apr_hdr->dest_port = 0; + apr_hdr->token = port; + apr_hdr->opcode = opcode; + + return 0; + +} +static int send_rtac_afe_apr(void *buf, uint32_t opcode) +{ + int32_t result; + uint32_t bytes_returned = 0; + uint32_t port_index = 0; + uint32_t apr_msg_size = 0; + struct rtac_afe_user_data user_afe_buf; + + pr_debug("%s\n", __func__); + + if (rtac_cal[AFE_RTAC_CAL].map_data.ion_handle == NULL) { + result = rtac_allocate_cal_buffer(AFE_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed! ret = %d\n", + __func__, result); + goto done; + } + } + + if (rtac_cal[AFE_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(AFE_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed! ret = %d\n", + __func__, result); + goto done; + } + } + + if (copy_from_user(&user_afe_buf, (void *)buf, + sizeof(struct rtac_afe_user_data))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + + if (user_afe_buf.buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_afe_buf.buf_size); + goto done; + } + + port_index = q6audio_get_port_index(user_afe_buf.port_id); + if (port_index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port = 0x%x\n", + __func__, user_afe_buf.port_id); + goto done; + } + + mutex_lock(&rtac_afe_apr_mutex); + if (rtac_afe_apr_data.apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + if (opcode == AFE_PORT_CMD_SET_PARAM_V2) { + struct afe_port_cmd_set_param_v2 *afe_set_apr_msg; + + /* set data size to actual out of band payload size */ + if (user_afe_buf.rtac_afe_set.cmd.payload_size > + rtac_cal[AFE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, + user_afe_buf.rtac_afe_set.cmd.payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr, + buf+offsetof(struct rtac_afe_user_data, + rtac_afe_set.data), + user_afe_buf.rtac_afe_set.cmd.payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + + /* Copy AFE APR Message */ + afe_set_apr_msg = (struct afe_port_cmd_set_param_v2 *) + ((u8 *)rtac_afe_buffer + + sizeof(struct apr_hdr)); + if (copy_from_user((void *) + afe_set_apr_msg, + buf + offsetof(struct rtac_afe_user_data, + rtac_afe_set.cmd), + sizeof(struct afe_port_cmd_set_param_v2))) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + + afe_set_apr_msg->payload_address_lsw = + lower_32_bits(rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + afe_set_apr_msg->payload_address_msw = + msm_audio_populate_upper_32_bits( + rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + afe_set_apr_msg->mem_map_handle = + rtac_cal[AFE_RTAC_CAL].map_data.map_handle; + + apr_msg_size = sizeof(struct apr_hdr) + + sizeof(struct afe_port_cmd_set_param_v2); + + } else { + struct afe_port_cmd_get_param_v2 *afe_get_apr_msg; + + if (user_afe_buf.cmd_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, user_afe_buf.cmd_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + afe_get_apr_msg = (struct afe_port_cmd_get_param_v2 *) + ((u8 *) rtac_afe_buffer + + sizeof(struct apr_hdr)); + if (copy_from_user((void *)afe_get_apr_msg, + buf+offsetof(struct rtac_afe_user_data, + rtac_afe_get.cmd), + sizeof(struct afe_port_cmd_get_param_v2))) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + + afe_get_apr_msg->payload_address_lsw = + lower_32_bits(rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + afe_get_apr_msg->payload_address_msw = + msm_audio_populate_upper_32_bits( + rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + afe_get_apr_msg->mem_map_handle = + rtac_cal[AFE_RTAC_CAL].map_data.map_handle; + afe_get_apr_msg->payload_size -= sizeof(struct apr_hdr); + apr_msg_size = sizeof(struct apr_hdr) + + sizeof(struct afe_port_cmd_get_param_v2); + } + + fill_afe_apr_hdr((struct apr_hdr *) rtac_afe_buffer, + port_index, opcode, apr_msg_size); + + atomic_set(&rtac_afe_apr_data.cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_afe_apr_data.apr_handle, + (uint32_t *)rtac_afe_buffer); + if (result < 0) { + pr_err("%s: Set params failed port = 0x%x, ret = %d\n", + __func__, user_afe_buf.port_id, result); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_afe_apr_data.cmd_wait, + (atomic_read(&rtac_afe_apr_data.cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set params timed out port = 0x%x, ret = %d\n", + __func__, user_afe_buf.port_id, result); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == AFE_PORT_CMD_GET_PARAM_V2) { + struct afe_port_param_data_v2 *get_resp; + + get_resp = (struct afe_port_param_data_v2 *) + rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr; + + bytes_returned = get_resp->param_size + + sizeof(struct afe_port_param_data_v2); + + if (bytes_returned > user_afe_buf.buf_size) { + pr_err("%s: user size = 0x%x, returned size = 0x%x\n", + __func__, user_afe_buf.buf_size, + bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user(buf, (void *) + rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user,size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + } else { + bytes_returned = user_afe_buf.rtac_afe_set.cmd.payload_size; + } + mutex_unlock(&rtac_afe_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_afe_apr_mutex); + return result; +} + +/* Voice APR */ +void rtac_set_voice_handle(u32 mode, void *handle) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_voice_apr_mutex); + rtac_voice_apr_data[mode].apr_handle = handle; + mutex_unlock(&rtac_voice_apr_mutex); +} + +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size) +{ + if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) || + (mode >= RTAC_VOICE_MODES)) + return false; + + pr_debug("%s\n", __func__); + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0); + wake_up(&rtac_voice_apr_data[mode].cmd_wait); + return true; +} + +int send_voice_apr(u32 mode, void *buf, u32 opcode) +{ + s32 result; + u32 user_buf_size = 0; + u32 bytes_returned = 0; + u32 payload_size; + u32 dest_port; + u32 data_size = 0; + struct apr_hdr voice_params; + + pr_debug("%s\n", __func__); + + if (rtac_cal[VOICE_RTAC_CAL].map_data.ion_handle == NULL) { + result = rtac_allocate_cal_buffer(VOICE_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed!", + __func__); + goto done; + } + } + + if (rtac_cal[VOICE_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(VOICE_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed!", + __func__); + goto done; + } + } + + if (copy_from_user(&user_buf_size, (void *)buf, + sizeof(user_buf_size))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + if (user_buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_buf_size); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (copy_from_user(&dest_port, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy port id from user buffer\n", + __func__); + goto done; + } + + if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) { + pr_err("%s: Invalid Mode for APR, mode = %d\n", + __func__, mode); + goto done; + } + + mutex_lock(&rtac_voice_apr_mutex); + if (rtac_voice_apr_data[mode].apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + if (opcode == VOICE_CMD_SET_PARAM) { + /* set payload size to in-band payload */ + /* set data size to actual out of band payload size */ + data_size = payload_size - 4 * sizeof(u32); + if (data_size > rtac_cal[VOICE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, data_size); + result = -EINVAL; + goto err; + } + payload_size = 4 * sizeof(u32); + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr, + buf + 7 * sizeof(u32), data_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + /* set payload size in packet */ + rtac_voice_buffer[8] = data_size; + } else { + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_voice_buffer + + sizeof(voice_params)/sizeof(u32), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EINVAL; + goto err; + } + } + + /* Pack header */ + voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + voice_params.src_svc = 0; + voice_params.src_domain = APR_DOMAIN_APPS; + voice_params.src_port = get_voice_index(mode, dest_port); + voice_params.dest_svc = 0; + voice_params.dest_domain = APR_DOMAIN_MODEM; + voice_params.dest_port = (u16)dest_port; + voice_params.token = 0; + voice_params.opcode = opcode; + + /* fill for out-of-band */ + rtac_voice_buffer[5] = rtac_cal[VOICE_RTAC_CAL].map_data.map_handle; + rtac_voice_buffer[6] = + lower_32_bits(rtac_cal[VOICE_RTAC_CAL].cal_data.paddr); + rtac_voice_buffer[7] = + msm_audio_populate_upper_32_bits( + rtac_cal[VOICE_RTAC_CAL].cal_data.paddr); + + memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params)); + atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[VOICE_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle, + (uint32_t *)rtac_voice_buffer); + if (result < 0) { + pr_err("%s: apr_send_pkt failed opcode = %x\n", + __func__, opcode); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait, + (atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: apr_send_pkt timed out opcode = %x\n", + __func__, opcode); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == VOICE_CMD_GET_PARAM) { + bytes_returned = ((u32 *)rtac_cal[VOICE_RTAC_CAL].cal_data. + kvaddr)[2] + 3 * sizeof(u32); + + if (bytes_returned > user_buf_size) { + pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", + __func__, user_buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user(buf, (void *) + rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user, size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + } else { + bytes_returned = data_size; + } + mutex_unlock(&rtac_voice_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_voice_apr_mutex); + return result; +} + +void get_rtac_adm_data(struct rtac_adm *adm_data) +{ + mutex_lock(&rtac_adm_mutex); + memcpy(adm_data, &rtac_adm_data, sizeof(struct rtac_adm)); + mutex_unlock(&rtac_adm_mutex); +} + + +static long rtac_ioctl_shared(struct file *f, + unsigned int cmd, void *arg) +{ + int result = 0; + + if (!arg) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EFAULT; + goto done; + } + + switch (cmd) { + case AUDIO_GET_RTAC_ADM_INFO: { + mutex_lock(&rtac_adm_mutex); + if (copy_to_user((void *)arg, &rtac_adm_data, + sizeof(rtac_adm_data))) { + pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_ADM_INFO\n", + __func__); + mutex_unlock(&rtac_adm_mutex); + return -EFAULT; + } + result = sizeof(rtac_adm_data); + mutex_unlock(&rtac_adm_mutex); + break; + } + case AUDIO_GET_RTAC_VOICE_INFO: { + mutex_lock(&rtac_voice_mutex); + if (copy_to_user((void *)arg, &rtac_voice_data, + sizeof(rtac_voice_data))) { + pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_VOICE_INFO\n", + __func__); + mutex_unlock(&rtac_voice_mutex); + return -EFAULT; + } + result = sizeof(rtac_voice_data); + mutex_unlock(&rtac_voice_mutex); + break; + } + + case AUDIO_GET_RTAC_ADM_CAL: + result = send_adm_apr((void *)arg, ADM_CMD_GET_PP_PARAMS_V5); + break; + case AUDIO_SET_RTAC_ADM_CAL: + result = send_adm_apr((void *)arg, ADM_CMD_SET_PP_PARAMS_V5); + break; + case AUDIO_GET_RTAC_ASM_CAL: + result = send_rtac_asm_apr((void *)arg, + ASM_STREAM_CMD_GET_PP_PARAMS_V2); + break; + case AUDIO_SET_RTAC_ASM_CAL: + result = send_rtac_asm_apr((void *)arg, + ASM_STREAM_CMD_SET_PP_PARAMS_V2); + break; + case AUDIO_GET_RTAC_CVS_CAL: + result = send_voice_apr(RTAC_CVS, (void *)arg, + VOICE_CMD_GET_PARAM); + break; + case AUDIO_SET_RTAC_CVS_CAL: + result = send_voice_apr(RTAC_CVS, (void *)arg, + VOICE_CMD_SET_PARAM); + break; + case AUDIO_GET_RTAC_CVP_CAL: + result = send_voice_apr(RTAC_CVP, (void *)arg, + VOICE_CMD_GET_PARAM); + break; + case AUDIO_SET_RTAC_CVP_CAL: + result = send_voice_apr(RTAC_CVP, (void *)arg, + VOICE_CMD_SET_PARAM); + break; + case AUDIO_GET_RTAC_AFE_CAL: + result = send_rtac_afe_apr((void *)arg, + AFE_PORT_CMD_GET_PARAM_V2); + break; + case AUDIO_SET_RTAC_AFE_CAL: + result = send_rtac_afe_apr((void *)arg, + AFE_PORT_CMD_SET_PARAM_V2); + break; + default: + pr_err("%s: Invalid IOCTL, command = %d!\n", + __func__, cmd); + result = -EINVAL; + } +done: + return result; +} + +static long rtac_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + int result = 0; + + if (!arg) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EFAULT; + } else { + result = rtac_ioctl_shared(f, cmd, (void __user *)arg); + } + + return result; +} + +#ifdef CONFIG_COMPAT +#define AUDIO_GET_RTAC_ADM_INFO_32 _IOR(CAL_IOCTL_MAGIC, 207, compat_uptr_t) +#define AUDIO_GET_RTAC_VOICE_INFO_32 _IOR(CAL_IOCTL_MAGIC, 208, compat_uptr_t) +#define AUDIO_GET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 209, compat_uptr_t) +#define AUDIO_SET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 210, compat_uptr_t) +#define AUDIO_GET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 211, compat_uptr_t) +#define AUDIO_SET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 212, compat_uptr_t) +#define AUDIO_GET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 213, compat_uptr_t) +#define AUDIO_SET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 214, compat_uptr_t) +#define AUDIO_GET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 215, compat_uptr_t) +#define AUDIO_SET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 216, compat_uptr_t) +#define AUDIO_GET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 217, compat_uptr_t) +#define AUDIO_SET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 218, compat_uptr_t) + +static long rtac_compat_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + int result = 0; + + if (!arg) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EINVAL; + goto done; + } + + switch (cmd) { + case AUDIO_GET_RTAC_ADM_INFO_32: + cmd = AUDIO_GET_RTAC_ADM_INFO; + goto process; + case AUDIO_GET_RTAC_VOICE_INFO_32: + cmd = AUDIO_GET_RTAC_VOICE_INFO; + goto process; + case AUDIO_GET_RTAC_AFE_CAL_32: + cmd = AUDIO_GET_RTAC_AFE_CAL; + goto process; + case AUDIO_SET_RTAC_AFE_CAL_32: + cmd = AUDIO_SET_RTAC_AFE_CAL; + goto process; + case AUDIO_GET_RTAC_ADM_CAL_32: + cmd = AUDIO_GET_RTAC_ADM_CAL; + goto process; + case AUDIO_SET_RTAC_ADM_CAL_32: + cmd = AUDIO_SET_RTAC_ADM_CAL; + goto process; + case AUDIO_GET_RTAC_ASM_CAL_32: + cmd = AUDIO_GET_RTAC_ASM_CAL; + goto process; + case AUDIO_SET_RTAC_ASM_CAL_32: + cmd = AUDIO_SET_RTAC_ASM_CAL; + goto process; + case AUDIO_GET_RTAC_CVS_CAL_32: + cmd = AUDIO_GET_RTAC_CVS_CAL; + goto process; + case AUDIO_SET_RTAC_CVS_CAL_32: + cmd = AUDIO_SET_RTAC_CVS_CAL; + goto process; + case AUDIO_GET_RTAC_CVP_CAL_32: + cmd = AUDIO_GET_RTAC_CVP_CAL; + goto process; + case AUDIO_SET_RTAC_CVP_CAL_32: + cmd = AUDIO_SET_RTAC_CVP_CAL; +process: + result = rtac_ioctl_shared(f, cmd, compat_ptr(arg)); + break; + default: + result = -EINVAL; + pr_err("%s: Invalid IOCTL, command = %d!\n", + __func__, cmd); + break; + } +done: + return result; +} +#else +#define rtac_compat_ioctl NULL +#endif + +static const struct file_operations rtac_fops = { + .owner = THIS_MODULE, + .open = rtac_open, + .release = rtac_release, + .unlocked_ioctl = rtac_ioctl, + .compat_ioctl = rtac_compat_ioctl, +}; + +struct miscdevice rtac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_rtac", + .fops = &rtac_fops, +}; + +static int __init rtac_init(void) +{ + int i = 0; + + /* Driver */ + atomic_set(&rtac_common.usage_count, 0); + atomic_set(&rtac_common.apr_err_code, 0); + + /* ADM */ + memset(&rtac_adm_data, 0, sizeof(rtac_adm_data)); + rtac_adm_apr_data.apr_handle = NULL; + atomic_set(&rtac_adm_apr_data.cmd_state, 0); + init_waitqueue_head(&rtac_adm_apr_data.cmd_wait); + mutex_init(&rtac_adm_mutex); + mutex_init(&rtac_adm_apr_mutex); + + rtac_adm_buffer = kzalloc( + rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_adm_buffer == NULL) + goto nomem; + + /* ASM */ + for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED+1; i++) { + rtac_asm_apr_data[i].apr_handle = NULL; + atomic_set(&rtac_asm_apr_data[i].cmd_state, 0); + init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait); + } + mutex_init(&rtac_asm_apr_mutex); + + rtac_asm_buffer = kzalloc( + rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_asm_buffer == NULL) { + kzfree(rtac_adm_buffer); + goto nomem; + } + + /* AFE */ + rtac_afe_apr_data.apr_handle = NULL; + atomic_set(&rtac_afe_apr_data.cmd_state, 0); + init_waitqueue_head(&rtac_afe_apr_data.cmd_wait); + mutex_init(&rtac_afe_apr_mutex); + + rtac_afe_buffer = kzalloc( + rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_afe_buffer == NULL) { + kzfree(rtac_adm_buffer); + kzfree(rtac_asm_buffer); + goto nomem; + } + + /* Voice */ + memset(&rtac_voice_data, 0, sizeof(rtac_voice_data)); + for (i = 0; i < RTAC_VOICE_MODES; i++) { + rtac_voice_apr_data[i].apr_handle = NULL; + atomic_set(&rtac_voice_apr_data[i].cmd_state, 0); + init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait); + } + mutex_init(&rtac_voice_mutex); + mutex_init(&rtac_voice_apr_mutex); + + rtac_voice_buffer = kzalloc( + rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_voice_buffer == NULL) { + kzfree(rtac_adm_buffer); + kzfree(rtac_asm_buffer); + kzfree(rtac_afe_buffer); + goto nomem; + } + + return misc_register(&rtac_misc); +nomem: + return -ENOMEM; +} + +module_init(rtac_init); + +MODULE_DESCRIPTION("SoC QDSP6v2 Real-Time Audio Calibration driver"); +MODULE_LICENSE("GPL v2"); -- GitLab